Can I write a DTO which extracts both query and params vars in nestjs - nestjs

I'm making an api which looks like:
http://example.com/story/:storyId/analytics?from={date}&to={date}
The storyId is part of the path, but from and to are query parameters.
I've got a DTO, like this:
class GetStoryAnalyticsDTO {
storyId: string
from: Date
to: Date
}
(validators omitted for brevity)
And I'm using it like this in my controler:
#Get()
getStoryAnalytics(#Query() query: GetStoryAnalyticsRequestDto): Promise<MyResponse> {...}
But, (obviously!), this only extracts the from and to parameters.
Is there any way to extract both from the query and the path to get all the vars in one dto?
If not, it's not a massive hassle - I can just add #Param storyId: string to the controller and it's all good :)

You could make a custom decorator like #QueryParam() that pulls together req.query and req.params. It could look something like this:
export const QueryParam = createParamDecorator((data: unknown, context: ExecutionContext) => {
const req = context.switchToHttp().getRequest();
return { ...req.query, ...req.params };
});
And just make sure to add validateCustomDecorators on the ValidationPipe if you want it to auto validate for you. Otherwise, you're good to start using it.

Related

How to get gql return type of resolver from within resolver?

EDIT: Maybe a simpler question - Is there any way of accessing __typename in a resolver? like this php question https://github.com/webonyx/graphql-php/discussions/1087
I am trying to prevent manually defining the type arg of fieldWasRequested below. Can I reliably get this string from within the resolver? This would be the same as the resolvers __typename value. I found a way that kinda gets close but is ultimately too brittle.
I am using parseResolveInfo from graphql-parse-resolve-info to check if a field was requested in a resolver. In order to do this, I need to access the type name of the resolver. Currently I am manually setting this and getting the name by looking at the graphql schema file.
import { parseResolveInfo } from 'graphql-parse-resolve-info'
export const fieldWasRequested = (field: string, type: string, info: GraphQLResolveInfo) => {
const parsedInfo = parseResolveInfo(info)
const fields = parsedInfo?.fieldsByTypeName?.[type] ?? {}
return field in fields
}
export const thing1: Resolvers['thing1'] = async (parent, args, ctx, info): Promise<Thing1[]> => {
const fieldWasRequested = fieldWasRequested('field1', 'Thing1', info)
// continued ...
}
I see there is info.returnType, which is this GraphQLOutputType. This is a unioin of many types, all of which seem to have a toString method.
Logging info.returnType.toString() returns [Thing1]. This is an array of Thing1. But for other resolvers it could be Thing2, [Thing3!]!, etc. It's just not feasible to parse from this. Is there a better way?

NestJSX get parameter from CrudRequest object

I want to overrride the following route which was generated by nestjsx:
GET /offer-event-matchings/{id}
To get the id from the CrudRequest I wrote the following code.
#Override()
getOne(#ParsedRequest() req: CrudRequest): Promise<GetUserDto> {
const id = req.parsed.search.$and[1]['id']['$eq'];
return this.service.getOfferEventMatching(id);
}
It works but I think and hope there is a better and more beautiful way to get the id from the CrudRequest object?
The bottom of the section Routes Override in the docs mentions you can use the typical decorators as well, so the easiest would be to use Param:
getOne(
#ParsedRequest() req: CrudRequest,
#Param('id') id: string
): Promise<GetUserDto> {
// code here
}

How to build a Graqhql mutation with existing variables

This might seem like an odd question, or something really straightforward, but honestly I am struggling to figure out how to do this. I am working in Node.js and I want to set data I have saved on a node object into my GraphQL mutation.
I'm working with a vendor's GraqhQL API, so this isn't something I have created myself, nor do I have a schema file for it. I'm building a mutation that will insert a record into their application, and I can write out everything manually and use a tool like Postman to manually create a new record...the structure of the mutation is not my problem.
What I'm struggling to figure out is how to build the mutation with variables from my node object without just catting a bunch of strings together.
For example, this is what I'm trying to avoid:
class MyClass {
constructor() {
this.username = "my_username"
this.title = "Some Title"
}
}
const obj = new MyClass()
let query = "mutation {
createEntry( input: {
author: { username: \"" + obj.username + "\" }
title: \"" + obj.title + "\"
})
}"
I've noticed that there are a number of different node packages out there for working with Graphql, but none of their documentation that I've seen really addresses the above situation. I've been completely unsuccessful in my Googling attempts, can someone please point me in the right direction? Is there a package out there that's useful for just building queries without requiring a schema or trying to send them at the same time?
GraphQL services typically implement this spec when using HTTP as a transport. That means you can construct a POST request with four parameters:
query - A Document containing GraphQL Operations and Fragments to execute.
operationName - (Optional): The name of the Operation in the Document to execute.
variables - (Optional): Values for any Variables defined by the Operation.
extensions - (Optional): This entry is reserved for implementors to extend the protocol however they see fit.
You can use a Node-friendly version of fetch like cross-fetch, axios, request or any other library of your choice to make the actual HTTP request.
If you have dynamic values you want to substitute inside the query, you should utilize variables to do so. Variables are defined as part of your operation definition at the top of the document:
const query = `
mutation ($input: SomeInputObjectType!) {
createEntry(input: $input) {
# whatever other fields assuming the createEntry
# returns an object and not a scalar
}
}
`
Note that the type you use will depend on the type specified by the input argument -- replace SomeInputObjectType with the appropriate type name. If the vendor did not provide adequate documentation for their service, you should at least have access to a GraphiQL or GraphQL Playground instance where you can look up the argument's type. Otherwise, you can use any generic GraphQL client like Altair and view the schema that way.
Once you've constructed your query, make the request like this:
const variables = {
input: {
title: obj.title,
...
}
}
const response = await fetch(YOUR_GRAPHQL_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables }),
})
const { data, errors } = await response.json()

