Two endpoints for same controller (route aliases) in NestJS - node.js

I want to change an entity name from Person to Individual. I want to keep the old /person endpoint (for temporary backward compatibility) and add a new /individual endpoint.
What would be the easiest way to do it in Node.js using Nest?
I can copy the code but I'm hoping for a better solution that won't require duplication

The #Controller() decorator accepts an array of prefix, thus you can use like this:
import { Controller, Get } from '#nestjs/common';
#Controller(['person', 'individual'])
export class IndividualController {
#Get()
findAll(): { /* ... */ }
}
to me this is the easiest way.
source

In NestJS, We can have multiple routes for the whole controller or for a single route. This is supported for all HTTP methods (POST, GET, PATCH etc., )
#Controller(['route-1', 'route-2'])
export class IndividualController {
#Get(['/sub-route-1','/sub-route-2'])
public async getSomething(...){...}
All HTTP methods support either a single string route or an array of string routes. We could use this technique to deprecate a bad route and start introducing a better route without breaking the consumers immediately.

if you mean expressjs instead of jestjs (which is a testing framework), my approach would be the following:
simply exclude your controller code into a function and pass it to your routes.
// your controller code
const doSomethingWithPersonEntity = (req, res, next) => {
res.status(200).json(persons);
}
router.get("/person", doSomethingWithPersonEntity);
router.get("/individual", doSomethingWithPersonEntity);

Related

Is there an easy way to keep query params on redirects in NestJS?

I am pretty new to this kind of stuff so any advice is appreciated, especially if there's a better way to solve this than what I am looking for.
I am trying to support an old endpoint by redirecting it to a new endpoint, and both endpoints should support query parameters. Each endpoint lives in a separate controller.
Example:
I want /old-namespace/getStuff?foo=bar to redirect to /new-namespace/getStuff?foo=bar without manually rebuilding a query string like the Nest docs point out, because the params can be dynamic.
Looking at the NestJS docs, there is a handy #Redirect decorator that you can use on an endpoint like so, to easily redirect the request to a different URL:
#Get('docs')
#Redirect('https://docs.nestjs.com', 302)
getDocs(#Query('version') version) {
if (version && version === '5') {
return { url: 'https://docs.nestjs.com/v5/' };
}
}
However, when using this, request.query is cleared on redirect (pretty sure this is expected behavior). Does anyone have a minimally-invasive solution for this? I've tested out building middleware/interceptors/custom decorators to get around this with varying degrees of success, but that seems heavy-handed and I wish there was a way to throw an extra param in the #Redirect decorator like retainQuery = true or something.
I ended up using the top answer here: How to rewrite url path in Nestjs?
Ambroise Rabier's first response on nginx rewrites cued me in to search for a simple middleware solution since I didn't want to inject a service in multiple places, as there will be quite a few small redirects from old namespaces to new ones.
In a common module:
consumer
.apply(RewriteApiEndpointMiddleware)
.forRoutes('/')
In a middleware:
import { Injectable, NestMiddleware } from '#nestjs/common';
#Injectable()
export class RewriteApiEndpointMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
// the linked answer replaced "api" but in my case I needed to send "app" requests somewhere else
req.url = req.url.replace(/^\/app/, '/new-namespace');
next();
}
}
What you need is called rewrite in nginx.
I don't know if it work, but maybe you can do that ?
#Get('/old-namespace/getStuff')
getStuff(#Query() query) {
// Done !
getNewStuff(query);
}
#Get('/new-namespace/getStuff')
getNewStuff(#Query() query) {
}
If not, you can always do something like:
function handleGetNewStuff() {
}
#Get('/old-namespace/getStuff')
getStuff(#Query() query) {
handleGetNewStuff(query);
}
#Get('/new-namespace/getStuff')
getNewStuff(#Query() query) {
handleGetNewStuff(query);
}
However, if your issue is about changing the link on the user side (and not just for internal routing), you can try (not tested either):
#Get('/old-namespace/getStuff')
getStuff(#Req() request, #Res() response) {
const newURL = req.originalUrl.replace('old', 'new');
response.redirect(newURL);
}
Remember that NestJS use expressJS bellow.

Annotating an annonymus middleware in Newrelic in a NestJS app

