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.
We're using a simple regex to search on the username
field, but you can use any of MikroOrm's operators like $and
& $or
.
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.
Note
Since search is a read-only command, you can enable allowGetMethod
in the CmdSecurity. This allows for searching using a simple url: /crud/s/profile/cmd/search?query={"userNameLike":"doe"}"