How can I return a non empty response using Nest JS api and postman - node.js

I am using Nest Js to setup my server. I tried to fetch data from Postman to test if the API urls work. However I find that I get empty response from the server or undefined value from the postman request. The below code is the users.controller.ts
import {
Body,
Controller,
Delete,
Get,
Header,
Param,
Post,
Put,
} from '#nestjs/common';
import { User } from './users.entity';
import { UsersService } from './users.service';
#Controller('users')
export class UsersController {
constructor(private usersService: UsersService) {}
/* #Get('/api/users')
index(): string {
return 'This will return user.';
} */
#Get()
index(): Promise<User[]> {
return this.usersService.findUsers();
}
#Post('create')
#Header('Content-Type', 'application/json')
async create(#Body() userData: User): Promise<any> {
console.log(userData.UserId, userData.UserType + ' Check if working');
return this.usersService.create(userData);
}
#Put(':userid/update')
async update(#Param('userid') userid, #Body() userData: User): Promise<any> {
userData.UserId = Number(userid);
console.log('Update #' + userData.UserId);
return this.usersService.update(userData);
}
#Delete(':userid/delete')
async delete(#Param('userid') userid): Promise<any> {
return this.usersService.delete(userid);
}
}
and this code is users.service.ts
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './users.entity';
import { UpdateResult, DeleteResult } from 'typeorm';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(User) private userRepository: Repository<User>,
) {}
async findUsers(): Promise<User[]> {
return await this.userRepository.find();
}
async create(user: User): Promise<User> {
console.log(user.UserType + 'User type');
return await this.userRepository.save(user);
}
async update(user: User): Promise<UpdateResult> {
return await this.userRepository.update(user.UserId, user);
}
async delete(userid): Promise<DeleteResult> {
return await this.userRepository.delete(userid);
}
}
If you can see the post request, there is a console.log() to check whether there is a response or not. Hence I am getting an undefined value instead. I am seeking for support to understand where I am going wrong. I am not able to traceback the error as well.

Welcome!
You are not logging what your service returns, instead you are logging what your service receives as the body of the request:
#Post('create')
#Header('Content-Type', 'application/json')
async create(#Body() userData: User): Promise<any> {
// ##################################
// here, you are logging userData, which is the object your
// service receives as input
// ##################################
console.log(userData.UserId, userData.UserType + ' Check if working');
return this.usersService.create(userData);
}
If that console.log line is logging undefined, it means you are not sending the right data in your HTTP Post request body in postman.
You should be sending a User json object as the request payload.

Had the same problem and solved it by annotating my return object.
export class CriarTarefaDto {
public nome: string;
public ativo?: boolean;
}
This was my object that always returned me empty when checking the return of the #Body annotation. I fixed it by including class-validator lib annotations in my DTO properties.
import { IsBoolean, IsString } from 'class-validator';
export class CriarTarefaDto {
#IsString()
public nome: string;
#IsBoolean()
public ativo?: boolean;
}
In your DTO object you need to use some annotation or extend the DTO with classes that register its properties.

Related

How to Extract a Value from microservice returned Observable in NestJS?

I am trying to implement NestJS Microservice to Validate my Bearer Token:
Whenever I get TCP response from Microservice which decides whether token is valid or not, it return Observable object, I am unable to extract the response from that Observable, please help me. Not getting proper way to extract the value from Observable in Nestjs, already tried lastValueFrom from RXJS
Here is the Controller to Make a call to Microservice(consumer/caller of microservice):
app.controller.ts
import { Body, Controller, Get, Post } from '#nestjs/common';
import { AppService } from './app.service';
import { PayloadDTO} from './payload.dto';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getHello(): string {
return this.appService.getHello();
}
#Post()
createUserData(#Body() payload: PayloadDTO) {
const token = header.authorization.replace('Bearer ', '')
const isValidToken = this.appService.validateToken(token);
}
if(isValidToken) {
this.appService.CreateRecord(Payload : PayloadDTO)
} else {
//Throw New UnathorizedException
}
}
app.service.ts
import { Inject, Injectable } from '#nestjs/common';
import { ClientProxy } from '#nestjs/microservices';
import { PayloadDTO } from './payload.dto';
#Injectable()
export class AppService {
constructor(
#Inject('COMMUNICATION') private readonly commClient: ClientProxy
) {}
// service method to call and validate token
validateToken(token: String) {
//calling microservice by sending token
const checkToken: Observable = this.commClient.send({cmd:'validate_token'},token);
//Unable to extract value from Observable here, it is returning observable object
return extractedResponse;
}
// service method to create record
createRecord(payload: PayloadDTO) {
//code related to creating new Record
}
}
app.controller.ts - Microservice Project
import { Controller, Get } from '#nestjs/common';
import { MessagePattern } from '#nestjs/microservices';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#MessagePattern({ cmd: 'validate_token' })
validateToken() {
return this.appService.checkToken();
}
}
Finally I got the answer on my own:
In Microservice Consumer/Caller app.controller.ts I forgot to use await before calling this.appService.validateToke(token)
#Post()
async createUserData(#Body() payload: PayloadDTO) {
const token = header.authorization.replace('Bearer ', '')
const isValidToken = await this.appService.validateToken(token);
}
To convert/extract value from Observable:
In app.service.ts (consumer/caller of microservice) :
use async before validateToken
use await lastValueFrom(checkToken)
return resp
// service method to call and validate token
async validateToken(token: String) {
//calling microservice by sending token
const checkToken = this.commClient.send({cmd:'validate_token'},token);
const resp = await lastValueFrom(checkToken);
reutn resp;
}

