How to use cookie in middleware of Nest JS ( Fastify ) - nestjs

I'm using
Nestjs CLI 9.0.0
Nestjs common - Nestjs Platform Fastify 9.0.11
Fastify - 4.4.0
#fastify/cookie - 8.0.0
I try to use cookie and set cookie in middleware.
But it seems all about cookie is undefined.
this.logger.debug("cookies", request.cookies)
this.logger.debug("setCookie", response.setCookie)
this.logger.debug("cookie", response.cookie)
Log from Middleware
DEBUG [DeviceMiddleware] cookies
DEBUG [DeviceMiddleware] undefined
DEBUG [DeviceMiddleware] setCookie
DEBUG [DeviceMiddleware] undefined
DEBUG [DeviceMiddleware] cookie
DEBUG [DeviceMiddleware] undefined
Log from Controller
DEBUG [AppController] cookies
DEBUG [AppController] Object:
{}
DEBUG [AppController] setCookie
DEBUG [AppController] function setCookie (name, value, cookieOptions) {
const opts = Object.assign({}, options.parseOptions, cookieOptions)
return fastifyCookieSetCookie(this, name, value, opts, signer)
}
DEBUG [AppController] cookie
DEBUG [AppController] function setCookie (name, value, cookieOptions) {
const opts = Object.assign({}, options.parseOptions, cookieOptions)
return fastifyCookieSetCookie(this, name, value, opts, signer)
}

Coming back with a clear mind this isn't a bug at all, nor is it related to when parts of the code are ran. It's related to how middleware works in Nest and the middie wrapper. To make the middleware compatible, Nest passes the req and res to middie, but this req and res is IncomingMessage and ServerResponse (the same values that an express middleware would have) whereas what the fastify plugins attach req.cookie, res.setCookie and res.cookie to are the FastifyRequest and FastifyReply wrapper classes, which have IncomingMessage and ServerResponse available at the .raw property, but are actually more complex objects themselves.
This is how middleware compatibility with fastify and middie was designed to work, as we are using express style middleware with fastify's plugin based architecture. I'd suggest if possible to move the code accessing req.cookie to either a guard or an interceptor for better compatibility

Related

NextJS middleware cookies issue