how to use optional url parameters with NestjS

I'm trying to replace our current backend service using Nestjs library,
however, I want to create a route with 2 optional parameters in the URL something like :
/route/:param1/config/:OptionalParam3?/:OptionalParam3?
that means the route should catch :
route/aa/config
route/aa/config/bb
route/aa/config/bb/cc
how can I achieve that, I have tried to use ? and () but it's not working well.
If you are looking for how to annotate an optional query parameter, you can do it like so:
#ApiQuery({
name: "myParam",
type: String,
description: "A parameter. Optional",
required: false
})
async myEndpoint(
#Query("myParam") myParam?: string
): Promise<blah> {
[...]
}
Router params name should be unique. The correct route path is:
Existing one is:
/route/:param1/config/:OptionalParam3?/:OptionalParam3?
Correction:
/route/:param1/config/:OptionalParam3?/:OptionalParam4?
Opinion: You can use query params if the params are optional. It is never a good idea to create optional param routes (disagreements agreed). Both serve the same purpose, but having them as the query params makes it more understandable for debugging and fellow developers.
I solved this problem by using #Query decorator as below:
Here is my controller:
#Get()
async getAll(#Query('someParameter') someParameter?: number) {
return this.service.getAll(someParameter);
}
Here is my client (Angular) service:
getAll(someParameter?: number) {
return this.http.get(`apiUrl/controllerAddress?someParameter=${someParameter}`
);
}
You can use this structure:
-route
-aa
-config
-[[...id]].js
It will work for the routes :
route/aa/config/{anything}

Automatically parse query parameter to object when defined in NestJS

I am writing a NestJS application. Some of the endpoints support sorting e.g. http://127.0.0.1:3000/api/v1/members?sort=-id&take=100 Which means sort by id descending.
This parameter arrives as a #Query parameter and is passed to my service. This service transforms it into an object which is used by TypeORM:
{
id: 'DESC'
}
I don't want to call this conversion method manually every time I need sorting.
I've tried an intercepter but this one could not easily change the request parameters into the desired object.
A pipe worked but then I still need to add #Query(new SortPipe()) for every endpoint definition.
Another option is in the repository itself. The NestJS documentation is very well written, but misses guidance in where to put what.
Is there someone who had a similar issue with converting Query parameters before they are used in NestJS, and can explain what approach is the best within NestJS?
This question might look like an opinion based question, however I am looking for the way it is supposed to be done with the NestJS philosophy in mind.
Pipes are probably the easiest way to accomplish this. Instead of adding your pipe for every endpoint definition you can add a global pipe that will be called on every endpoint. In your main.ts:
async function bootstrap() {
...
app.useGlobalPipes(new SortPipe());
...
}
You can then create a pipe like this:
import { PipeTransform, Injectable, ArgumentMetadata } from '#nestjs/common';
#Injectable()
export class SortPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
const { type } = metadata;
// Make sure to only run your logic on queries
if (type === 'query') return this.transformQuery(value);
return value;
}
transformQuery(query: any) {
if (typeof query !== 'object' || !value) return query;
const { sort } = query;
if (sort) query.sort = convertForTypeOrm(sort);
return query;
}
}
If you do not want sort value on ALL endpoints to be automatically converted, you can pass custom parameter to #Query(), for example #Query('sort'). And then:
transform(value: any, metadata: ArgumentMetadata) {
const { type, data } = metadata;
// Make sure to only run your logic on queries when 'sort' is supplied
if (type === 'query' && data === 'sort') return this.transformQuery(value);
return value;
}

Resources