import { IQueryHandler, QueryHandler } from '@nestjs/cqrs'; import { Ok, Result } from 'oxide.ts'; import { PaginatedParams, PaginatedQueryBase } from '@libs/ddd/query.base'; import { Paginated } from '@src/libs/ddd'; import { InjectPool } from 'nestjs-slonik'; import { DatabasePool, sql } from 'slonik'; import { UserModel, userSchema } from '../../database/user.repository'; export class FindUsersQuery extends PaginatedQueryBase { readonly country?: string; readonly postalCode?: string; readonly street?: string; constructor(props: PaginatedParams) { super(props); this.country = props.country; this.postalCode = props.postalCode; this.street = props.street; } } @QueryHandler(FindUsersQuery) export class FindUsersQueryHandler implements IQueryHandler { constructor( @InjectPool() private readonly pool: DatabasePool, ) {} /** * In read model we don't need to execute * any business logic, so we can bypass * domain and repository layers completely * and execute query directly */ async execute( query: FindUsersQuery, ): Promise, Error>> { /** * Constructing a query with Slonik. * More info: https://contra.com/p/AqZWWoUB-writing-composable-sql-using-java-script */ const statement = sql.type(userSchema)` SELECT * FROM users WHERE ${query.country ? sql`country = ${query.country}` : true} AND ${query.street ? sql`street = ${query.street}` : true} AND ${query.postalCode ? sql`"postalCode" = ${query.postalCode}` : true} LIMIT ${query.limit} OFFSET ${query.offset}`; const records = await this.pool.query(statement); return Ok( new Paginated({ data: records.rows, count: records.rowCount, limit: query.limit, page: query.page, }), ); } }