I created custom nest js guard which is running before every request and I have added a property called all() on the request in that guard

I have created a interface which is extending the request (of express)
I m adding property called all() in it
which contains the body , query and params in it
import { Request as BaseRequest } from 'express';
export interface Request extends BaseRequest {
all(): Record<string, any>;
}
this is the interface
which is extending the express request
and i m adding this all() property using the guard
this is the implementation of it
#Injectable()
export class RequestGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
this.requestHelper(context.switchToHttp().getRequest());
return true;
}
requestHelper(request: any): any {
const all = function (): Record<string, any> {
return {
...request.query,
...request.body,
...request.params,
};
};
request.all = all;
return request;
}
}
in the main.ts file i have used this guard
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '#nestjs/common';
import { RequestGuard } from './core/guards/request.guard';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
}),
);
app.useGlobalGuards(new RequestGuard());
await app.listen(3000);
}
bootstrap();
and i have tried consoling the all() property in the guard and it's working
its mean request is flowing in it
when i try to get this all() property in my controller then it showing
Cannot read properties of undefined (reading 'all')
That's how i m calling it
import {
Controller,
Get,
Post,
Param,
Body,
Req,
Res,
UseGuards,
} from '#nestjs/common';
import { RequestGuard } from 'src/core/guards/request.guard';
import { Request } from 'src/core/http/Request';
import { Response } from 'src/core/http/Response';
#UseGuards(RequestGuard)
#Controller('customers')
export class CustomersController {
constructor(private customersService: CustomersService) {}
#Get('/order-data/:id')
async OrderData(#Param('id') id: string, req: Request, #Res() res: Response) {
console.log(req.all());
const data = await this.customersService.allOrdersData(parseInt(id));
return data;
}
}
I m calling the route localhost:3000/customers/order-data/1
console.log(req.all());
It should print {id:{'1'}}
But it's giving error
Cannot read properties of undefined (reading 'all')
You're missing the #Req() for your req property in the OrderData method.

How to use mongoose.isValidObjectId as a middleware in nestjs?

I have an issue with repetitive requests for checking an Order id, if it is valid ObjectId or not. I got this error:
CastError: Cast to ObjectId failed for value "629b9fbd620dbc419a52e8" (type string) at path "_id" for model "Order"
After a lot of Googling, I found two approaches to tackle the problem, however I'll have to duplicate these codes for each service, which isn't a good idea.
First approach:
if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
throw new HttpException('Not a valid ObjectId!', HttpStatus.NOT_FOUND);
} else {
return id;
}
Second approach:
if (!mongoose.isValidObjectId(req.params.id)) {
throw new BadRequestException('Not a valid ObjectId');
} else {
return id;
}
I used below codes for making and using a middleware, thus I could check ID whenever a service using an id parameter.
validateMongoID.ts
import {
BadRequestException,
Injectable,
NestMiddleware,
} from '#nestjs/common';
import { Request, Response, NextFunction } from 'express';
import mongoose from 'mongoose';
#Injectable()
export class IsValidObjectId implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
// Validate Mongo ID
if (req.params.id) {
if (!mongoose.isValidObjectId(req.params.id)) {
throw new BadRequestException('Not a valid ObjectId');
}
}
next();
}
}
orders.module.ts
export class OrdersModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(IsValidObjectId).forRoutes('/');
}
}
After trying as a middleware in the orders.modules.ts, I got the same error mentioned above. So, any idea to use it as a middleware?
I had to do this exact thing a couple of weeks ago.
Here is my solution. Works perfectly fine. Not a middleware, though.
id-param.decorator.ts
import { ArgumentMetadata, BadRequestException, Param, PipeTransform } from '#nestjs/common';
import { Types } from 'mongoose';
class ValidateMongoIdPipe implements PipeTransform<string> {
transform(value: string, metadata: ArgumentMetadata) {
if (!Types.ObjectId.isValid(value)) {
throw new BadRequestException(`${metadata.data} must be a valid MongoDB ObjectId`);
}
return value;
}
}
export const IdParam = (param = '_id'): ParameterDecorator => (
Param(param, new ValidateMongoIdPipe())
);
Usage
// If param is called _id then the argument is optional
#Get('/:_id')
getObjectById(#IdParam() _id: string) {
return this.objectsService.getById(_id);
}
#Get('/:object_id/some-relation/:nested_id')
getNestedObjectById(
#IdParam('object_id') objectId: string,
#IdParam('nested_id') nestedId: string,
) {
return this.objectsService.getNestedById(objectId, nestedId);
}
How it works
When using the #Param decorator you can give it transform pipes that will validate and mutate incoming value.
#IdParam decorator is just a #Param with the ValidateMongoIdPipe provided as a second argument.
I have found another way to solve it with the help of Lhon (tagged in comments).
create a file (I named it globalErrorHandler.ts) as follows:
import {
ArgumentsHost,
ExceptionFilter,
HttpException,
HttpStatus,
InternalServerErrorException,
} from '#nestjs/common';
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: InternalServerErrorException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
/**
* #description Exception json response
* #param message
*/
const responseMessage = (type, message) => {
response.status(status).json({
statusCode: status,
path: request.url,
errorType: type,
errorMessage: message,
});
};
// Throw an exceptions for either
// MongoError, ValidationError, TypeError, CastError and Error
if (exception.message) {
const newmsg: any = exception;
responseMessage(
'Error',
newmsg.response?.message ? newmsg.response.message : exception.message,
);
} else {
responseMessage(exception.name, exception.message);
}
}
}
add below line to main.ts
app.useGlobalFilters(new AllExceptionsFilter());
create another file (I named it validateMongoID.ts) as follows:
import {
BadRequestException,
Injectable,
NestMiddleware,
} from '#nestjs/common';
import { Request, Response, NextFunction } from 'express';
#Injectable()
export class IsValidObjectId implements NestMiddleware {
async use(req: Request, res: Response, next: NextFunction) {
// Validate Mongo ID
if (req.params.id) {
if (!/^[a-fA-F0-9]{24}$/.test(req.params.id)) {
throw new BadRequestException('Not a valid ObjectId');
}
}
next();
}
}
last step: import it as a middleware in app.module.ts
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(IsValidObjectId).forRoutes('*');
}
}

