Auth0 provides express-openid-connect, which produces global middleware with routes. I'd like to make NestJS Middleware that wraps it.
I've created my middleware wrapper:
import { Inject, Injectable, NestMiddleware } from '#nestjs/common';
import { RequestHandler } from 'express';
import { auth } from 'express-openid-connect';
import { OidcOptions, OIDC_MODULE_OPTIONS } from './oidc';
#Injectable()
export class OidcMiddleware implements NestMiddleware {
private middleware: RequestHandler;
constructor(#Inject(OIDC_MODULE_OPTIONS) oidcOptions: OidcOptions) {
this.middleware = auth({
...oidcOptions.configParams
})
}
use(req: any, res: any, next: () => void) {
this.middleware(req, res, next);
}
}
and I apply it on the consumer:
#Module({
imports: [HttpModule],
controllers: [OidcController]
})
export class OidcModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(OidcMiddleware)
.forRoutes('*')
}
}
The problem is that the express middleware has routes, and they aren't being applied in the same way they are when I register the express middleware directly with the app in main.ts with the app.use(auth({...}) pattern. When registered from main.ts the /login and /callback are working. In both cases I've enabled DEBUG and confirmed that express-openid-connect middleware objects contains the routes; but when using the NestMiddleware pattern I think NestJS just returns 404 Not Found responses since the routes aren't registered with the framework.
I tried registering dummy routes on a dummy controller, but then inside of the use method the req has already been rewritten, and manually calling the pieces started feeling way too hacky.
What is the conventional method used by NestJS developers to wrap express middleware in a NestJS compatible module?
Related
I recently rewrote an older server in Nest.js that uses Parse-server (the firebase-like back-end as a service product facebook shut down). We want to be able to apply the CORS middleware to both our nest routes and our parse routes.
main.ts (simplified)
async function bootstrap() {
const app = NestFactory.create(AppModule)
app.enableCors({
origin: ['https://my-origin.com']
})
await app.listen(3000)
}
bootstrap()
app.module.ts (simplified)
#Module({/*module set up here*/})
export class AppModule implements NestModule {
constructor(private readonly config: ConfigService, private readonly parse: ParseService) {}
configure(consumer: MiddlewareConsumer) {
consumer.apply(this.parse.parseServer).forRoutes('/parse-path')
}
}
The CORS middleware gets applied to my nest routes, but not my parse routes. The parse routes are just normal middleware. What is the reason for the middleware being applied to the Nest routes but not the Parse server routes?
I'm experimenting with organizing my routes with Express router and classes in Typescript. This is what I've tried so far. The index.ts file is supposed to reference the Notes class in the notes.ts file which exposes an endpoint via a private method.
So, for example, I have a file index.ts
import * as express from "express";
import rootRoutes from "./root";
import { Notes } from "./notes";
import accountRoutes from "./account";
const router: express.Router = express.Router();
router.use("/", rootRoutes);
router.use("/account", accountRoutes);
router.use("/notes", Notes.routes);
export = router;
and another file notes.ts:
import express, { Router, Request, Response, NextFunction } from "express";
const router: express.Router = express.Router();
export class Notes {
constructor() {
this.routes();
}
private routes(): void {
//Get notes
router
.route("/")
.get((req: Request, res: Response, next: NextFunction) => {
res.send("notes");
});
}
}
What am I missing here?
You are calling a private method router.use("/notes", Notes.routes);
To use it like you did, first you have to instantiate the class or make the method static.
Mixing class instance state with router state can be tricky, try to keep it free of any instance state.
Also I would suggest you to simplify that implementation, to something like this:
export class Notes {
static handler(req: Request, res: Response): void {
res.send('Hello from A!')
}
}
app.get('/', Notes.handler);
Alright so I am trying to understand Node.js and Typescript, so I tried working on a simple script which is as follows;
app.ts
import * as express from "express";
import * as bodyParser from "body-parser";
import { Routes } from "./routes/crm_routes";
class App {
public app;
public routePrv : Routes = new Routes();
constructor() {
this.app = express();
this.routePrv.routes(this.app);
this.config();
}
private config():void {
this.app.use(bodyParser.json);
this.app.use(bodyParser.urlencoded({ extended: false }));
}
}
export default new App().app;
./lib/routes/crm_routes.ts
import {Request, Response} from "express";
export class Routes {
public routes(app): void {
app.route('/').get((req, res) => res.json({name : "ohmygodnotthisagain"}));
}
}
server.ts
import app from "./app";
app.listen(3000, () => console.log('Example app listening on port 3000!'));
Now I was playing around so I put this.config() above this.routePrv.routes(this.app), and my server stopped routing to / altogether.
When I put them back in the above order it started working again.
Now I tried to understand the cause of this, and am confused, is it because body-parser needs to be the last middleware called such that auth, extra checks, etc middleware complete working or are there something else?
Any and all help will be appreciated. Thanks!
PS: I'm pretty new to TS, pointers would be great.
Body parser (or the middleware in general) should be called before the actual route.
Your route is not working because you have a spelling error here:
this.app.use(bodyParser.json);
Should be:
this.app.use(bodyParser.json());
The route works when you put that code last, because its never actually executed (the route gets matched first and stops the execution, since you are not calling the next() function)
How does nestjs get the cookie in the request?
import { Get, Controller, Response, Request } from '#nestjs/common';
import { AppService } from './app.service';
const l = console.log
#Controller()
export class AppController {
#Get('json')
json(#Request() req){
console.log(req.cookies) // undefined
}
}
You have to install cookie-parser middleware.
$ npm install --save cookie-parser
once the installation process is completed, simply bind middleware to your application:
const app = await NestFactory.create(ApplicationModule);
app.use(cookieParser());
read more here: https://expressjs.com/en/resources/middleware/cookie-parser.html
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import * as cookieParser from 'cookie-parser'
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(cookieParser());
await app.listen(5000);
}
bootstrap();
For everyone, who is looking at this question in 2022.
You can find it in the docs:
https://docs.nestjs.com/techniques/cookies
The other answers don't work, if you use Typescript.
You need to run (according to docs):
$ npm i cookie-parser
$ npm i -D #types/cookie-parser
I am trying to send a file to a NestJS controller but keep getting a too many parameters exception. I have installed bodyParser and updated the request size limit to get around a request too large exception.
main.ts:
import { NestFactory } from "#nestjs/core";
import { ApplicationModule } from "./app/app.module";
import * as express from "express";
import * as bodyParser from "body-parser";
async function bootstrap() {
const server = express();
server.use(bodyParser({limit: '50mb'}));
console.log(server);
const app = await NestFactory.create(ApplicationModule, server);
await app.listen(process.env.PORT || 3000);
}
bootstrap();
Controller:
import { Get, Controller, Query, Post, Request } from "#nestjs/common";
import { CloudVisionLogoService } from "./logos.component";
#Controller("logos")
export class LogoRecognitionController {
public constructor(
private readonly _logoRecognition: CloudVisionLogoService
) {
}
#Post()
public async detectLogos(#Request() req) {
console.log(req.files[0]);
// return await this._logoRecognition.detectLogos(imageUri);
}
}
Postman request (not shown, binary attachment of image):
POST /logos HTTP/1.1
Host: localhost:3000
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: c95da069-c602-58a9-1e05-36456a527f02
undefined
The following is from body-parser docs:
This does not handle multipart bodies, due to their complex and
typically large nature. For multipart bodies, you may be interested in
the following modules:
busboy and connect-busboy
multiparty and connect-multiparty
formidable
multer
I suggest you to use multer package because it's easy and many users use it