Generic types allows you to reduce code duplication. For example, if all your index
endpoints return paginated responses it wouldn't be productive to write again and again the same class for each of your models.
Hopefully we can leverage mixins to avoid this.
import { ApiProperty , ApiBody } from '@foadonis/openapi/decorators'
function PaginatedResponse < TItem extends object >( Item : new ( ... args : any []) => TItem ) {
abstract class Pagination {
@ ApiProperty ()
declare total : number
@ ApiProperty ({ type : [ Item ] })
declare items : TItem []
}
}
class RecipesPaginatedResponse extends PaginatedResponse ( Recipe ) {}
class UsersController {
@ ApiBody ({ type : RecipesPaginatedResponse })
index () {
// ...
}
}
In the following example we will create a mixin to create schemas for Lucid pagination .
app/controllers/users_controller.ts import { HttpContext } from '@adonisjs/core/http'
import { ApiQuery } from '@foadonis/openapi/decorators'
import User from '#models/user'
export class UsersController {
@ ApiQuery ({ name : 'page' })
@ ApiQuery ({ name : 'limit' })
index ({ request } : HttpContext ) {
const page = request . input ( 'page' , 1 )
const limit = request . input ( 'limit' , 25 )
return User . query (). paginate ( page , limit )
}
}
First we will need to create the schema for the meta
property of our PaginatedResponse.
app/openapi/schemas/paginated_response.ts import { ApiProperty } from '@foadonis/openapi/decorators'
export class PaginatedResponseMeta {
@ ApiProperty ()
declare total : number
@ ApiProperty ()
declare perPage : number
@ ApiProperty ()
declare currentPage : number
@ ApiProperty ()
declare lastPage : number
@ ApiProperty ()
declare firstPage : number
@ ApiProperty ()
declare firstPageUrl : string
@ ApiProperty ()
declare lastPageUrl : string
@ ApiProperty ({ required : false })
declare nextPageUrl ?: string
@ ApiProperty ({ required : false })
declare previousPageUrl ?: string
}
We can now create a mixin that will have the properties data
and meta
.
app/openapi/schemas/paginated_response.ts import { ApiProperty } from '@foadonis/openapi/decorators'
export default function PaginatedResponse < TItem extends object >(
Item : new ( ... args : any []) => TItem
) {
abstract class Pagination {
@ ApiProperty ()
declare meta : PaginatedResponseMeta
@ ApiProperty ({ type : [ Item ] })
declare items : TItem []
}
}
You can now use the mixin in your controllers and simply pass the User
model as a parameter.
app/controllers/users_controller.ts import { HttpContext } from '@adonisjs/core/http'
import { ApiQuery , ApiResponse } from '@foadonis/openapi/decorators'
import User from '#models/user'
import PaginatedResponse from '#openapi/schemas/paginated_response'
class UsersPaginatedResponse extends PaginatedResponse ( User ) {}
export class UsersController {
@ ApiQuery ({ name : 'page' })
@ ApiQuery ({ name : 'limit' })
@ ApiResponse ({ type : UsersPaginatedResponse })
index ({ request } : HttpContext ) {
const page = request . input ( 'page' , 1 )
const limit = request . input ( 'limit' , 25 )
return User . query (). paginate ( page , limit )
}
}