How to pass api key as query string on request url using passport and nestjs

I have developed api-key strategy following https://www.stewright.me/2021/03/add-header-api-key-to-nestjs-rest-api/
and it works, I pass api-key in header and it authorize it.
Now for some cases I need to pass api-key as query params to url instead of header. I wasn't able to figure it out.
example mysite.com/api/book/5?api-key=myapikey
my current code is
api-key-strategy.ts
#Injectable()
export class ApiKeyStrategy extends PassportStrategy(Strategy, 'api-key') {
constructor(private configService: ConfigService) {
super({ header: 'api-key', prefix: '' }, true, async (apiKey, done) =>
this.validate(apiKey, done)
);
}
private validate(apiKey: string, done: (error: Error, data) => any) {
if (
this.configService.get(AuthEnvironmentVariables.API_KEY) === apiKey
) {
done(null, true);
}
done(new UnauthorizedException(), null);
}
}
api-key-auth-gurad.ts
import { Injectable } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class ApiKeyAuthGuard extends AuthGuard('api-key') {}
app.controller
...
#UseGuards(ApiKeyAuthGuard)
#Get('/test-api-key')
testApiKey() {
return {
date: new Date().toISOString()
};
}
...
I found a solution in case someone else has same problem.
I added canActivate method to my guard, then read the api key from request.query, and add it to header. Then the rest of code is working as before and checking header
#Injectable()
export class ApiKeyAuthGuard extends AuthGuard('api-key') {
canActivate(context: ExecutionContext) {
const request: Request = context.switchToHttp().getRequest();
if (request && request.query['api-key'] && !request.header('api-key')) {
(request.headers['api-key'] as any) = request.query['api-key'];
}
return super.canActivate(context);
}
}

Nestjs how to pass data from AuthGuard to controller

I have two microservices one for authentication and another for users. I can log in and get a token, and i can use protected routes only when logged in. However I want to use the userId which i get in the AuthGuard's canActivate function, but i cant reach it in the controller. What is the best way to do it?
My auth guard:
import { CanActivate, ExecutionContext, Inject, Logger } from '#nestjs/common';
import { ClientProxy } from '#nestjs/microservices';
export class JwtAuthGuard implements CanActivate {
constructor(
#Inject('AUTH_CLIENT')
private readonly client: ClientProxy,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
try {
const res = await this.client
.send(
{ role: 'auth', cmd: 'check' },
{ jwt: req.headers['authorization']?.split(' ')[1] },
)
.toPromise<boolean>();
return res;
} catch (err) {
Logger.error(err);
return false;
}
}
}
The controller:
#UseGuards(JwtAuthGuard)
#Get('greet')
async greet(#Request() req): Promise<string> {
return 'AUTHENTICATED!' + req;
}
The response:
AUTHENTICATED![object Object]
Attach the userId that you get in the AuthGuard to the req object and then you can access it in the controller:
// after fetching the auth user in the AuthGuard, attach its ID like this
req.userId = authUser.id
And in the controller, you can access it like this:
#UseGuards(JwtAuthGuard)
#Get('greet')
async greet(#Request() req): Promise<string> {
return 'AUTHENTICATED USER ID!' + req.userId;
}

Resources