AdonisJS v6 is here. Learn more in our release blog post.

Pagination

Lucid has inbuilt support for offset-based pagination. You can paginate the results of a query by chaining the .paginate method.

The paginate method accepts the page number as the first argument and the rows to fetch as the second argument. Internally, we execute an additional query to count the total number of rows.

const page = request.input('page', 1)
const limit = 10
const posts = await Database.from('posts').paginate(page, limit)
console.log(posts)

The paginate method returns an instance of the SimplePaginatorClass . It holds the meta data for the pagination, alongside the fetched rows.

SimplePaginator {
perPage: 10,
currentPage: 1,
firstPage: 1,
isEmpty: false,
total: 50,
hasTotal: true,
lastPage: 5,
hasMorePages: true,
hasPages: true
}

It is recommended to use the orderBy method when using pagination to avoid a different order every time you query the data.

Following is a complete example of displaying the pagination links inside an Edge template.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Database from '@ioc:Adonis/Lucid/Database'
class PostsController {
public async index ({ request, view }: HttpContextContract) {
const page = request.input('page', 1)
const limit = 10
const posts = await Database.from('posts').paginate(page, limit)
// Changes the baseURL for the pagination links
posts.baseUrl('/posts')
return view.render('posts/index', { posts })
}
}

Open the posts/index.edge file and paste the following code snippet inside it.

<div>
@each(post in posts)
<h1>{{ post.title }}</h1>
<p> {{ excerpt(post.body, 200) }} </p>
@endeach
</div>
<hr>
<div>
@each(anchor in posts.getUrlsForRange(1, posts.lastPage))
<a href="{{ anchor.url }}">
{{ anchor.page }}
</a>
@endeach
</div>

The getUrlsForRange method accepts a range of pages and returns an array of objects with the following properties.

[
{
url: '/?page=1',
page: 1,
isActive: true,
isSeperator: false,
},
{
url: '/?page=2',
page: 2,
isActive: true,
isSeperator: false,
},
// ...
]

Serializing to JSON

You can also serialize the paginator results to JSON by calling the toJSON method. It returns the key names in snake_case by default. However, you can pass a naming strategy to override the default convention.

const posts = await Database.from('posts').paginate(page, limit)
return posts.toJSON()
{
"meta": {
"total": 50,
"per_page": 5,
"current_page": 1,
"last_page": 10,
"first_page": 1,
"first_page_url": "/?page=1",
"last_page_url": "/?page=10",
"next_page_url": "/?page=2",
"previous_page_url": null
},
"data": []
}

In the following example, we override the naming strategy to return keys in camelCase.

const posts = await Database.from('posts').paginate(page, limit)
posts.namingStrategy = {
paginationMetaKeys() {
return {
total: 'total',
perPage: 'perPage',
currentPage: 'currentPage',
lastPage: 'lastPage',
firstPage: 'firstPage',
firstPageUrl: 'firstPageUrl',
lastPageUrl: 'lastPageUrl',
nextPageUrl: 'nextPageUrl',
previousPageUrl: 'previousPageUrl',
}
}
}
return posts.toJSON()

You can also assign a custom naming strategy to the SimplePaginator class constructor to override it globally. The following code must go inside a provider or a preload file .

import { ApplicationContract } from '@ioc:Adonis/Core/Application'
export default class AppProvider {
constructor(protected app: ApplicationContract) {}
public async ready() {
const Db = this.app.container.use('Adonis/Lucid/Database')
Db.SimplePaginator.namingStrategy = {
paginationMetaKeys() {
return {
// ... same as above
}
}
}
}
}

Additional reading