Recipes - Search command
If you need to perform complex queries, read operations may not suffice.
You can create a search
command to use the full potential of your Database / ORM.
Setup
Start by generating a new cmd using the CLI.
Let's change the DTO to accept a string to be used as a regex:
export class SearchDto {
@IsString()
@$Transform((v: string) => v.replace(/[.*+?^$}{)(|[\]\\]/g, '\\$&'))
userNameLike: string;
@IsOptional()
@IsString()
visibility?: 'public' | 'private';
}
export type SearchReturnDto = any;
Note
We escape userNameLike
of any regex character to avoid potential regex attacks (ReDoS).
Then, let's allow guests to use the search command in the security:
const getCmdSecurity = (search, profile): CmdSecurity => {
return {
dto: SearchDto,
rolesRights: {
guest: {
async defineCMDAbility(can, cannot, ctx) {
// Define abilities for guest
can(search, profile);
}
}
},
}
}
guestCanUseAll
parameter for better performance.
const getCmdSecurity = (search, profile): CmdSecurity => {
return {
dto: SearchDto,
rolesRights: {},
guestCanUseAll: true
}
}
Implementation
We can now implement the command action, to make things simpler we use the CrudService->$find method.
export async function search(this: ProfileService, dto: SearchDto, ctx: CrudContext, inheritance?: any ){
const query: Partial<Profile> = {
userName: new RegExp(dto.userNameLike, 'i') as any,
};
if (dto.visibility) {
query.visibility = dto.visibility as any;
}
const fakeCtx: CrudContext = {
...ctx,
query,
data: null,
origin: 'crud',
method: 'GET',
};
await this.crudAuthorization.authorize(fakeCtx, this.security);
const opParams = ctx.queryOptions ? {options: ctx.queryOptions} : undefined;
return this.$find(query, fakeCtx, opParams);
}
Note
The call to service.crudAuthorization.authorize
allows us to use the CRUD security for read
operations. Anything forbidden for $find
will be forbidden in our search
cmd as well.
Usage
Since search
returns the result of $find
, it behaves as a limited command.
In our front-end, we call it with CrudClient->cmdL to benefit from the limit
and offset
options.
const searchDto: SearchDto = {
userNameLike: 'doe',
visibility: 'public',
};
const options: ICrudOptions = {
limit: 5,
}
const { data, total, limit } = await profileClient.cmdL(
'search',
searchDto,
options
);
// results : [{ userName: "Michael Doe", ... }, { userName: "Jon Doe", ...}]
Note
If no limit
is specified, the client will perform multiple requests until total
profiles have been fetched. The maximum limit
that can be used is set in the CmdSecurity.