im trying to add graphql to nestjs app. I use postgraphile for the graphql server.
everything works, but i don't know how to protect the '/graphql' endpoint.
the login is done via session, through the nestjs app.
on the needed endpoints (in the existed nestjs app) there is a Session guard which works fine.
i want to add the same behavior to the graphql endpoint.
this is the graphql code:
export const graphqlBootstrap = async (app: INestApplication) => {
app.use(cors());
// what i want to achieve
app.use('/graphql', (req, res, next) => {
if (!req.session.user) res.send('Authetication Error`);
next();
})
app.use(
postgraphile(...)
and the nestjs code the init it:
const bootstrap = async () => {
const { app, start } = await createApplication(TearupsApplicationModule);
app.useGlobalInterceptors(new ApiResponseInterceptor());
configureHiddenFileUploadEndpoint(app.getHttpAdapter());
await Promise.all(app.get(INITIATION_SEQUENCE).map((fn) => fn()));
await graphqlBootstrap(app); // <--
await start();
app.get(EventsEmitter).init(app);
};
is it possible to use the session in a middleware?
I don't know how to reach the ExecutionContext that exist in the Guard.
I read in the docs that there is also ArgumentsHost which might hold the session, but i also don't know how to get it in simple middleware(app.use(...))
Edit 1
i tried to add a middleware to the app module, with forRoutes call.
but the middleware isn't called with the graphql route /graphql.
also, when using route: / - there is no session on the request.
middleware:
export function Logger(req: any, res: Response, next: NextFunction) {
if (req.session) console.log(`Request...`, req.session.user);
next();
}
App Module:
export class MyApp implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(Logger).forRoutes('/');
}
Edit 2
tried this now, still didn't work:
export class MyApp implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(logger)
.forRoutes({ path: 'api/tearups/graphql', method: RequestMethod.ALL });
}
}
Write a functional middleware as follow signature:
import { Request, Response, NextFunction } from 'express';
export function Logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`, req.session.user);
next();
};
Or, a class-based middleware as:
#Injectable()
export class Logger implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...', req.session.user);
next();
}
}
Now apply the middleware as follows:
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(Logger)
.forRoutes(CatsController);
}
}
Related
I'm quite new to NestJS, so if I overlook something obvious, please forgive me.
Now I'm implementing a simple logger which logs request and response.
In NestJS, you can put a middleware only before routing, so my middleware overwrite res.write and res.end so that response chunks will be pushed to an array in the middleware.
export class Logger implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const originalResWrite = res.write;
const originalResEnd = res.end;
const chunks = [];
res.write = (...chunk) => {
chunks.push(Buffer.from(chunk[0]));
originalResWrite.apply(res, chunk);
};
res.end = (...chunk) => {
if (chunk[0]) {
chunks.push(Buffer.from(chunk[0]));
}
const body = Buffer.concat(chunks).toString("utf8");
console.log({
requestBody: req.body,
responseBody: JSON.parse(body) || body || {},
});
originalResEnd.apply(res, chunk);
};
}
}
However, if this middleware is instantiated as a singleton and shared by all requests--like Django middleware--, chunks Array will receive chunks from several streams, and the log will be totally messed up.
So, the problem is, how comsumer.apply instantiate a midddleware.
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(Logger)
.forRoutes('*');
}
}
In NestJS, is a middleware instantiated as a singleton, or instantiated each time request comes?
If you would answer my question, I would appreciate it.
Yes, by default providers are singleton in Nestjs, however, you can define the scope for your middleware using the options in the Injectable decorator.
So you can add this before your middleware class definition
#Injectable({ scope: Scope.REQUEST })
Check out this link in the documentation and this answer on Stackoverflow.
I have the following middleware to log all http requests:
#Injectable()
export class RequestMiddleware implements NestMiddleware {
constructor(
#InjectRepository(Request) private requestsRepository: Repository<Request>,
) {}
private readonly logger = new Logger('HTTP');
use(request: Request, response: Response, next: NextFunction) {
response.on('finish', () => {
const { method, originalUrl } = request;
const { statusCode, statusMessage } = response;
const message = `${method} ${originalUrl} ${statusCode} ${statusMessage}`;
return this.logger.log(message);
});
next();
}
}
My goal is to log all requests to a database. I am using TypeORM so I would like to inject the Request repository and log each request that way. When I do this I receive the following error:
Error: Nest can't resolve dependencies of the class RequestMiddleware
The issue is that this middleware isn't part of a module, it's just a single typescript file so how do I import the TypeORM repo module into a normal typescript file so I can use it?
In the module where RequestMiddleware is defined and used, TypeormModule.forFeature([Request]) needs to be added to the imports array
I am new to tsoa and I want to do CSRF implementation in my node app. I have been able to make api using app.use() but I want to write in tsoa. Is there any way?
In the pre-released version, you can use the #Middlewares() decorator.
Just put what you had in a app.use() to the #Middlewares() decorator.
You can define your Middleware / Middlewares like this:
import { Request, Response, NextFunction } from 'express';
// ... controller logic ...
// #Get('/endpoint') | #Post('/endpoint') etc.
#Middlewares([
(req: Request, res: Response, next: NextFunction) => {
console.log(req.headers);
next();
},
(req: Request, res: Response, next: NextFunction) => {
console.log('Second middleware, but we can also use only one!');
next();
},
])
// getEndpoint(): string {
// return 'Hello World!';
// }
// ... controller logic ...
Also remember to have set the experimentalDecorators to true in your tsconfig.json.1
1 https://github.com/lukeautry/tsoa/pull/1123#issuecomment-1018251162
I have a system writed in using NestJs and serverless framework were each endpoint is a lambda function on aws. One of the functions is not an endpoint, but a trigger from AWS eventbridge. As this function is not an endpoint it cannot be included on a NestJs module since it have to be exported separatelly. My problem is that when the event comes to Eventbridge and triggers the lambda I have to call a NestJs service but I'm not able to do this, since the lambda function is outside NestJs environment. Is that a way for me to call a NestJs service from outside the module?
Here is the serverless framework configs
functions:
function 1(NestJs controller):
handler: src/lambda.handler
events:
- http:
cors: true
method: post
path: entrypoint for function 1
Function 2 (External from NestJs modules):
handler: path to lambda function
events:
- eventBridge:
eventBus: eventbus name
pattern:
source:
- source
Currently I'm using axios to call another NestJs endpoint to just pass the received payload. As you can see on the lambda function file:
import { Context, Handler } from 'aws-lambda'
import axios from 'axios'
export const handler: Handler = async (event: any, context: Context) => {
return await axios
.post(
'lambda function production url',
event.detail
)
.then((data) => {
console.log('data', data)
return data
})
.catch((error) => {
console.log('error', error)
return error
})
}
Here is the controller of lambda function 1
import { Body, Controller, Post } from '#nestjs/common'
import { MyService } from './enrichment.service'
#Controller('function1')
export class EnrichmentController {
constructor(private readonly myService: MyService) {}
#Post('entrypoint')
sendForm(#Body() body) {
return this.myService.start(body)
}
}
and here is the service
import { forwardRef, Inject, Injectable } from '#nestjs/common'
import { EventbridgeService } from '../eventbridge/eventbridge.service'
import { CampaignsService } from '../campaigns/campaigns.service'
import { UploadedDataService } from '../uploaded-data/uploaded-data.service'
#Injectable()
export class MyService {
constructor(
private readonly anotherService: AnotherService,
) {}
async start(body) {
return this.anotherService.updateData(body)
}
}
The question is: Is that a way to call all this NestJs structure from the function file, since it is outside NestJs modules and since the trigger for this function is not an http request but a trigger from Eventbridge? Thank you so much.
You can use a "Standalone" Nest application and pass the event data directly to MyService
You can use NEstJs standalone app, and make your handler like this
export const checkDeletion: Handler = async (event: any, context: Context) => {
async function bootstrap() {
const app = await NestFactory.createApplicationContext(AppModule);
await app
.select(SchedulerModule)
.get(SchedulerService, { strict: true })
.runScheduler();
}
await bootstrap();
};
After that call your handler from serverless.yaml like
functions:
followup-emails:
environment:
STAGE: ${opt:stage}
name: followup-emails-${opt:stage}
handler: src/lambda.checkDeletion
events:
- schedule: rate(1 day)
I want to update a recored, and the code is OK to update record. I am not getting any syntax error.
Issue: When I update a record the particular record which I want to update is not updating. It updates the record which display on top.
Maybe I am not sending a the right request.
RecordRouter.ts
import { Router } from 'express';
import {RecordComponent} from '../../components';
const router: Router = Router();
router.put('/:id', RecordComponent.update);
export default router;
IRepository.ts
export interface IRepository<T> {
put:(T: any)=>Promise<T>;
}
In my RecordComponent.ts
export async function update(req: Request, res: Response, next: NextFunction): Promise <void> {
try {
const record: IRecord = await RecordService.put(req.body)
res.status(201).json(record);
} catch (error) {
next(new HttpError(error.message.status, error.message));
}
}
I am using Node.js Express API with TypeScript 3 module for this project.