Pagination

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 db.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 { HttpContext } from '@adonisjs/core/http'
import db from '@adonisjs/lucid/services/db'
class PostsController {
async index({ request, view }: HttpContextContract) {
const page = request.input('page', 1)
const limit = 10
const posts = await db.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 camelCase by default. However, you can pass a naming strategy to override the default convention.

const posts = await db.from('posts').paginate(page, limit)
return posts.toJSON()
{
"meta": {
"total": 50,
"perPage": 5,
"currentPage": 1,
"lastPage": 10,
"firstPage": 1,
"firstPageUrl": "/?page=1",
"lastPageUrl": "/?page=10",
"nextPageUrl": "/?page=2",
"previousPageUrl": null
},
"data": []
}

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

const posts = await db.from('posts').paginate(page, limit)
posts.namingStrategy = {
paginationMetaKeys() {
return {
total: 'total',
perPage: 'per_page',
currentPage: 'current_page',
lastPage: 'last_page',
firstPage: 'first_page',
firstPageUrl: 'first_page_url',
lastPageUrl: 'last_page_url',
nextPageUrl: 'next_page_url',
previousPageUrl: 'previous_page_url',
}
},
}
return posts.toJSON()

You can also assign a custom naming strategy to the SimplePaginator class constructor to override it globally inside a service provider

import db from '@adonisjs/lucid/services/db'
import type { ApplicationService } from '@adonisjs/core/types'
export default class AppProvider {
constructor(protected app: ApplicationService) {}
async ready() {
db.SimplePaginator.namingStrategy = {
paginationMetaKeys() {
return {
// ... same as above
}
},
}
}
}