How to access Response object in NestJS GraphQL resolver - node.js

How can I access pass #Res() into my graphql resolvers?
this doesn't work:
#Mutation(() => String)
login(#Args('loginInput') loginInput: LoginInput, #Res() res: Response) {
return this.authService.login(loginInput, res);
}

#Res() is for HTTP Requests. To access to res object you'd need to first make sure it is added to the context for graphql by using context: ({ req, res }) => ({ req, res }) in the GraphqlModule's options, and then you can use #Context() ctx to get the context and ctx.res to get the response object

Related

Add additional functionality to end method on Node/Express Response object?

My team and I are trying to mutate the response.end method in our Express middleware in order to have extra functionality be called just before the server responds back to the client.
Here is our attempt:
return (req: Request, res: Response, next: NextFunction): NextFunction => {
// reassign res.end in order to allow logger functionality before
// a response is sent back the client
const temp = res.end;
res.end = () => {
// instantiates PostQuery object with passed in query data from limiter middleware
const postQuery = new PostQuery(gateURI, projectID, res.locals.graphqlGate);
// our logger middleware functionality
try {
await postQuery.post();
} catch (err) {
if (err) console.log(err);
}
// our temp variable holding node's res.end definition
return temp.call(this);
};
return next();
};
Our test server throws this error when we include this function in our middleware chain:
TypeError: Cannot read properties of undefined (reading 'finished')
at end (node:_http_outgoing:856:19)
at /Users/jon/Documents/Solo Projects/OSP/graphQL-gate-logger/src/index.ts:65:25
index.ts:65 points to return temp.call(this)
We have also tried return temp() , as well as binding temp to the res object, and receive the same error in every instance.
Is there some other way we can reach this goal or do we have to start back at the drawing board?
If you don't have to execute your code BEFORE the response has been sent, but can instead do it right afterwards, then you can use the finish event on the res stream.
app.use((req, res, next) => {
res.on('finish', () => {
console.log(`got finish event for ${req.url}`);
// do your business here after a response has been sent
});
next();
});
There are also a couple problems with your existing override middleware. First off, you aren't preserving arguments that can be optionally send to res.end(). Second, res.end() is supposed to return res which makes it chainable. You aren't doing that. You have assigned it an async function which returns a promise, not res.
Though I think it would be much better to use the finish event as illustrated above and not have to override any methods, this would fix some of the problems with your override:
return (req: Request, res: Response, next: NextFunction): NextFunction => {
// reassign res.end in order to allow logger functionality before
// a response is sent back the client
const origEnd = res.end;
res.end = function(...args) {
// instantiates PostQuery object with passed in query data from limiter middleware
const postQuery = new PostQuery(gateURI, projectID, res.locals.graphqlGate);
// our logger middleware functionality
postQuery.post().catch(err => {
console.log(err);
}).finally(() => {
return origEnd.call(this, ...args);
});
return res;
};
return next();
};

How to clone Request in nestjs guard?

