# The Model-View-Controller Design Pattern in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)

**Author:** Ndianabasi Udonkang  
**Published:** 2021-10-01

We will learn about the `Model-View-Controller` design pattern as implemented by the AdonisJS Framework and create model, controller and migration files

---

## Tags

- [Nodejs](/llms/technical-blog/tag/nodejs.md)
- [Vuejs](/llms/technical-blog/tag/vuejs.md)
- [API Development](/llms/technical-blog/tag/api-development.md)
- [Adonisjs](/llms/technical-blog/tag/adonisjs.md)
- [Quasar Framework](/llms/technical-blog/tag/quasar-framework.md)

## Part of Series: [Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vue.js (Quasar Framework)](/llms/technical-blog/series/clnwu9bc991hiu3wo2vwmw45/full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vue-js-quasar-framework.md)

This article is part of the **[Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vue.js (Quasar Framework)](/llms/technical-blog/series/clnwu9bc991hiu3wo2vwmw45/full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vue-js-quasar-framework.md)** series.

### Articles in this Series:

- [Introduction to Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vuejs (Quasar Framework)](/llms/technical-blog/article/xe5mlxg43s9rpx1pxsvfdp3r/introduction-to-full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vuejs-quasar-framework.md)
- [Workspace Setup | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/fo8str8yjpkz5dygxg875bpw/workspace-setup-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Frontend Overview | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/nxgmng6fhf1cwjk30coflro2/frontend-overview-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [The Left Drawer | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/m6fagvy2il5h65llxxbbl9os/the-left-drawer-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Improve The Header | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/jjwbxcg450jmkkcrrmkmwvwj/improve-the-header-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [New Contact Form Design | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/ebguwpkntk3empakunrki4dq/new-contact-form-design-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Validating the Contact Form with Vuelidate | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/z0xr9m2ljler2cwnpsw2ycwa/validating-the-contact-form-with-vuelidate-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contacts Table | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/h2p1x1z406ib38zu2dci3jur/designing-the-contacts-table-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contacts Table (Part 2) | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/j0iscua9gg8ekuz8ifh6chpn/designing-the-contacts-table-part-2-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Refactoring the Main Layout | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/uktmlqsivwtr3cvnpld59k9q/refactoring-the-main-layout-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Improving User Experience with the Contacts Table | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/dzs812auf61ej7qu2cg726dh/improving-user-experience-with-the-contacts-table-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contact Details Page | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/kf9pz97n63fvgfrcy5viwzrg/designing-the-contact-details-page-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Creating the Contact Edit Page | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/fmr90prj84c9zn0633jc368g/creating-the-contact-edit-page-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [How Software Backends Work | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/aca8fcrghz70nn1wqg3uzbum/how-software-backends-work-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up The Backend | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fxb49fmz9ljbwrimbnnzudfv/setting-up-the-backend-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Why Choose the AdonisJs Framework? | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/lnhm5oudx34yo2869gmcms37/why-choose-the-adonis-js-framework-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up Our API Server with AdonisJs Framework | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/x1jgmet84aqj9qggu5mulkh0/setting-up-our-api-server-with-adonis-js-framework-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up Postman with the API Server | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/v4qehksislosmuy92qgjr4ct/setting-up-postman-with-the-api-server-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- **The Model-View-Controller Design Pattern in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)** (Current Article)
- [Create Column Definitions & Insert New Contacts | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/asc2emsho4u73r6jb2gxjddw/create-column-definitions-and-insert-new-contacts-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Data Validation & Sanitisation with AdonisJS | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/bcey0ac7m5sbs9hqy1h824sl/data-validation-and-sanitisation-with-adonis-js-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Creating a Middleware and Updating a Contact | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/oqxzmtgfy1r3oewpnedc7wf6/creating-a-middleware-and-updating-a-contact-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Fetching and Deleting a Contact with AdonisJS | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/p6bh0ponq43u3vi6o8uww84x/fetching-and-deleting-a-contact-with-adonis-js-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Using Model Factories and Seeders in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/cfotveuxbgjtdp12l6pzcb2y/using-model-factories-and-seeders-in-adonis-js-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Creating and Registering a Vuex Module | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/cw96ewnzujgsz2e2garhbuzb/creating-and-registering-a-vuex-module-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Connecting UI Components to the Vuex Store | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fdyqp17yehbnqrjvjvab4b5b/connecting-ui-components-to-the-vuex-store-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Connecting the Frontend to the API Server | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fk4262359gg3g7f0jn95fn9b/connecting-the-frontend-to-the-api-server-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Creating and Updating Contacts From the Frontend | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/t15w2s3ao9jo74l96stjo7z6/creating-and-updating-contacts-from-the-frontend-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Uploading Files and Creating Avatars for Contacts With AdonisJS and Axios](/llms/technical-blog/article/hg9ilaubqesm8angg0rhrjp9/uploading-files-and-creating-avatars-for-contacts-with-adonis-js-and-axios.md)
- [Deleting a Contact with AdonisJS and Vuejs](/llms/technical-blog/article/zdr4pxmgcax2dsfnwimhty0g/deleting-a-contact-with-adonis-js-and-vuejs.md)

---

## Article Content

Finally, we will start during real development of the API side of our of Google Contacts Clone app. In this lesson, we will learn about the `Model-View-Controller` design pattern as implemented by AdonisJS. We will then create the model, controller, and migration files for the `Contact` entity.

Let's start by creating a new branch for our repo:

```bash
# Make sure you are within your project
git checkout -b 11-create-contact-model-and-controller
```

### The Model-View-Controller Design Pattern in the AdonisJS Framework

AdonisJS follows the `Model-View-Controller (MVC)` design pattern. In this pattern:

1. There is a `model` which holds the state of our data objects fetched from the database;
2. The `view` which renders and displays to the client(frontend) a formatted (HTML) version of our data; and
3. The controller which holds the logic for accepting requests from the clients, fetching data from the database, and sending the rendered data (HTML) back to the client. The `controller` also includes the routing mechanism for the application.

In the context of our API server, we will not do much rendering of views because AdonisJS will return responses in `JSON` format **and not** `HTML` format. So the `view` aspect of the `MVC` pattern will be dormant. AdonisJS has a complete module for handing rendering of views via the Edge templating engine and it is always available for use any time.

Since, we are following the `MVC` design pattern, for every entity (such as a contact), we need to have a `model file`, `controller file`, and `view file`. Again, we are using `AdonisJS` in the API mode, so we won't be creating `view` files. Additionally, we need a `migration file` for creating the structure of the database table for each entity.

Let's take more about each of these files.

#### The Model File

In AdonisJS, models are regular JavaScript objects which hold the state/properties of entities within our application. A `model` is an instance of the the AdonisJS `BaseModel` class. For example: if a request is made for a specific contact on our database, after we fetch the record for the contact from the database, `Lucid` will map the record from the database into properties of the `Contact` model. This way, we are able to access those properties throughout the lifecycle of the API request. If the API request involved modifying the properties, we can easily assign new values to the properties and `save` the `Contact` model. The `save` operation will persist the new values of the properties in the database. We could also return that `Contact` model as a response to the client making the API request. AdonisJS will handle to conversion of the `model` to JSON before sending the data. This conversion process is known as `serialization`.

For AdonisJS to be able to map the record fetched from the database to the properties of the `Contact` model (for example), we need to specify those properties within the `Contact` class itself (we will create this `Contact` class very soon). The `Contact` class will extend the `BaseModel` class from the `Lucid` ORM package. Since it is extending the `BaseModel` class, we have all the `Lucid` operations at our fingertips for each model instance.

#### The Controller File

In AdonisJS, you can create dedicated `Controller` files (each one contains a single class). The `controller` class contains `async` methods which are mapped as route handlers for different routes. Instead of carrying out logic within route definitions, we call the methods within controller classes so that there is separation of concern and a less-congested `routes` file. Within these controller methods, we can create new records in our database, fetch data from the database, edit records, save files to the filesystem or upload them to external file storage services, delete records, etc.

#### The Migration File

Migration files are used for creating tables and column definitions in the database. As you develop the application, you might need to add or drop columns within various tables. Migration files can be used to achieve that. Migration files can also be used to reverse changes made to tables within the database. This is possible because AdonisJS logs the migration history and know which migration file was run during any batch. With this you can easily rollback changes.

### Running our First Database Migration

Before we continue, let's migrate our database for the first time. When we setup the `Auth` package earlier, the package created a migration file for us. Check the folder `api/database/migrations` and you will see a file ending with  `users.ts`. Below is the content of the file. Read more about AdonisJS [database migration here](https://docs.adonisjs.com/guides/database/migrations).

```ts
import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class UsersSchema extends BaseSchema {
  protected tableName = 'users'

  public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id').primary()
      table.string('email', 255).notNullable()
      table.string('password', 180).notNullable()
      table.string('remember_me_token').nullable()

      /**
       * Uses timestampz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true }).notNullable()
      table.timestamp('updated_at', { useTz: true }).notNullable()
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}

```

The migration file defines `users` as the `tableName`. The `up` method is run when we are migration while the `down` method is run if we want to rollback changes made by the migration file. In this case, the rollback with drop the entire table since it was created during this migration.

Within the `up` method, we create the table with the function call `this.schema.createTable()`. The function takes the name of the table to be created as the first argument and passes in an instance of the `table` into the callback function. We then use that `table` instance to create the columns. Six columns will be created: `id`, `email`, `password`, `remember_me_token`, `created_at`, and `updated_at`. The `table` instance has various methods for creating different types of columns.

To run the migration, do:

```bash
# Ensure that your current working directory is the `api` folder, then run
node ace migration:run
```

The migration should be successful.

Now open MySQL Workbench. Within the Navigator panel on the left side, switch to the `Schemas` tab. Expand the `google_contacts_clone_app` schema and expand the `Tables` node. You will see the `contacts` table within. You will also see the `adonis_schema` table. When you hover over the name of the tables, there will be a toolbar to the right of the table names. Click the last icon to browse the tables.

![image.png](https://cdn.ndianabasi.com/site/the_model_view_controller_design_pattern_in_adonisjs_full_stack_google_contacts_clone_with_adonisjs_node_js_and_quasar_framework_vue_js_v1633112724607_v_XL_Ri_I_Khm_f8a6c6e857.png)

The `users` table:

![image.png](https://cdn.ndianabasi.com/site/the_model_view_controller_design_pattern_in_adonisjs_full_stack_google_contacts_clone_with_adonisjs_node_js_and_quasar_framework_vue_js_v1633112802063_4_Rsk1_RH_Hd0_bfc1edec5f.png)

The `adonis_schema` table:

![image.png](https://cdn.ndianabasi.com/site/the_model_view_controller_design_pattern_in_adonisjs_full_stack_google_contacts_clone_with_adonisjs_node_js_and_quasar_framework_vue_js_v1633112844653_v_Gb_Qkrqf5_7d8294565c.png)

At the stage, the `adonis_schema` table has a single row which contains the name of the single file which was migrated and used to created the `users` table. It also assigned a batch number of `1` to the file.

Multiple files can be migrated in one batch and will be assigned the same batch number so that when that batch is rollback, all the changes made during that batch will be reversed.

### Creating our Model, Controller, and Migration Files for our Contacts

Having learnt about the MVC pattern and carried out a migration, let's create model, controller and migration files for our Contacts. `AdonisJS` has a single command which can scaffold all three files for us. Do:

```bash
# Make sure your current working directory is the `api` directory
# Run:
node ace make:model Contact -mc
# Result:
CREATE: database\migrations\1633078123032_contacts.ts
CREATE: app\Controllers\Http\ContactsController.ts
CREATE: app\Models\Contact.ts
```

Three new files are created as seen the result above.

More on the command: `node ace make:model Contact -mc`

1. `node`: This is used to call the `Node.js` runtime for running the script
2. `ace`: `ace` (path: `api/ace`) is `AdonisJS` single entry file for running commands. Press `CTRL+P` and search for `ace`, you will see the file.
3. `make:model`: `make:model` is a command registered within AdonisJS by the `Lucid` package. It is used for created new models.
4. `Contact`: `Contact` is an example of the single argument to the `make:model` command. Here we specify the name of the model we want to create.
5. `-mc`: `-mc` specifies two flags for the `make:model` command. `m` stands for `migration`, while `c` stands for `controller`. Here, we are telling the `make:model` command to create `migration` and `controller` files for the `Contact` model as well.

This will be all for this lesson. In this next lesson, we will add column definitions to `Contact` model and migration files and run our migration again to create the `contacts` table.

Save all your files, commit and merge with the master branch.

```bash
git add .
git commit -m "feat(api): create model, controller, and migration files for the contact model"
git push --set-upstream origin 11-create-contact-model-and-controller
git checkout master
git merge master 11-create-contact-model-and-controller
git push

```


*This document was generated from the live article page on https://ndianabasi.com/technical-blog/article/h5wqql65iejop5xrplujw458/the-model-view-controller-design-pattern-in-adonis-js-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js • 2026-06-07*
