# Using Model Factories and Seeders in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)

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

In this lesson, we make use a model factory and seeder in AdonisJS to generate and persist sample contacts. We also list all contacts with pagination.

---

## Tags

- [JavaScript](/llms/technical-blog/tag/javascript.md)
- [Nodejs](/llms/technical-blog/tag/nodejs.md)
- [Vuejs](/llms/technical-blog/tag/vuejs.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)](/llms/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.md)
- [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)** (Current Article)
- [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

In this lesson, we will learn how to create sample (fake) data as we continue to develop and test our Google Contacts Clone app. We will make use of Model Factories and Seeders in the AdonisJS Framework for this.

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

```bash
# Make sure you are within your project
git checkout -b 16-seeding-and-listing-contacts
```

## What are Model Factories and Seeders

A model factory is simply a function used to generate sample/fake model instances from a `faker` object before they are persisted to the database by a seeder. The `faker` object is a [Faker.js](https://github.com/marak/Faker.js/) object passed into the factory function by AdonisJS. You can make use of it or import your own `Faker` library into the Factory file. Read more about [Model Factories](https://docs.adonisjs.com/guides/models/factories).

A seeder is a class which must have a `run()` method as an entry point into the class. A seeder can be used to persist fake data generated from a factory or persist a real data from a data source such as JSON or JavaScript or API or CSV into your database. A seeder works like a controller. So you can import models into it and use the models to persist data from data source. If you want to use a factory, simple import the factory into the seeder file and then call the `create` or `createMany` static method to persist the data generated by the factory into the database.

We will learn how to use a model factory in this lesson

## Creating a Model Factory

In AdonisJS, all model factories must be defined within the `api/database/factories/index.ts` file and then exported from that file so that seeders can consume them. If you need separation of concern, you can create standalone factory files but you must still import them into the `api/database/factories/index.ts`. Let's look at how to achieve this for our API server.

1. Create a new file: `api/database/factories/ContactFactory.ts`. If you are using VS Code do:

   ```bash
   ```

# In the route of your project, do:

code api/database/factories/ContactFactory.ts

````

2. Open the newly-created `api/database/factories/ContactFactory.ts` file. Copy and paste the content of [this snapshot ](https://github.com/ndianabasi/google-contacts/blob/16-seeding-and-listing-contacts/api/database/factories/ContactFactory.ts) of the `ContactFactory.ts` file into the created file. Save the file.

3. Open `api/database/factories/index.ts`. Paste the lines below into the file. You can remove the comment on the first line. This imports the `ContactFactory` model factory into our Factory index file and export the `ContactFactory` as well so that seeders can easily consume the `ContactFactory`. Save the file. 

```ts
import ContactFactory from './ContactFactory'

export { ContactFactory }
````

Let's discuss what's going within the `api/database/factories/ContactFactory.ts` file. Please refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/16-seeding-and-listing-contacts/api/database/factories/ContactFactory.ts) for the line numbers. The content of the file is shown below for easy reference.

```ts
import Contact from 'App/Models/Contact'
import Factory from '@ioc:Adonis/Lucid/Factory'
import { DateTime } from 'luxon'

const ContactFactory = Factory.define(Contact, ({ faker }) => {
  const firstName = faker.name.firstName(faker.random.arrayElement([0, 1]))
  const surname = faker.name.lastName()
  const omitAddresses = faker.datatype.boolean()

  return {
    firstName,
    surname,
    company: faker.company.companyName(),
    jobTitle: (() => {
      const omit = faker.datatype.boolean()
      return omit ? null : faker.name.jobTitle()
    })(),
    email1: faker.internet.email(firstName, surname),
    email2: (() => {
      const omit = faker.datatype.boolean()
      return omit ? null : `${firstName}.${surname}@${faker.internet.domainName()}`
    })(),
    phoneNumber1: faker.phone.phoneNumber(),
    phoneNumber2: (() => {
      const omit = faker.datatype.boolean()
      return omit ? null : faker.phone.phoneNumber()
    })(),
    country: faker.address.country(),
    state: omitAddresses ? null : faker.address.state(),
    streetAddressLine1: omitAddresses ? null : faker.address.streetAddress(),
    streetAddressLine2: omitAddresses ? null : faker.address.streetAddress(),
    postCode: omitAddresses ? null : faker.address.zipCode(),
    birthday: (() => {
      const omit = faker.datatype.boolean()
      return omit ? null : DateTime.fromJSDate(faker.date.past())
    })(),
    website: (() => {
      const omit = faker.datatype.boolean()
      return omit ? null : `https://${faker.internet.domainName()}`
    })(),
    notes: (() => {
      const omit = faker.datatype.boolean()
      return omit ? null : faker.lorem.paragraphs(faker.random.arrayElement([1, 2]))
    })(),
  }
}).build()

export default ContactFactory
```

1. At Lines 1-3, we make the necessary imports.

2. At Line 5, we call the `Factory.define` function. The `define` function takes two arguments. The first argument is the primary model we want to return from the factory. In this case, it is the `Contact` model. The second argument is a callback function. The callback function is passed a context object and the context object contains the `faker` instance. We will use the `faker` object to create random data. Note that the properties of the object return at Line 10 must match the properties of the `api/app/Models/Contact.ts` file, else type errors will be thrown.

3. At Line 6-7, we create and initialise the `firstName` and `surname` constants. These are generated outside the object we are returning at Line 10 because we also need the `firstName` and `surname` for generating the `email2` property too. At Lines 11-12, we assign the `firstName` and `surname` constants using `ES6` syntax.

   The function `faker.name.firstName()` used to generate a random first name takes an argument which can be either `0` or `1`. `0` generates a male first name while `1` generates a female first name. So to achieve more randomness in the generation of the first names, we use the function `faker.random.arrayElement()`. The `arrayElement()` function takes an array argument and returns a random element in the array. So, `faker.random.arrayElement([0, 1])` will return either `0` or `1` randomly.

4. At Line 8, we create and initialise `omitAddresses` constant which will be used to randomly determine if address-related properties will be generated from Line 29 downwards.

5. The `faker` functions are very easy to understand. However, you will notice that some properties have self-executing functions assigned to them. They include: `jobTitle`, `email2`, `birthday`, `website`, and `notes` properties.

   Self-executing functions are also known as `IIFEs (Immediately-Invoked Function Expressions)`. Let me explain why I used `IIFEs`.

   Now, consider this line: `company: faker.company.companyName(),`. For each `ContactFactory.create()` or `ContactFactory.createMany()` call from the seeder, the factory will resolve all the properties defined within the object return at Line 10. When it gets to the `company` property, it will call the value assigned to the property. Because the value is a function execution -`faker.company.companyName()` - a random company name will be generated and assigned to the company. It is function execution because of the `()` appended to `companyName`. If we do not append `()`, we will just be returning the `companyName` function and not executing it.

   Now, imagine that we defined the `jobTitle` property as just an arrow function as shown below:

   ```ts
   jobTitle: () => {
   const omit = faker.datatype.boolean()
   return omit ? null : faker.name.jobTitle()
   },
   ```

````

   When the `jobTitle` property is resolved, the assigned function will be returned. That is: `typeof jobTitle` will be equal to `function` not `string`. This is not what we want. So, to avoid this dangerous `beginner mistake`, we have to wrap and self-execute the function:

      ```ts
   // Step 1: wrap the function:
    jobTitle: (() => {
      const omit = faker.datatype.boolean()
      return omit ? null : faker.name.jobTitle()
    }),

   // Step 2: self-execute it by appending `()`
    jobTitle: (() => {
      const omit = faker.datatype.boolean()
      return omit ? null : faker.name.jobTitle()
    })(), // <-- See here
````

Now, when `jobTitle` is called, the function will self-execute and return the job title string that we want.

6. You will also notice this line (Line 15) within the IIFE: `const omit = faker.datatype.boolean()`.We use the line to generate local random `true` and `false` values which determines if the `jobTitle` should be generated or not. The same is observed in other properties with IIFEs.

7. The logic of the other properties follow similar formats. Please study the code.

8. The `birthday` property needs special mention. For the `birthday` property, our intention is to return a Luxon `DateTime` object. This is very important. Remember that the `birthday` property in our `api/app/Models/Contact.ts` model file has the type: `DateTime | null | undefined`. So we can only return a `DateTime` object or `null` or `undefined` from the IIFE. Else, a type error will be thrown by TypeScript.

   Because of this, we start by calling `faker.date.past()` to generate a random past date (that is, a date before `now`). The `faker.date.past()` function returns a native JavaScript `Date` object. Because we now have a native JavaScript `Date` object, we make use of Luxon's `DateTime.fromJSDate()` function to convert the JavaScript `Date` object to a Luxon `DateTime` object by passing in the `Date` object as the only argument in the `DateTime.fromJSDate()` function. Therefore: `DateTime.fromJSDate(faker.date.past())` returns a Luxon `DateTime` object in order to satisfy our type constraints.

9. Lastly, after the `define()` function, we chain the `build()` function to build/compile the factory.

Now, let's consume our `ContactFactory`.

## Creating the `Contact` seeder.

AdonisJS has a command for creating seeders. Run:

```bash
# Make sure you are in the `api` directory
node ace make:seeder Contact
# CREATE: database\seeders\Contact.ts
```

Now, open the `api/database/seeders/Contact.ts` file. Copy and paste the lines below into the file. Please refer [this snapshot](https://github.com/ndianabasi/google-contacts/blob/16-seeding-and-listing-contacts/api/database/seeders/Contact.ts) for this update.

```
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder'
import { ContactFactory } from '../factories'

export default class ContactSeeder extends BaseSeeder {
  public async run() {
    await ContactFactory.createMany(100)
  }
}
```

Save the file. Let's discuss what's going on within the file.

1. At Line 1, we import the `BaseSeeder` from `@ioc:Adonis/Lucid/Seeder` package. Our `ContactSeeder` class will extend the `BaseSeeder` and inherit its methods and properties.

2. At Line 2, we import `ContactFactory` from `api/database/factories/index.ts`

3. At Line 5, we define the `run` method. A `seeder` class which extends the `BaseSeeder` class must have a `run` method. The `run` method is the entry point into a `seeder`.

4. Within the `run` method, we call and await `ContactFactory.createMany()` static method. The `createMany()` method takes an integer argument which is the number of records factory records we want to generate and persist to the database. In this case, we want to generate `100` random contacts from the `ContactFactory` and persist them to the database.

## Running the `Contact Seeder`

To run the `Contact` seeder, do the following:

```bash
# Make sure that you are in the `api` directory. Do:
node ace db:seed -i
# completed database\seeders\Contact
```

This starts the `db:seed` command in interactive mode. It might take some time to start. When asked to `Select files to run`, press the space bar to select: `database\seeders\Contact`. Press `Enter`. This will generate and seed/persist 100 random contacts into the `contacts` table. You can generate more random contacts by re-run the `Contact` seeder as many times as you want.

Alternatively, you assign the file you want to seed with the `--files` flag. This can save you some time:

```bash
node ace db:seed --files database/seeders/Contact.ts
```

Open MySQL Workbench and inspect the `contacts` table. You will see a bunch of new `contact` rows.

## Listing All Contacts with Pagination

Since we have a bunch of contacts in our `contacts` table, we can now list or fetch them. Now, if we have 10000 contacts, we do not want to fetch all 10000 contacts at once. The performance will be awful. So we have to use `pagination` to control how many contacts we want to fetch per call and the page to fetch. This is how we will be able to display the contacts on the frontend.

Let's start.

1. Open the API route file: `api/start/routes.ts`. Add this route definition to the file:

   ```
   ```

Route.get('/contacts', 'ContactsController.index')

````

   Here we are defining a `GET` method on the path `/contacts`. The `index` method of the `ContactsController` file is defined as the route handler. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/16-seeding-and-listing-contacts/api/start/routes.ts#L34).

2. Open `api/app/Controllers/Http/ContactsController.ts`. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/16-seeding-and-listing-contacts/api/app/Controllers/Http/ContactsController.ts#L7-L24). Update the `index` method to:

   ```ts
  public async index({ request, response }: HttpContextContract) {
    try {
      const { page, perPage } = request.qs()

      const contacts = await Contact.query()
        .select(['id', 'first_name', 'surname', 'email1', 'phone_number1', 'company', 'job_title'])
        .paginate(page, perPage)

      return response.ok({ data: contacts })
    } catch (error) {
      Logger.error('Error at ContactsController.list:\n%o', error)

      return response.status(error?.status ?? 500).json({
        message: 'An error occurred while deleting the contact.',
        error: process.env.NODE_ENV !== 'production' ? error : null,
      })
    }
  }
````

Save the files. Let's discuss what's going on within the `index` method:

1. We destructure `page` and `perPage` from the `request.qs()` method. The `request.qs()` parses the `query` portion of our API path and creates a record containing the query parameters and their corresponding values. Our path for paginating contacts will look like this: `/contacts?perPage=50&page=1`. A call to `request.qs()` will return the object:

   ```ts
   ```

{
perPage: 50,
page: 1
}

````

2. We make the query:

   ```ts
await Contact.query()
        .select(['id', 'first_name', 'surname', 'email1', 'phone_number1', 'company', 'job_title'])
        .paginate(page, perPage)
````

The `query()` method returns a `query builder` instance which we can apply query statements on. We need to call the `select` and `paginate` methods on the `query builder` instance. The `select` method accepts an array of columns we want to display from the `contacts` table. While the `paginate` method accepts two argument: `page` and `perPag`. The `page` argument indicates the current page we are fetching from paginated result while the `perPage` argument indicates the number of rows which should be return from the `contacts` table for each pagination call.

3. We assign the result of the pagination to the `contacts` constant and then return the `contacts` at Line 15.

## Testing the Pagination with Postman

To conclude this lesson, we will use Postman to fetch paginated contacts results from our API server.

For the `GET /contacts` endpoint, do the following:

1. Right-click on the `CRUD` collection and click `Add Request`. Enter `List Contacts` as the name.

2. Ensure that the request method is `GET`. Enter `/contacts?perPage=5&page=1` in the`Request URL` field. You will notice that as your enter the query parameters and their values, the keys and values within the `Query Params` table within the `Params` tab will automatically update. We want to fetch 5 rows at a time. Feel free to use any number you want for the `perPage` parameter. Save the request.

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

3. Ensure that your API server is running. If it is not running, do the following:

   ```bash
   ```

# Ensure that you are in the `api` directory. Then run:

yarn serve

````

4. Click the `Send` button to send the request.

5. Your result should be like this:

   ```json
{
    "data": {
        "meta": {
            "total": 211,
            "per_page": 5,
            "current_page": 1,
            "last_page": 43,
            "first_page": 1,
            "first_page_url": "/?page=1",
            "last_page_url": "/?page=43",
            "next_page_url": "/?page=2",
            "previous_page_url": null
        },
        "data": [
            {
                "id": "ckut8nv4a00003cvo9rg0bbgc",
                "first_name": "Hammad",
                "surname": "Pulham",
                "email1": "hpulham0@si.edu",
                "phone_number1": "+420 (767) 548-7576",
                "company": null,
                "job_title": null
            },
            {
                "id": "ckuxw4ivm000074vo5eyvcjn3",
                "first_name": "Zechariah",
                "surname": "Pollak",
                "email1": "zpollak1@blogtalkradio.com",
                "phone_number1": "+66 (700) 444-4282",
                "company": "Welch, Littel and Rowe",
                "job_title": "Account Executive"
            },
            {
                "id": "ckuyj7rb900010ovo5khu3gqt",
                "first_name": "George",
                "surname": "Schiller",
                "email1": "George72@yahoo.com",
                "phone_number1": "319.296.0522 x974",
                "company": "Moen LLC",
                "job_title": "Principal Implementation Architect"
            },
            {
                "id": "ckuyj7rbr00020ovobba6d3ja",
                "first_name": "Moses",
                "surname": "Hand",
                "email1": "Moses.Hand@gmail.com",
                "phone_number1": "(324) 307-7850",
                "company": "Lubowitz, Hirthe and Gorczany",
                "job_title": "Future Quality Specialist"
            },
            {
                "id": "ckuyj7rc600030ovo5nai1lhl",
                "first_name": "Maggie",
                "surname": "Nicolas",
                "email1": "Maggie.Nicolas31@gmail.com",
                "phone_number1": "1-285-526-9566 x68993",
                "company": "Goyette, Kerluke and Keebler",
                "job_title": "Legacy Assurance Technician"
            }
        ]
    }
}
````

6. Update the value of the `page` parameter to 2. And click the `Send` button. This fetches the 2nd page of the result.

This is how pagination is done in API servers. Congratulations.

This concludes this lesson. In the next lesson, we will return to the frontend and begin to connect it to the API server.

Save all your files, commit, merge with the master branch, and push to the remote repository (GitHub).

```bash
git add .
git commit -m "feat(api): complete seeding and listing of contacts"
git push origin 16-seeding-and-listing-contacts
git checkout master
git merge master 16-seeding-and-listing-contacts
git push origin master
```


*This document was generated from the live article page on https://ndianabasi.com/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 • 2026-06-07*
