Skip to content

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.

eicrud generate cmd profile search

Let's change the DTO to accept a string to be used as a regex:

search.dto.ts
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:

search.security.ts
const getCmdSecurity = (search, profile): CmdSecurity => { 
    return {
        dto: SearchDto,
        rolesRights: {
            guest: {
                async defineCMDAbility(can, cannot, ctx) {
                    // Define abilities for guest
                    can(search, profile);
                }
            }
        },
    }
}
Alternatively, you can use the guestCanUseAll parameter for better performance.
search.security.ts
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.

search.action.ts
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.

profile.security.ts
guest: {
    async defineCRUDAbility(can, cannot, ctx) {
        can('read', profile, { visibility: 'public'});
    },
},

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.