I'm using NestJS (with Express Server) for a project and trying to optimize the performance on some of the endpoints, using New Relic I noticed that a big chunk of the response time of all endpoints is spent in an anonymous middleware, reaching 89% at some points.
Is there a way to find out which middleware is this?
I've made an answer to this in another question, but I figured I should go into a bit more depth here about what's going on under the hood of Nest.
A route handler in Nest is technically a middleware in express. I.e.
#Controller('test')
export class TestController {
#Get()
testGet() {
return 'do the thing';
}
}
Get's transformed under the hood (through some really awesome code) to
app.get('test', (req, res, next) => {
res.send('do the thing');
})
Now when we add on a filter
#Controller('test')
export class TestController {
#Get()
#UseFilter(SomeExceptionFilter)
testGet() {
return 'do the thing';
}
}
More magic happens and we get this
app.get('test', (req, res, next) => {
let returnVal = '';
try {
returnVal = controller.testGet();
} catch (err) {
returnVal = someExceptionFilterInstnace.catch(err, customArgumentHostThatNestMaintains);
}
res.send(returnVal);
})
(Disclaimer: this isn't a perfect 1:1 representation of what actually happens, as there's a lot going on, but this should get the idea across that it's all middleware in the end)
This follows too as you add in services, they're just method calls inside of the express/fastify middleware, and so they don't get broken out in a nice way in newrelic.
Unfortunately, I don't know of a good way to help with this in newrelic or how to add better annotations. Looking at the timing of the middleware, you've got 218 ms, which isn't too bad. Most likely a database operation somewhere, so I'd check your pipes, guards, and interceptors, along with your service, and see if you have any possibly large queries. If nothing else, you can do some profiling locally and get a bit more raw information about the time being taken

Access request object within validation pipe

I am trying to access to Request object from within a Validation Pipe in nestjs
In order to verify uniqueness of certain fields, I require the ID/UUID parameters supplied with PUT/PATCH request (not available in the data structure itself)
any idea?
Currently, it is not possible to access the request object at all in a pipe. If you need the request you can use a guard or an interceptor.
If you are working on verifying uniqueness, that sounds like a part of business logic more than just about anything else, so I would put it in a service and handle the query to the database there. Just my two cents.
Edit 11/17/2020
After learning way more about how the framework works as a whole, technically it is possible to get the entire request object in a pipe. There are two ways to go about it.
Make the pipe #Injectable({ scope: Scope.REQUEST }) so that it is request scoped. This will end up creating a new pipe on each request, but hey, if that's your cup of tea then great.
Make a custom parameter decorator as custom decorators get completely passed into pipes as they are. Do note, that this could impact how the ValidationPipe is functioning if that is bound globally, at the class, or method level.
We can create a Pipe and access request object. We can move further and update the Body as well, if needed.
Following is an example scenario, where createdBy field should be added to the Body dynamically. Let's say user details are available from request:
// user.pipe.ts
import { Request } from 'express'
import { REQUEST } from '#nestjs/core'
import { Injectable, Inject, Scope, PipeTransform } from '#nestjs/common'
#Injectable({ scope: Scope.REQUEST })
export class UserPipe implements PipeTransform<any> {
constructor(#Inject(REQUEST) protected readonly request: Request) {}
transform(value) {
let email = this.request["user"].email;
value['createdBy'] = email;
return value
}
}
We can now use this in controller like this:
// someentity.controller.ts
#Post()
public async create(
#Body(SetUserPipe) dto: SomeEntityDto,
): Promise<SomeEntity> {
....
}

Is it ok to use controller and graphql resolver together in nestJs?

For my next project I would like to use Graphql inside the FrontEnd. Furthermore this project should also offer a Rest-Api.
Now I have discovered this extremely great framework "nestjs", where it is theoretically possible to combine a Graphql endpoint and a rest endpoint.
Unfortunately I can't find anything in the documentation if this can lead to problems. Is the following code usable without problems?
Artikel controller:
#Controller('article')
#Resolver('Article')
export class ArticleController {
constructor(private articleService: ArticleService){}
#Get()
#Query(returns => CArticle)
async Article() {
const dbElement=await this.articleService.getById("xy");
return dbElement;
}
}
Article module:
#Module({
controllers:[ArticleController],
providers:[ArticleService,ArticleController]
})
export class ArticleModule {}
As this would work in this specific example, it may not work well in other use cases you may reach.
In my experience with using both Rest and gRPC - there are eventually different things that the controllers need to take care of.
I would highly recommend having dedicated controllers/resolvers for each API - with these taking care of API entry (i.e. authenticating, taking care of context) and having the Business Logic in a separate Provider.
So your example would look like so:
Article.controller.ts:
#Controller('article')
export class ArticleController {
constructor(private articleService: ArticleService){}
#Get()
async Article() {
return this.articleService.getById("xy");
}
}
Article.resolver.ts:
#Resolver('Article')
export class ArticleResolver {
constructor(private articleService: ArticleService){}
#Query(returns => CArticle)
async Article() {
return this.articleService.getById("xy");
}
}
Article.module.ts:
#Module({
controllers:[ArticleController, ArticleResolver],
providers:[ArticleService]
})
export class ArticleModule {}
As I understand nestjs now, there should be no problems, as the decorators do not change the code but only create new code.

Should my controller take the (res, req) params or already extracted params?

I wonder what way should I organize my routing in expressJS :
Params parsing in Controller
router.get('/users/:id', UserController.get);
class UserController {
get(res, req) {
var id = res.params.id;
UserModel.get(id, function(user) {
res.send(user);
}
}
}
Params parsing in Route
router.get('/users/:id', function(req, res) {
var id = req.params.id;
UserController.get(id, function(user) {
res.json(user);
}
});
class UserController {
get(id, fn) {
UserModel.get(id, fn);
}
}
I find the second version Params parsing in Route easier for
unit test
In case of change in the URL params or request body
but most of the example I found use the first version, why ?
If you consider a much larger, messier real world application, with route names that no longer match controller names etc., it might be beneficial to place the full routing table (all of the router.xxx calls) in one place, such as a routes.js. For a given url, this makes it much simpler for a new developer to figure out which code handles which url.
If you included all of the parameter parsing in your routes.js, it would become really messy and you'd likely lose some of the benefit of having collected all that into one file in the first place.
That said, there's no reason why you cant have the best of both worlds by separating the routing, the parameter parsing/response formatting, and the controller logic each into their own modules.

Resources