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);
Related
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?
I'm having a problem during the initialization of a system. In order to initialize this system, I'm calling the index.ts file. This file imports an initializer class called StartServer. This class has an async method called start that initialize all the system database connections and express server. The problem is, somehow Node initialize express files, index.routes.ts, where the routes are defined, and all it dependencies, before starting the index file with the initializer class, generating an error of undefined due to no connections exits at this point.
index.ts file :
import { StartServer } from './services';
StartServer.start();
StarterServer.ts file :
export class StartServer {
public static async start(): Promise<void> {
try {
const configPort: IConfig = config.get('App.port');
await Connections.startDatabaseConnections();
const server = ServerFactory.create();
await server.init();
console.log(`Server is listening at port ${configPort}...`);
} catch (error) {
console.error((error as Error)?.stack);
}
}
}
ExpressServer.ts file :
import routes from '../routes/index.routes';
export class ExpressApp {
private app = express();
private port = config.get('App.port');
routes() {
this.app.use(routes);
}
async init() {
this.routes();
this.app.listen(this.port);
}
}
index.routes.ts file :
import express from 'express';
const router = express.Router();
router.get('/', (_, res) => {
res.status(200).json({ message: 'App listening...' });
});
export default router;
ServerFactory.ts file :
import { ExpressApp } from '#src/services';
export class ServerFactory {
public static create() {
return new ExpressApp();
}
}
In Node, import (and require) are synchronous. As soon as you require a file it is executed.
// a.js
import b from './b';
console.log('a.js');
//b.js
import c from './c';
console.log('b.js');
// c.js
console.log('c.js');
That will output:
c.js
b.js
a.js
In your case, the index.routes.ts will be executed as soon as it's required by ExpressServer.ts. One pattern to avoid initialisation is to export a factory function to perform the setup. You're doing this for your server already!
import express from 'express';
export default function createRouter() {
const router = express.Router();
router.get('/', (_, res) => {
res.status(200).json({ message: 'App listening...' });
});
}
I have controller homeController where i do export let index
import { Request, Response } from "express";
export let index = (req: Request, res: Response) => {
console.log("home");
};
In app.ts
import * as homeController from "../src/modules/home/controllers/home.controller";
const app = express();
...
app.get("/", homeController.index);
tslint passed it but whe i do npm start (npm run serve), i getting the error
internal/modules/cjs/loader.js:589
throw err;
^
Error: Cannot find module '../src/modules/home/controllers/home.controller'
path to /home.controller is right
The problem is that in your HomeController you are exporting a function, the index function. So, when you import that file you are already importing the function so
import * as homeController from "../src/modules/home/controllers/home.controller";
const app = express();
...
app.get("/", homeController);
might work
If you want to do homeController.index
In you homeController module you should do
import { Request, Response } from "express";
let index = (req: Request, res: Response) => {
console.log("home");
};
module.exports = {
index
}
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
I'm trying to load ExpressJS routes from a Typescript class. The idea is that this should eventually be dynamic routes. But I'm already stuck at defininghard coded routes in a class.
My index.ts js looks like this:
import generic = require('./generic');
import Generic = generic.Generic;
class Test {
private app:any;
private port:number = 3000;
constructor() {
this.app = express();
new Generic();
this.app.listen(this.port, () => {
console.log('Listening on port ' + this.port);
});
}
}
export = Test;
new Test();
Then my generic.ts looks like this. I'm trying to define another route from this class:
module Generic {
export class Generic {
constructor() {
console.log('In generic');
var router:any = express.Router();
router.get('/', function (req, res) {
res.setHeader('Content-Type', 'application/json');
res.status(200).send("AAA");
});
}
}
}
export = Generic;
When I run my application then I see the message In generic appear in console. But when I load my browser it says:
Cannot GET /
So for some reason it's not really registering the route from the Generic class. What am I doing wrong?
Take a look at the sample on Microsoft's github page. Rather than using express.Router, they simply use get and import the function from an external module. It has worked pretty well for me.
app.ts
import * as express from "express";
import * as routes from "./routes/index";
var app = express();
app.get('/', routes.index);
routes/index.ts
import express = require("express")
export function index(req: express.Request, res: express.Response) {
res.render('index', { title: 'ImageBoard'});
};