I'm using nestjs and I was trying to create a custom decorator:
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
import { User } from './user.entity';
export const GetUser = createParamDecorator(
(data, ctx: ExecutionContext): User => {
const req = ctx.switchToHttp().getRequest();
return req.user;
},
);
The decorator is used here:
#Post('/test')
#UseGuards(AuthGuard())
test(#GetUser() user: User) {
console.log(user);
}
But I get the following error:
[Nest] 15053 - 26/03/2020, 13:28:19 [ExceptionsHandler] ctx.switchToHttp is not a function +61625ms
TypeError: ctx.switchToHttp is not a function
at /Users/nelson.larios/Documents/nestjs/nestjs-task-management/dist/auth/get-user.decorator.js:5:21
at /Users/nelson.larios/Documents/nestjs/nestjs-task-management/node_modules/#nestjs/core/router/router-execution-context.js:115:35
at resolveParamValue (/Users/nelson.larios/Documents/nestjs/nestjs-task-management/node_modules/#nestjs/core/router/router-execution-context.js:143:31)
at Array.map (<anonymous>)
at pipesFn (/Users/nelson.larios/Documents/nestjs/nestjs-task-management/node_modules/#nestjs/core/router/router-execution-context.js:148:45)
at /Users/nelson.larios/Documents/nestjs/nestjs-task-management/node_modules/#nestjs/core/router/router-execution-context.js:36:36
at InterceptorsConsumer.intercept (/Users/nelson.larios/Documents/nestjs/nestjs-task-management/node_modules/#nestjs/core/interceptors/interceptors-consumer.js:10:20)
at /Users/nelson.larios/Documents/nestjs/nestjs-task-management/node_modules/#nestjs/core/router/router-execution-context.js:45:60
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async /Users/nelson.larios/Documents/nestjs/nestjs-task-management/node_modules/#nestjs/core/router/router-proxy.js:8:17
Any thoughts?
Thanks
Prior to version 7.x, Custom Decorators were static, and thus did not get the ExecutionContext passed to them. In v7.x release, this has been added.
As mentioned in comments on the OP's question, you need to upgrade to this version to get the example in the documentation working.
Related
I am trying to create an authentication middleware for my express server and I get no Type errors in my IDE but when I try to complile I am getting TypeError: Cannot read properties of undefined (reading protect). The route works fine without the middleware and the middleware has no detectable linting issues. I am also using socket.io so I tried io.use(wrap(middleware)) on the off chance it would work and it didn't but that was a shot in the dark anyway, the problem seems unrelated. I've also tried replacing ALL relevant type declarations with any and got the same problem.
userController:
export const getMe = asyncHandler(async (req: IGetUserAuthInfoRequest, res: Response): Promise<void> => {
res.status(200).json(req.user)
})
RoutesController:
public static protect = asyncHandler(async (req: IGetUserAuthInfoRequest, res: Response, next: NextFunction): Promise<void> => {
let token: string
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
try {
token = req.headers.authorization.split(' ')[1]
const decoded: JwtPayload = jwt.verify(token, process.env.JWT_SECRET, { complete: true })
req.user = await UserModel.findById(decoded.id).select('-password')
next()
} catch (err) {
res.status(401)
throw new Error('Not authorised')
}
}
if (!token) {
res.status(401)
throw new Error('Not a valid token')
}
})
Extended express Request interface:
export interface IGetUserAuthInfoRequest extends Request {
user: any
}
userRouter:
userRouter.route('/me').get(RoutesController.protect, userController.getMe)
The error:
TypeError: Cannot read properties of undefined (reading 'protect')
at Object.<anonymous> (C:\Users\liams\dev\my-rest-server\src\routes\user.router.ts:8:46)
at Module._compile (node:internal/modules/cjs/loader:1097:14)
at Module._compile (C:\Users\liams\dev\my-rest-server\node_modules\source-map-support\source-map-support.js:568:25)
at Module.m._compile (C:\Users\liams\AppData\Local\Temp\ts-node-dev-hook-8639111545614118.js:69:33)
at Module._extensions..js (node:internal/modules/cjs/loader:1149:10)
at require.extensions.<computed> (C:\Users\liams\AppData\Local\Temp\ts-node-dev-hook-8639111545614118.js:71:20)
at Object.nodeDevHook [as .ts] (C:\Users\liams\dev\my-rest-server\node_modules\ts-node-dev\lib\hook.js:63:13)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Module.require (node:internal/modules/cjs/loader:999:19)
[ERROR] 20:08:00 TypeError: Cannot read properties of undefined (reading 'protect')
Any ideas what I'm missing?
EDIT: rather than posting everyline of my code here you can see it in the repo: https://github.com/dotDone/my-rest-server/tree/auth
Your RouteController is not defined yet when you use it in the user controller. ( Your architecture can be improved, but I will try to only answer your question, just know that there is a better way to organize all this )
Try the following
Turn UserRoutes to a class
import { Router } from 'express'
import * as userController from '../controllers/user.controller'
class UserRoutes {
constructor(routesController) {
this.userRouter: Router = Router();
this.routesController = routesController;
this.initRoutes();
}
initRoutes() {
userRouter.route('/').get(userController.getUsers).post(userController.createUser).put(userController.editUser).delete(userController.deleteUser)
userRouter.route('/me').get(this.routesController.protect, userController.getMe)
}
}
Then in your server.ts, create another function, call it initRoutes for example where you do something like this
constructor() {
this.startServer().then(() => this.startControllers()).then(() => this.initRoutes()).catch(err => console.log(err))
}
...
initRoutes() {
this.userRoutes = new UserRoutes(this.routesController);
}
I studying serverless nest.js with aws lambda
When I run serverless offline and send the request, an error occurred.
[Nest] 11048 - 2021. 07. 14. 오후 7:02:11 [NestFactory] Starting Nest application...
[Nest] 11048 - 2021. 07. 14. 오후 7:02:11 [InstanceLoader] AppModule dependencies initialized +16ms
[Nest] 11048 - 2021. 07. 14. 오후 7:02:11 [ExceptionHandler] this.instance.use is not a function +3ms
TypeError: this.instance.use is not a function
at ExpressAdapter.use (C:\study\nestjs-practice\serverless-nestjs\node_modules\#nestjs\core\adapters\http-adapter.js:20:30)
at NestApplication.use (C:\study\nestjs-practice\serverless-nestjs\node_modules\#nestjs\core\nest-application.js:140:26)
at C:\study\nestjs-practice\serverless-nestjs\node_modules\#nestjs\core\nest-factory.js:127:40
at Function.run (C:\study\nestjs-practice\serverless-nestjs\node_modules\#nestjs\core\errors\exceptions-zone.js:9:13)
at Proxy.<anonymous> (C:\study\nestjs-practice\serverless-nestjs\node_modules\#nestjs\core\nest-factory.js:126:46)
at Proxy.<anonymous> (C:\study\nestjs-practice\serverless-nestjs\node_modules\#nestjs\core\nest-factory.js:168:54)
at bootstrapServer (C:\study\nestjs-practice\serverless-nestjs\.build\src\lambda.js:16:17)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async handler (C:\study\nestjs-practice\serverless-nestjs\.build\src\lambda.js:23:20)
at async InProcessRunner.run (C:\study\nestjs-practice\serverless-nestjs\node_modules\serverless-offline\dist\lambda\handler-runner\in-process-runner\InProcessRunner.js:211:24)
at async LambdaFunction.runHandler (C:\study\nestjs-practice\serverless-nestjs\node_modules\serverless-offline\dist\lambda\LambdaFunction.js:355:20)
at async hapiHandler (C:\study\nestjs-practice\serverless-nestjs\node_modules\serverless-offline\dist\events\http\HttpServer.js:601:18)
at async module.exports.internals.Manager.execute (C:\study\nestjs-practice\serverless-nestjs\node_modules\#hapi\hapi\lib\toolkit.js:45:28)
at async Object.internals.handler (C:\study\nestjs-practice\serverless-nestjs\node_modules\#hapi\hapi\lib\handler.js:46:20)
at async exports.execute (C:\study\nestjs-practice\serverless-nestjs\node_modules\#hapi\hapi\lib\handler.js:31:20)
at async Request._lifecycle (C:\study\nestjs-practice\serverless-nestjs\node_modules\#hapi\hapi\lib\request.js:312:32)
at async Request._execute (C:\study\nestjs-practice\serverless-nestjs\node_modules\#hapi\hapi\lib\request.js:221:9)
How can I fix it?
Except for the lambda.ts and serverless.yml files, the settings are the same as when the nest.js project was created.
I'll post the code, please let me know if you know a solution
lambda.ts
import { NestFactory } from '#nestjs/core';
import { Handler, Context } from 'aws-lambda';
import { ExpressAdapter } from '#nestjs/platform-express';
import * as express from 'express';
import { Server } from 'http';
import { AppModule } from './app.module';
import { eventContext } from 'aws-serverless-express/middleware';
import { createServer, proxy } from 'aws-serverless-express';
const binaryMimeTypes: string[] = [];
let cachedServer: Server;
async function bootstrapServer(): Promise<Server> {
if (!cachedServer) {
const expressApp = express();
const nestApp = await NestFactory.create(
AppModule,
new ExpressAdapter(express),
);
nestApp.use(eventContext);
await nestApp.init();
cachedServer = createServer(expressApp, undefined, binaryMimeTypes);
}
return cachedServer;
}
export const handler: Handler = async (event: any, context: Context) => {
cachedServer = await bootstrapServer();
return proxy(cachedServer, event, context, 'PROMISE').promise;
};
serverless.yml
service:
name: serverless-nestjs
plugins:
- serverless-plugin-typescript
- serverless-plugin-optimize
- serverless-offline
provider:
name: aws
runtime: nodejs12.x
functions:
main: # The name of the lambda function
# The module 'handler' is exported in the file 'src/lambda'
handler: src/lambda.handler
events:
- http:
method: any
path: /{any+}
I am using NextJs and it's serverless functions.
I have a api route /api/blog/write, it is a protected route. Only authorize users can access it. For this i defined a middleware which check if request is comeing from a authorize user then processed other wise redirect request to login page.
api/blog/write.js
import requiredLogin from "#middleware/required-login";
const index= (req, res) => {
const cookies = parseCookies({req}).ts
res.statusCode = 200
return res.json({ status: 'Your first blog' })
}
export default requiredLogin(index);
#middleware/required-login.js
import { parseCookies } from 'nookies';
import jwt from "jsonwebtoken";
const JWT_KEY = process.env.JWT_KEY;
const requiredLogin = (handler) => (req,res)=>{
const token = parseCookies({req}).ts;
jwt.verify(token,JWT_KEY,(err,dev)=>{
if(err) return res.status(401).redirect("/auth?auth=signin")();
return handler(req,res);
})
}
module.exports = requiredLogin;
Using the above code for required-login.js gives me
status code : 500 and error is ReferenceError: parseCookies is not defined.
When i just replace return handler(req,res); in required-login.js with return res.status(200).send(dev) then i am getting expected result.
error consoled by node is
ReferenceError: parseCookies is not defined
at index (C:\Users\Rahul kumar\desktop\Projects\dev\bdevg\.next\server\pages\api.js:110:19)
at C:\Users\Rahul kumar\desktop\Projects\dev\bdevg\.next\server\pages\api.js:151:12
at C:\Users\Rahul kumar\desktop\Projects\dev\bdevg\node_modules\jsonwebtoken\verify.js:223:12
at getSecret (C:\Users\Rahul kumar\desktop\Projects\dev\bdevg\node_modules\jsonwebtoken\verify.js:90:14)
at Object.module.exports [as verify] (C:\Users\Rahul kumar\desktop\Projects\dev\bdevg\node_modules\jsonwebtoken\verify.js:94:10)
at C:\Users\Rahul kumar\desktop\Projects\dev\bdevg\.next\server\pages\api.js:148:25
at apiResolver (C:\Users\Rahul kumar\desktop\Projects\dev\bdevg\node_modules\next\dist\next-server\server\api-utils.js:8:7)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async DevServer.handleApiRequest (C:\Users\Rahul kumar\desktop\Projects\dev\bdevg\node_modules\next\dist\next-server\server\next-server.js:45:397)
at async Object.fn (C:\Users\Rahul kumar\desktop\Projects\dev\bdevg\node_modules\next\dist\next-server\server\next-server.js:37:176)
parseCookies is not defined in api/blog/write.js, and you use it there.
Since Node.JS (and JavaScript itself) doesn't expose variables defined in one file to another file that has be "required" by that file, you cannot access parseCookies in api/blog/write.js.
You must import it there as well.
// You must import `parseCookies`!
import { parseCookies } from 'nookies';
import requiredLogin from "#middleware/required-login";
const index= (req, res) => {
// You use parseCookies on the following line,
// so you have to import it.
const cookies = parseCookies({req}).ts
res.statusCode = 200
return res.json({ status: 'Your first blog' })
}
export default requiredLogin(index);
I have implemented WebSockets in Nestjs using below method and one of the requirement is that I need to update the db once I receive any message from websocket server.
In order to do that I have done the below code but getting error like this:
But if I call the same method inside any other controller method it works fine(check /test1). Only when I call it from websocket listener I'm getting this error. Even if I call simple method from same controller to just print some log, I'm getting the same error TypeError: this.processData is not a function. Weird!
Can someone tell me what am I doing wrong with this?
(node:11421) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'updateResponseData' of undefined
at WebSocket.incoming (/opt/dist/controller/sample.controller.js:62:44)
at WebSocket.emit (events.js:210:5)
at Receiver.receiverOnMessage (/opt/node_modules/ws/lib/websocket.js:800:20)
at Receiver.emit (events.js:210:5)
at Receiver.dataMessage (/opt/node_modules/ws/lib/receiver.js:423:14)
at Receiver.getData (/opt/node_modules/ws/lib/receiver.js:353:17)
at Receiver.startLoop (/opt/node_modules/ws/lib/receiver.js:139:22)
at Receiver._write (/opt/node_modules/ws/lib/receiver.js:74:10)
at doWrite (_stream_writable.js:431:12)
at writeOrBuffer (_stream_writable.js:415:5)
at Receiver.Writable.write (_stream_writable.js:305:11)
at Socket.socketOnData (/opt/node_modules/ws/lib/websocket.js:875:35)
at Socket.emit (events.js:210:5)
at addChunk (_stream_readable.js:308:12)
at readableAddChunk (_stream_readable.js:289:11)
at Socket.Readable.push (_stream_readable.js:223:10)
(node:11421) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:11421) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Controller Code :
import { Controller, Post, Get, Inject, Req, UsePipes, Body, Header,Param } from '#nestjs/common';
import { Request } from 'express';
import * as WebSocket from 'ws';
import { DatabaseService } from '../service/datafetch.service';
#Controller('sample')
export class SampleController {
#Inject(dbService)
private dbService: DatabaseService;
static ws: any;
static wsnew: any;
public receiveDistData = '';
// Connect WS & Listen to messages
constructor() {
this.initWs();
SampleController.ws.on('message', async function incoming(data) {
console.log('message recieved 8081');
var dataJson = JSON.parse(data);
await this.dbService.updateResponseData(dataJson);
return data;
});
}
#Get('/test/data/:id')
async testData(#Param('id') id: string) {
return await this.callWs(id);
}
#Get('/test1/data/:id')
async testData1(#Param('id') id: string) {
const data = {id: id, data:{}};
return await this.dbService.updateResponseData(data);
}
async initWs() {
SampleController.ws = new WebSocket('ws://127.0.0.1:8081');
}
async processData() {
console.log('Printing a log...');
}
// Data Distribution
async callWs(id) {
// If Socket is not opened, try to re-open
if(SampleController.ws.readyState != 1) {
console.log('Server is dead....');
this.initWs();
}
const Data = await this.dbService.findAll(id);
await SampleController.ws.send(JSON.stringify({
event: 'channel1',
data: Data,
}));
}
}
Repository Code:
import { InjectRepository } from '#nestjs/typeorm';
import { Injectable } from '#nestjs/common';
import { Repository } from 'typeorm';
import { SampleRepository } from '../repository/sample.repository';
#Injectable()
export class SampleService {
constructor(
#InjectRepository(SampleRepository)
private readonly sampleRepository: SampleRepository
) {}
async updateResponseData(data): Promise<any> {
return await this.sampleRepository.updateData(data);
}
}
Inside an anonymous function, this refers to the global object, not the instance of your service.
Instead you can use an arrow function. Here, this refers to the class instance, as you would expect:
async function(data) {
// this is global object
this.call();
(data) => {
// this is the same as in enclosing object
this.call();
I'm trying to throw a http nest exception to make use of the default nest handler.
I Tried to throw the exception in the controller that calls the adminService.update function, and with great success it worked.
async update(update: DeepPartial<Admin>) {
const admin = await this.findOne({ id: update.id});
const adminName = await this.findOne({ username: update.username});
if (!adminName) {
throw new ConflictException('Username already in use');
}
admin.username = update.username;
admin.save();
}
Output when putting the call in the controller:
{
"statusCode": 409,
"error": "Conflict",
"message": "Username already in use"
}
The controller method.
#Put()
async update(#Body() updateDTO: UpdateDTO): Promise<void> {
throw new ConflictException('Username already in use');
this.adminService.update(updateDTO);
}
The error itself:
UnhandledPromiseRejectionWarning: Error: [object Object] at AdminService.<anonymous> (C:\Users\JBRETAS_EXT\Documents\mapa-digital\dist\admin\admin.service.js:55:19) at Generator.next (<anonymous>)
It seems that I was missing a return statement in my controller method.
#Put()
async update(#Body() updateDTO: UpdateDTO): Promise<void> {
return this.adminService.update(updateDTO);
}