We are using NestJS for our NodeJS application.
In our app we have some middlewares/guards/interceptors to create a user-request-context, validate jwt token, intercept request/response, etc.
We also implemented some custom decorators, to set metadata for our endpoints.
It's very easy to use this data in guards / intercetpors, because you have the ExecutionContext in canActivate / intercept functions.
But we are deeply missing this functionality in middlewares.
Is there any chance to get / inject the ExecutionContext in a NestJS middleware?
e.g.
export class SomeMiddleware implements NestMiddleware {
constructor(#Inject('ExecutionContext') context: ExecutionContext) {}
use(req, res, next) {
// get data from context / decorator
Reflect.getMetadata(SOME_KEY, context.getHandler());
next();
}
}
Related
I need the nextjs middleware to run for two different paths:
First, to protect against unauthorized access which is achieved using next-auth.
Second, to redirect authorized users from certain pages e.g. if an authorized user lands on anything under the auth namespace, then redirect to them to the dashboard. (I know I can do this on the client, but its much cleaner on the middleware level, imo).
Problem is that it looks like next-auth takes over the entire middleware logic:
import { withAuth } from "next-auth/middleware"
export default withAuth(
function middleware(req) {
console.log(req.nextauth.token)
},
{
callbacks: {
authorized: ({ token }) => token?.role === "admin",
},
}
)
export const config = { matcher: ["/dashboard/:path*", "/auth/:path*"] };
Is it possible to move this logic to a regular nextjs middleware? If not, how is it possible to implement logic unrelated next-auth, but in addition to it?
I want to integrate sentry with nest.js + express but I just found raven version but that is deprecated.
I follow the sentry docs for integrate with express but dont know how to handle the 'All controllers should live here' part.
const express = require('express');
const app = express();
const Sentry = require('#sentry/node');
Sentry.init({ dsn: 'https://5265e36cb9104baf9b3109bb5da9423e#sentry.io/1768434' });
// The request handler must be the first middleware on the app
app.use(Sentry.Handlers.requestHandler());
**// All controllers should live here
app.get('/', function rootHandler(req, res) {
res.end('Hello world!');
});**
// The error handler must be before any other error middleware and after all controllers
app.use(Sentry.Handlers.errorHandler());
// Optional fallthrough error handler
app.use(function onError(err, req, res, next) {
// The error id is attached to `res.sentry` to be returned
// and optionally displayed to the user for support.
res.statusCode = 500;
res.end(res.sentry + "\n");
});
app.listen(3000);
I just created a Sample Project on Github to answer this question:
https://github.com/ericjeker/nestjs-sentry-example
Below is a partial copy of the README file. Let me know if you have any questions.
Create the needed elements
Create Sentry module, service, and interceptor
$ nest g module sentry
$ nest g service sentry
$ nest g interceptor sentry/sentry
SentryModule
Create the SentryModule.forRoot() method and add the Sentry.init(options) in it.
Call the SentryModule.forRoot({...}) in the AppModule and integrate with your preferred configuration (I use ConfigModule and a .env file).
Add the call to the Express requestHandler middleware in the AppModule.configure().
configure(consumer: MiddlewareConsumer): void {
consumer.apply(Sentry.Handlers.requestHandler()).forRoutes({
path: '*',
method: RequestMethod.ALL,
});
}
It is important to use that middleware otherwise the current Hub will be global and
you will run into conflicts as Sentry creates a Hub by thread and Node.js is not multi-threaded.
SentryService
We want to initialize the transaction in the constructor of the service. You can
customize your main transaction there.
Note that because I inject the Express request, the service must be request scoped. You
can read more about that here.
#Injectable({ scope: Scope.REQUEST })
export class SentryService {
constructor(#Inject(REQUEST) private request: Request) {
// ... etc ...
}
}
SentryInterceptor
The SentryInterceptor will capture the exception and finish the transaction. Please also
note that it must be request scoped as we inject the SentryService:
#Injectable({ scope: Scope.REQUEST })
export class SentryInterceptor implements NestInterceptor {
constructor(private sentryService: SentryService) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// ... etc ...
}
}
As an example, I added a span. This is not necessary, but it will just make the trace nicer in the performance viewer of Sentry.
You can add more spans anywhere in your application simply by injecting the SentryService and calling startChild or by simply calling the startChild method of the current span.
For integrate sentry with nestjs we to follow this steps:
install npm i nest-raven
In main.ts
async function bootstrap() {
Sentry.init({ dsn: 'https://5265e36cb9104baf9b3109bb5da9423e#sentry.io/1768434' });
const app = await NestFactory.create(AppModule);
// middlewares
await app.listen(3000);
}
For use it to all controllers in app.module.ts
#Module({
imports: [
RavenModule,...
],
controllers: [],
providers: [{
provide: APP_INTERCEPTOR,
useValue: new RavenInterceptor({
filters: [
// Filter exceptions of type HttpException. Ignore those that
// have status code of less than 500
{ type: HttpException, filter: (exception: HttpException) => 500 > exception.getStatus() },
],
}),
}],
})
The issue was tracking here https://github.com/getsentry/sentry-javascript/issues/2269, there you can follow an example.
I have an API and was trying to send a request. That is working but I noticed that the classes were not destroyed after I received a response. I'm working with nestJS at the moment but nodeJS + expressJS also had this issue when I tried to test.
I'm using following code:
#Injectable()
export class UsersService {
s = '';
constructor() {}
async findAll(): Promise<any> {
this.s += ' haha ';
return await this.s;
}
}
This returned haha first time haha haha the second time and so on.
I'm not really sure if this is the desired behaviour or may have not configured properly, because I'm just learning nestJS now. I have previously worked with Zend Framework which did not show this behaviour.
Any guidance will be much appreciated.
Thank you.
With the release of nest.js 6.0, injection scopes were added. With this, you can choose one of the following three scopes for your providers:
SINGLETON: Default behavior. One instance of your provider is used for the whole application
TRANSIENT: A dedicated instance of your provider is created for every provider that injects it.
REQUEST: For each request, a new provider is created. Caution: This behavior will bubble up in your dependency chain. Example: If UsersController (Singleton) injects UsersService (Singleton) that injects OtherService (Request), then both UsersController and UsersService will automatically become request-scoped.
Usage
Either add it to the #Injectable() decorator:
#Injectable({ scope: Scope.REQUEST })
export class UsersService {}
Or set it for custom providers in your module definition:
{
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT,
}
What you are looking for are request-scoped providers. They are not supported in nest v5, see this issue. As for now, all providers are singletons.
They were added with this pull request though and will be part of nest v6. With the new version, we will get transient and per-request scopes.
I have a "controller" middleware (connected to koa-router).
I am wondering what is the best approach to organize my "internal" app logic (beyond my controller, the middleware function connected to koa-router router.post('/', createCtrl)).
First case: "Everything is a middleware". My controller simply composes the set of middleware functions required to progressively go from the initial request object to a well suited response object. (I read and pass the arguments required by the next middleware through ctx.state).
import * as view from './views'
import * as repo from './repository'
import * as response from '../../services/response'
import { sign } from '../../services/jwt'
import compose from 'koa-compose'
const createCtrl = compose([
repo.create(),
sign(),
view.sessionUser(),
response.success(201)
])
Second case: The application logic is "decoupled" completely from Koa. The controller would be a koa middleware calling none-middleware functions as follow:
import * as view from './views'
import * as repo from './repository'
import * as response from '../../services/response'
import { sign } from '../../services/jwt'
const createCtrl = async function (ctx) {
try {
const entity = await repo.create()
const token = await sign(entity.id)
const dto = view.sessionUser(token, entity)
const response = response.success(201)
response(ctx, dto) // <- this is also a middleware
} catch(err) {
ctx.throw(500, 'my not very well handled error')
}
}
Is it a good idea to think of the controller as a composition of middleware functions? Or is this a misuse of what middleware functions are meant for?
The best way, is to separate the logic from the controller. Having this separation will allow you reuse the same logic in other parts of the application and it's much easier to test it.
Please check this repository: https://github.com/Talento90/typescript-node
I have a server folder where I put all server infrastructure like controllers and routes (both separated) and then I pass my managers to the server. Managers contain the application logic and are passed to the server.
To summarise: Application logic must NEVER depend on infrastructure in this case an HTTP Server.
If router.all() just match all methods,could it be instead by router.use()?
and what router.use() diff between router.route()?
router.all: What this means is, it doesn't matter the method of the request.. (post, get, put), if the url matches, execute the function.
ex- router.all("/abc",fn) will be work for all request to /abc
router.use() : router.use() helps you write modular routes and modules.. You basically define a middle ware for routes.
router.use("/pqr", pqrRoutes)
now for all requests that start with /pqr like /pqr/new or /pqr/xyz can be handles inside the pqrRoutes.
router.route(): this is nice way to define the different Method implementations for a single url end point.
lets just say you have two api end points. router.get("/jkl") and router.post("/jkl"), with router.route() you cam sort of combine these different api handlers..
you can say router.route("/jkl").get(fn1).post(fn2)
router.all() matches every http protocol, router.use() is for middleware, and router.route() returns an instance of a single route which you can then use to handle HTTP verbs with optional middleware.
You should check out the documentation for more informations
app.all(), which is not derived from any HTTP method. This method is used for loading middleware functions at a path for all request methods.
app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...')
next() // pass control to the next handler
})
Use the express.Router class to create modular, mountable route handlers. A Router instance is a complete middleware and routing system; for this reason, it is often referred to as a “mini-app”.
The following example creates a router as a module, loads a middleware function in it, defines some routes, and mounts the router module on a path in the main app.
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
Bascially we use .use when we use a middleware
express.Router
Use the express.Router class to create modular, mountable route handlers. A Router instance is a complete middleware and routing system; for this reason, it is often referred to as a “mini-app”.
The following example creates a router as a module, loads a middleware function in it, defines some routes, and mounts the router module on a path in the main app.
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
There can we more than 1 router this instance of router has name router defined below the express defination.
Here is the documentation for router
https://expressjs.com/en/guide/routing.html
Router.use()
Routers behave like middleware and can be .use()d by the app in other routers. In other words, routers allow you to chunk your big app into numerous mini-apps that you can later put together. For small apps, this might be overkill, but as soon as you think, “This app.js file is getting big,” it’s time to think about breaking down your app with routers.
router.route(path)
The router.route(path) method is used to chain HTTP verb methods. For example, in a create, read, update, and
delete (CRUD) server that has POST, GET, PUT, and DELETE endpoints for the /posts/:id URL (e.g., /posts/53fb401
dc96c1caa7b78bbdb), we can use the Router class as follows:
var express = require('express');
var router = express.Router();
router.param('postId', function(request, response, next) {
// Find post by ID
// Save post to request
request.post = {
name: 'Node.js',
url: 'http://your-url/node-js-blog'
};
return next();
});
The Router.route(path) method provides the convenience of chaining methods, which is a more appealing way
to structure, your code than re-typing router for each route.
Alternatively, we can use router.VERB(path, [callback...], callback) to define the routes just as we
would use app.VERB(). Similarly, the router.use() and router.param() methods work the same as app.use() and
app.param().