9

I have NestJS application I have to get the query from the URL. Problem is that my creationDate is an object and I can't get it as the nested object via @Query.

Here is example of the route:

xxx/yyy/orders?key1=value&key2=value&key3=value&createdAt[lte]=2021-07-01&createdAt[gte]=2011-03-30&orderBy=desc&limit=500

I am trying to get createdAt[lte] and createdAt[gte] as the nested object

export interface QueryI {
key1: string;
key2: string;
key3: string;
key4: string;
createdAt: {
    lte: string,
    gte: string,
}
orderBy: 'desc' | 'asc';
limit: number;
}

Here is my controller:

@Get('route')
getAll(
    @Query() query: QueryI): Promise<void> { 
    return this.myService.findAll(query);
}

but this gives me following result

{

key1: 'value',        
  key2: 'value',
  key3: 'value',       
  key4: 'value',
  'createdAt[lte]': 'some date',
  'createdAt[gte]': 'some date',
  orderBy: 'desc',
  limit: '500'

}

I have tried JSON.stringify and consulted similar questions on SOW but no luck.

Thank you

4
  • What's your HTTP engine? And what are you sending the request through? It may not be encoding the query parameters correctly Commented Oct 27, 2021 at 15:52
  • Not sure what do you mean by HTTP engine but I use postman to test it. Commented Oct 28, 2021 at 6:27
  • 1
    By HTTP engine, I mean express or fastify, which are the underlying engines Nest can use. Using curl to send the requests this worked: curl 'http://localhost:3000/?key1=value1&key2%5Binner1%5D=hello&key2%5Binner2%5D=world' while this didn't: 'http://localhost:3000/?key1=value&key2[inner1]=hello&key2[inner2]=world'. So like I said, it might be a query parameter encoding issue Commented Oct 28, 2021 at 15:21
  • I use Fastify with NestJs. I can't change the encoding of the URL coming. I am building a connector that connect 3 different API. This is the incoming request that I have no control over. Any chance I can make it work? Thanks Commented Oct 29, 2021 at 6:17

4 Answers 4

3

Switch QueryI from an interface to a class.

Working on NestJS 7.

Url used for testing

http://localhost:3000/api/v1/store/search?nestedObj[key1]=yolo&nestedObj[key2]=sup%20bruh&el=test

DTO Used

export class TestDto {
  el: string;
  nestedObj: {
    key1: string;
    key2: string;
  };
}

Controller

@Controller('v1/store')
@UsePipes(new ValidationPipe({ exceptionFactory: getFormattedErrors }))
@UseFilters(DomainExceptionFilter)
export class TestController{
@Get('search')
  public async searchStore(@Query() testDto: TestDto) {
    console.log(testDto);
    return 1;
  }
}
// Console log - { nestedObj: { key1: 'yolo', key2: 'sup bruh' }, el: 'test' }

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your answer but this doesn't work, I am not sure why ... Unless the decorators are important for it to work in which case plase add the imports. EDIT: my nestjs/common is 8.0.0
Did you use a validation pipe? If not, use one, then try.
I have just added this one @UsePipes(new ValidationPipe({ transform: true })), it still didn't work... I am new to NestJS can you give me more info pls
it works for me the problem was how to pass nested values via query string other things are same as post, so if anybody has same problem at first fix the issue via a post endpoint and use this method for sending nested fields.
3

EDIT

In the end I have implemented diferent classes and used @Pipe to validate my query: Here is the code:

pipe.ts

@Injectable()
export class ValidateQueryPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    let incomingQuery = value as IncomingQuery;
    let mappedQuery = new QueryParameters()
    mappedQuery.key1 = incomingQuery.key1
    mappedQuery.key2= incomingQuery.key2
    // other mapped valus
    mappedQuery.createdAt = new CreatedAt(incomingQuery['createdAt[lte]'], incomingQuery['createdAt[gte]'])        
    return mappedQuery;
  }

controller.ts

@Get(':someurl/someurl')
getAllOrders(
    @Query(ValidateQueryPipe) query: QueryParameters): Promise<Observable<any>> { 
    return this.service.findAll(erpCode, query);
}

query-parameters.ts

import { CreatedAt } from "./created-at";

export class QueryParameters {
    key1: string;
    key2: string;
    createdAt: CreatedAt;
}

created-at.ts

export class CreatedAt {
    lte: string;
    gte: string;

    constructor(lte: string, gte: string){
        this.lte = lte;
        this.gte = gte;
    };
}

Well I have found a partial solution and I will post it here untill better answer comes along: I am extracting the nested properties into seperate queries and creating my model. It's not perfect but it's doing the job for now...

controller.ts

 getAllOrders(
    @Query() query,
    @Query('createdAt[lte]') lte: string, 
    @Query('createdAt[gte]') gte: string): Promise<void> { 
    const mappedObject = new QueryParameters(query, lte, gte)
    return this.orderService.findAll(erpCode, mappedObject);
}

createdAtModel.ts

    export class CreatedAt {
    lte: string;
    gte: string;

    constructor(lte: string, gte: string){
        this.lte = lte;
        this.gte = gte;
    };
}

my-model.ts

import { CreatedAt } from "./created-at";

export class QueryParameters {
    //other keys
    createdAt: CreatedAt;

    constructor(query, lte, gte){
        //other keys
        this.createdAt = new CreatedAt(lte, gte)
    }
}

I am sure that better answer lies in using the NestJS validation pipes but I didn't manage to find the one that works for me.

Comments

2

Express use qs package under hood, unlike Fastify. So, my approach is to write a custom Pipe like below:

import { parse } from 'qs';

export class ParseQueryStringPipe {
  transform(value: string): object {
    return parse(value);
  }
}

Comments

1

There is built-in QS parsing in nestjs According to the documentation : https://docs.nestjs.com/controllers#query-parameters :

you'll need to configure your HTTP adapter (Express or Fastify) to use an appropriate query parser. In Express, you can use the extended parser, which allows for rich query objects:

const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.set('query parser', 'extended');

In Fastify, you can use the querystringParser option:

const app = await NestFactory.create<NestFastifyApplication>(
  AppModule,
  new FastifyAdapter({
    querystringParser: (str) => qs.parse(str),
  }),
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.