I wanted to check if user has a valid token inside the cookie before accessing /api routes on my NextJS app so I created a middleware which would check that.
Somehow I am not able to extract the value from req.cookies in my middleware... I followed NextJS official docs (https://nextjs.org/docs/advanced-features/middleware). First of all TypeScript is already throwing error: Error [TypeError]: request.cookies.getAll is not a function and also Property 'value' does not exist on type 'string'. Did you mean 'valueOf'?ts(2551)
export function middleware(request: NextRequest) {
const cookie = request.cookies.get("token")?.value;
console.log(cookie);
const allCookies = request.cookies.getAll();
console.log(allCookies);
const response = NextResponse.next();
return response;
}
// See "Matching Paths" below to learn more
export const config = {
matcher: "/api/:path*",
};
You cannot read in middleware the cookie value. You can read the cookie value only server side e.g. getserversideprop

Apollo server context initialization function parameter?

In Apollo server docs, Apollo server constructor run with some configurations, one of them is a context initialization function that is called with every request, and according to the docs, this function parameter is an object that get the request (req) automatically as one field of that object.
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
authScope: getScope(req.headers.authorization)
}),
});
This is ok for me, but in one project i got from Github that creates a what's app clone, the parameter object is different which i couldn't relate, something i searched for and i couldn't find anything can relate
export const server = new ApolloServer({
schema: rootModule.schema,
context: (session: any) => {
if (session.connection) {
const req = session.connection.context.session.request;
const cookies = req.headers.cookie;
if (cookies) {
req.cookies = cookie.parse(cookies);
}
}
return rootModule.context(session);
},
});
The context function parameter is a session object, and the developer need to get the request from that session object using that verbose code:
const req = session.connection.context.session.request;
I searched about that session parameter but couldn't find anything in the docs or anywhere else
I am confused about this inconsistency, or am i missing something?
In general, the actual context object can change dependending on the apollo server configuration (see: https://www.apollographql.com/docs/apollo-server/v2/api/apollo-server#middleware-specific-context-fields)
My guess in the "WhatsApp-clone", the parameter comes from the subscription support of Apollo Server (here as in the github repo from Version 2): https://www.apollographql.com/docs/apollo-server/v2/data/subscriptions/#operation-context
For whatever reason the argument in the example project is called session it is propably that object described in the documentation.
The subscription support is added here to the example server: https://github.com/Urigo/WhatsApp-Clone-Server/blob/master/index.ts#L13

Nestjs wss handleConnection(socket) socket.handshake is undefined and cannot access headers to authenticate

I have a Nestjs gateway where I'm trying to run authorization logic depending on values from the headers but wherever I'm trying to access the handshake it always returns 'undefined'
I'm also trying this over SSL which might be making a difference.
main.ts:
import { WsAdapter } from "#nestjs/platform-ws";
app = await NestFactory.create(UserMicroserviceModule, {
httpsOptions: {
key: get_ssl("key"),
cert: get_ssl("cert"),
},
});
app.useWebSocketAdapter(new WsAdapter(app));
await app.listen(process.env.PORT || 443);
gateway.ts:
#UseGuards(SessionGuard)
#WebSocketGateway({ path: "/user" })
export class UserMicroserviceGateway implements OnGatewayConnection {
handleConnection(socket) {
socket.handshake // <== undefined
}
session.guard.ts:
const socket_cookie: any = context.switchToWs().getClient().handshake; // <== undefined
And also, although I've addeded the session guard on the whole gateway --- the guard does not trigger for handleConnection()
Guards and other decorators don't work for the handleConnection() method in nestjs currently and the handshake isn't a concept that exists on the vanilla socket but more of a socket.io thing, so I just switched to using that and manually ran the actions needed for verifications inside the handleConnection() method.

How to make Passport.js work in Adonis Framework

I wanted to know if Passport.js can ONLY be used in an Express framework and not in any other? The docs doesn't completely answer my question. I'm in the middle of migrating my project from Express to Adonis.js and I can't seem to make passport work. Here is a sample of my code:
const passport = use('passport')
const bearer = use('./bearer')
passport.use('bearer', bearer)
module.exports = passport
and here is how I register it:
const namedMiddleware = {
auth: 'Adonis/Middleware/Auth',
guest: 'Adonis/Middleware/AllowGuestOnly',
bearer: passport.authenticate(['bearer'], { session: false }),
}
this is the usage (I provided a bearer token):
Route.post('/', ({ response }) => {
response.json('Hello world')
}).middleware(['bearer'])
It does not work. Error about res.setHeader is not a function showing. Maybe because the resoponse and http structure is different in adonis?
I know that Adonis has its own authentication library but my INITIAL goal is to get what I have now in Express to work in an Adonis environment before making any library changes to avoid any complications.
I recently migrated from knex to adonis.js as well. Integrating passport.js was initially painful but I get it to work with Macros.
For your error, Adonis' Request object has no setHeader. You will need to create a macro on Request for that function. Something like this
function setHeader (name, value) {
this.header(name, value)
}
Response.macro('setHeader', setHeader)
Add that to a provider or hooks and you should be all set.

How do I prevent node from logging an exception to the console in my NextJS/Express app?

I have an Express application that runs a blog in a NextJS app, very similar to the example in their repo
I have set it up so that my app runs a query to fetch a blog article, and if the result is empty it throws a NotFoundException.
I catch this exception in my NextJS _error.js file, which is similar to a React error boundary, where I route the user to my 404 page. This part works fine.
The problem I'm having is that this exception is logged to the node console even though I'm not logging it when catching the exception. This pollutes our company's logging software with all our 404's
Is there some node/express setting I'm missing here that prevents the logging of exceptions? Here's my Express process error handler:
process.on('unhandledRejection', (reason, promise) =>
console.error(`Unhandled Rejection at: ${promise}.\nreason: ${reason.stack || reason}`));
I know there is a log there, but the format of the one I want to eliminate is different to this, so I'm confident this is not the source.
I won't pretend to know what's going on, but my best guess is that next.js is logging the error somewhere. I did some digging and it appears there's an error logger in the server code that will log on errors unless a quiet property is set on the server:
https://github.com/zeit/next.js/blob/canary/packages/next-server/server/next-server.ts#L105:
return this.run(req, res, parsedUrl)
.catch((err) => {
this.logError(err)
res.statusCode = 500
res.end('Internal Server Error')
})
Here's the sig and body for the logError function:
private logError(...args: any): void {
if (this.quiet) return
// tslint:disable-next-line
console.error(...args)
}
If you look at the documentation for using the next API with a custom server, it notes the following options object properties that can be passed to the constructor:
The next API is as follows:
next(opts: object)
Supported options:
dev (bool) whether to launch Next.js in dev mode - default false
dir (string) where the Next project is located - default '.'
quiet (bool) Hide error messages containing server information - default false
conf (object) the same object you would use in next.config.js - default {}
When constructing the next object, try passing quiet as true to see if it resolves your issue:
const express = require('express')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev, quiet: true })
const handle = app.getRequestHandler()
The docs also mentions errors are logged in non-production environments (identified when process.env.NODE_ENV !== 'production'), so I would also check to ensure you're setting NODE_ENV to 'production' when starting your application:
NODE_ENV=production node server.js
I hope this helps!
In express you can setup an ErrorMiddleware.
After all your routes declaration, put
server.use(function(req, res, next) {
handler(req, res).catch(e => {
// use rejected promise to forward error to next express middleware
next(e)
})
});
Like this, when you reject a Promise, next(e) will send your error to next middleware. I usually setup a middleware where i send error, and then i manage all errors in one single function (based on statusCode error,...).

Resources