I need to get request params from form-data request in nest guard,so I use multer in my guard
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const postMulterRequest: any = await new Promise((resolve, reject) => {
multer().any()(request, {}, function(err) {
if (err) reject(err);
resolve(request);
});
});
}
and I also need to get files and request data in controller
#UseInterceptors(FileInterceptor('filedata'))
async UploadedFile(#UploadedFile() file, #Body() body, #Request() req): Promise<ImgInfo> {}
when I did not use multer, it works. But after using multer, it performs:
file: undefined, body:{}
I can get file from the req but can not get other params
I want to know how can I get the from form-data request in guard without modifying the original request?
reference:https://stackoverflow.com/questions/59294070/nestjs-receive-form-data-in-guards/61407264#61407264

NestJS: My controller doesn't send the response

I had a controller that didn't send the response back.
#Controller('/foo')
export class FooController {
#Post()
public async getBar(#Body() body: DTO, #Res() res) {
const response = await this.doSomething(body);
return response;
}
}
I had to use the res.send method:
#Controller('/foo')
export class FooController {
#Post()
public async getBar(#Body() body: DTO, #Res() res) {
const response = await this.doSomething(body);
res.send(response);
}
}
The cause was the #Res() res parameter. If you delete it, the response will be sent correctly:
#Controller('/foo')
export class FooController {
#Post()
public async getBar(#Body() body: DTO) {
const response = await this.doSomething(body);
return response;
}
}
You have to use #Res({ passthrough: true }) if you want the response to be send using the Nest way.
If you want to send the response like on Express framework use #Res() and add code res.status(200).send()
https://docs.nestjs.com/controllers
WARNING Nest detects when the handler is using either #Res() or
#Next(), indicating you have chosen the library-specific option. If
both approaches are used at the same time, the Standard approach is
automatically disabled for this single route and will no longer work
as expected. To use both approaches at the same time (for example, by
injecting the response object to only set cookies/headers but still
leave the rest to the framework), you must set the passthrough option
to true in the #Res({ passthrough: true }) decorator.

Request and Response Lifecyle in Node and Express

I have a very simple route setup for my web app,
router.get('/myTestRoute/:id', async (req: Request, res: Response, next: NextFunction) => {
await doSomeAsync(req, res);
res.json({ myObj: 32 });
return next();
}
The doSomeAsync actually performs a request to a service and takes up to 20 seconds. However the response that the user is looking for doesn't need or care about the doSomeAsync response, so I thought I could remove the await, so that it would look like this:
router.get('/myTestRoute/:id', async (req: Request, res: Response, next: NextFunction) => {
doSomeAsync(req, res);
res.json({ myObj: 32 });
return next();
}
Inside the doSomeAsync function, after the service call, we reference parameters on the req and res objects, like the parameters on the req object, and some authentication stuff on the res object.
I have found that this is not working, and I realized that I don't fully understand what happens with req and res after the response has been sent back to the user.
After I call res.json(), and then next(), what happens to the Request and Response objects, do they reset? Do they change in any way?
Should I refactor doSomeAsync() to accept the primitive values from req.params?

Wrapper for async handlers in Express with custom request properties

As demonstrated on https://strongloop.github.io/strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/#using-es7-asyncawait, I wanted to use a wrapper for all my async Express handlers to catch any errors happening there, but in a typed version for TypeScript.
I came up with this: (wrap.ts)
import { NextFunction, RequestHandler, Response, Request } from 'express';
type AsyncRequestHandler = (req: Request, res: Response, next: NextFunction) => Promise<any>;
/**
* Catches errors and passes them to the next callback
* #param handler Async express request handler/middleware potentially throwing errors
* #returns Async express request handler with error handling
*/
export default (handler: AsyncRequestHandler): RequestHandler => {
return (req, res, next) => {
return handler(req, res, next).catch(next);
};
};
I want to build a REST API with some endpoints like PUT /users/:userId and DELETE /users/:userId. For convenience, I don't want to query the specific user with the ID userId from the database in every handler and instead store it in req using a middleware. That means I have to use a modified Request interface for the handler definition adding a user property, e.g. UserRequest.
import express, { Request } from 'express';
import wrap from './wrap';
const app = express();
app.use('/users/:userId', wrap(async (req, res, next) => {
// set req.user ...
}));
export interface UserRequest extends Request {
user: User;
}
app.put('/users/:userId', wrap(async (req: UserRequest, res) => {
// do something with req.user ...
}));
// ...
This would be possible when not using wrap, but not with this type definition of wrap. The TypeScript compiler produces the following error:
Argument of type '(req: UserRequest, res: Response) => Promise<void>' is not assignable to parameter of type 'AsyncRequestHandler'.
Types of parameters 'req' and 'req' are incompatible.
Type 'Request' is not assignable to type 'UserRequest'.
Property 'user' is missing in type 'Request'.
What is the "TypeScript way" to accomplish this?
I somehow didn't realize that the issue appears without wrap as well (as long as the strict compiler option is enabled). My solution was to extend the express.Request interface.

Resources