Let's say we make the following GET API call to our REST API running with NestJS (and fastify):
http://localhost:5000/api/test?arrayParam=["abc","def"]&anotherParam="value"
Without parsing anything, the query object on the backend looks like this:
{
arrayParam: '["abc","def"]',
anotherParam: '"value"'
}
We can see that parameter values are strings, but in the case of arrayParam we would obviously like to work with the actual array.
I come from an expressJS background, and coming from there, there are a couple of approaches. First would be just using a JSON parser middleware, like body-parser. Or just using JSON.parse().
But what is the "proper", NestJS approach? I thought about using type decorators defined in a DTO, and assumed they would be automatically parsed to the type that I defined. But that doesn't work like I assumed it would.
I defined it like this:
#IsOptional()
#IsArray()
arrayParam?: string[];
But validation fails, since arrayParam is a string and not an array. So I assume this is not the correct approach
You are sending it incorrectly
http://localhost:5000/api/test?arrayParam[]=abc&arrayParam[]=def&anotherParam
Related
I'm pretty new to typescript and I faced the issue with extending the Request type. I actually found a solution, but it doesn't feel right to me, and seems like there might be a better way.
First, the structure. I have a middleware checkAuth that checks the Bearer token, finds the user in the database (or creates one, in case it's the first time) and adds user to the req variable.
Most people recommend modifying the Express' Request type with custom declarations. I don't like this idea, because that will put user object into all requests, even before I actually save user into req.
Another solution is what I use now:
interface ReqWithUser extends Request {
user?: {
...
}
}
This allows me to declare req: ReqWithUser. But there's a flaw. When I try to access req.user, typescript is telling me that it's possibly undefined. Well, that's exactly what I declared with user?:. If I don't put a question mark there, then typescript is mad in router, saying Property 'user' is missing in type Request. That's fair. One of the solution is to use req!.user. Solves all problems, but it still feels imperfect to me. I know the req.user is there, it's not optional, otherwise checkAuth would fail and return 401. If it didn't, the user is there. What feels like the right solution is somehow modify the req type after the checkAuth middleware. Because that's when user is added into the req. Is it possible?
The issue has nothing to do with your types, but it's the fact that Express will always emit Request and not RequestWithUser.
So in your middleware function, you will always get a Request. The only reason RequestWithUser is acceptable is because you made it optional.
Truth is the middleware / decorator pattern doesn't work that well with express. You have a few options (some of which you mentioned)
Use declaration merging to 'patch' the built-in Request. You already mentioned you don't like this though, because it's a bit of a hack.
Don't use Express' middleware system and instead write something that understands how types change with decorators. I don't know exactly what this looks like, or if this has been done before.
Whenever you want to use RequestWithUser in your controllers/middlewares start off with an assertion function to ensure that user exists (allowing you to make it non-optional).
Cast to RequestWithUser, when you need it.
All these options have drawbacks:
You don't like for the reasons mentioned. It's not always 'accurate' so you are sort of lying to Typescript for convenience.
Sounds hard to do
Requires the use of an assertion function, which means you need to do a little extra work every time you want to use the user property. This work is not needed because you as developer know.
You probably don't like casting for the same reason you don't like to use declaration merging.
By far I think 2 is the best solution, because it gives you the advantage of typing without any hacks and without having to do the extra work of an assertion function (which happens during runtime). But it means abandoning Express' Middleware system. I wouldn't know exactly how to write this code, but I am curious how if it can be done...
FYI I opened another stack overflow question, because I am curious:
Typescript typing for decorator middleware patterns
I read the doc, been there, done that. Still no clue how to write a decorator I need in a way that makes common sense.
In brief: Got an interceptor that executes before the validation layer. That simply means that invalid data can get in the interceptor and break the app. To avoid that I would like to use a decorator on some methods, and to be more accurate on parameters of such methods.
public async getUserById(#IsIntNumber() userId: number): Promise<UserEntity>
{
// method logic
}
Here #IsIntNumber() is a custom decorator that validates the userId parameter.
As a matter of fact I'd like to have a little library of mine in the application holding a bunch a that kinds of validation decorators that I could apply to different parameters.
Is there some legal method to do this without shedding too much blood and too many tears?
I know it's a difficult question.
In the docs they sort of say:
The #required decorator adds a metadata entry that marks the parameter
as required. The #validate decorator then wraps the existing greet
method in a function that validates the arguments before invoking the
original method.
Meaning I've got to pack all my validation logic into that validate function or what? Really?
Does it mean that we don't have adequate parameter decorators in TS? Cos if I understand this right, these ones are absolutely, totally unusable.
So, I am working on developing a CRUD server in nestjs, using Mongo as the database, and I am trying to inject the database connection that can be obtained from the service constructor (#InjectConnection() private connection?: mongoose.Connection), into a decorator by doing #Decorator(this.connection). This is not possible because the decorator factory runs before the connection to the database has been initialised. With this being, every time that the decorator is used, the connection is undefined. Is there a workaround for this kind of situation? I really wan't to implement the solution using typescript decorators.
decorators in typescript has 3 arguments. target which is the prototype of the class, key which is the key that you are applying the decorator to and third descriptors which is to change the value of the key. essentially when you type #Decorator, typescripts sees it as a function. this is how you defined your decorator.
#Decorator=function Decorator(target:any,key:string,desc:PropertyDescriptor){}
now you want to write it like this
#Decorator()
that means you have add additional () to the right side as well. this is simple maths.
#Decorator()=function Decorator()(target:any,key:string,desc:PropertyDescriptor){}
what you have to do is you have define decorator as the function which returns a function with those parameters.
function Decorator(...args){
return function(target:any,key:string,desc:PropertyDescriptor){
// you can access to ...args here
}}
In my NestJS app, I'm making a REST request to a remote API I do not have control over. The REST API has a response containing JSON, a large object, most of which I do not need. Let's assume hypothetically that we have a JSON object that looks like the following:
{
"foo": [
1,
2,
3
],
"bar": {
"nested": {
"some_key": "some_val"
}
}
}
What if in this case, after I make a request to this API, I want to pass around only a subset of the above. Something like a NestedDto that would look like the following:
import { IsNotEmpty, IsString } from 'class-validator'
export class NestedDto {
#IsNotEmpty()
#IsString()
someKey: string
}
What is the best way for me to take the data that's being returned from the REST API and transform it into the above-using tools that NestJS offers? I want to be able to take responses from remote APIs and pass said data around inside of my NestJS app using my interface specifications.
All right, if you're just using Axios with no special configuration, you could do what you are already doping in Express by just mapping the response down to what you want. If you want to get a little fancy with it you could always implement some sort of class decorated with class-transformer decorators and use a plainToClass method mixed with the #Transform() decorator.
By the way, by default, NestJS provides an HttpModule that is a wrapper around Axios, but its responses are as RxJS Observables. If you decide to go with the observable route, you can use the observable operator map to do the mapping for you (you'll still have to provide mapping implementation though, like the plainToClass I mentioned above), but that is completely up to you.
VSC like VS gives out syntax/signature hint. I understand : means data type
myText: string // : means datatype of myText is string
myStuff: any // any means can be any data type.
sometimes hard to guess what the operators mean, for example the Node's request(),
my understanding is
const request means I can define any variable like const x=request(...) or var x=request(...).
request.RequestAPI means it's an API call.
options: defines this parameter is a typical object-like options in form of {...}
(request.UriOptions & request.CoreOptions) I understand the beginning and end parts, they must be enum of Uri and Core, but what is &? Does it mean I need to supply both Uri AND Core?
| does this pipe mean OR? If it is then it's duplicating the part before the pipe.
callback?: request.RequestCallback, so here I must provide a callback which will be typed (or functioning) as RequestCallback, but what is ?:?
Is there any document for these conventions?
I wanted to comment, because I don't know the complete answer, but here is some helpful information:
You are probably seeing this definition of DefinitelyTyped: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/request/index.d.ts#L29
Have a look at this to understand the definition file syntax: http://www.typescriptlang.org/docs/handbook/declaration-files/by-example.html
And you can interpret the definition like this:
const request: there is a constant named request that implements the interface request.RequestAPI (which is also callable directly and then for that) takes arguments options of type (request.UriOptions & request.CoreOptions) | (request.UrlOptions & request.CoreOptions) and an optional parameter callback (hence the ? of type request.RequestCallback. The function returns a request.Request.
& usually mean and
A pipe | usually means or, there is no duplication URI vs URL
=> "returns"
You see request in front of everything because it's the namespace (my wording may be off here)
The definitions of UriOptions, UrlOptions, CoreOptions are buried a bit. I'm not a node user, so I don't know what you can pass to request.
For example UrlOptions can either be a string argument named "url" or a url (from require('url')). See https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/request/index.d.ts#L162