I'm doing the backend of a ToDo's project, and for that I'm using the prism orm, the node framework, the typescript language and to test the project I'm using insomnia. i already did all the methods i wanted (create ToDo, get ToDo's, delete ToDo, change ToDo task, change ToDo title) and they are all working, but when i went to test the error message that would occur when i tried to create a ToDo that already exists, in insomia it gives the following message:
Error: Server returned nothing (no headers, no data)
And in VsCode:
[UnhandledPromiseRejection: 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(). The promise rejected with the reason "#<AppError>".] {
code: 'ERR_UNHANDLED_REJECTION'
}
I made the code as follows:
export class AppError {
public readonly message: string;
public readonly statusCode: number;
constructor(message: string, statusCode = 400) {
this.message = message
this.statusCode = statusCode
}
}
import { AppError } from "../../errors/AppError";
import { prisma } from "../../client/prismaClient";
import { CreateToDoType } from "../Types/CreateToDoType";
export class createToDoUseCase {
async execute({title, task}: CreateToDoType) {
const todoAlreadyExists = await prisma.toDo.findUnique({
where: {
title: title
}
})
if (!todoAlreadyExists) {
const newToDo = await prisma.toDo.create({
data: {
title: title,
task: task
}
})
return newToDo
} else {
console.log("01 4")
throw new AppError("Error! ToDo already exists") //aqui está o erro
}
}
}
import {Request, Response} from "express"
import { createToDoUseCase } from "../UseCases/CreateToDoUseCases"
export class createToDoController {
async handle(req: Request, res: Response) {
const { title, task } = req.body
const CreateToDoUseCases = new createToDoUseCase()
const result = await CreateToDoUseCases.execute({title, task}) // aqui está o erro
return res.status(201).json(result)
}
}
Related
I have this code to change the default message from typeorm when a value in a unique column already exists. It just creates a custom message when we get an error 23505.
if (error.code === '23505') {
// message = This COLUMN VALUE already exists.
const message = error.detail.replace(
/^Key \((.*)\)=\((.*)\) (.*)/,
'The $1 $2 already exists.',
);
throw new BadRequestException(message);
}
throw new InternalServerErrorException();
I will have to use it in other services, so I would like to abstract that code.
I think I could just create a helper and then I import and call it wherever I need it. But I don’t know if there is a better solution to use it globally with a filter or an interceptor, so I don’t have to even import and call it in different services.
Is this possible? how can that be done?
If it is not possible, what do you think the best solution would be?
Here all the service code:
#Injectable()
export class MerchantsService {
constructor(
#InjectRepository(Merchant)
private merchantRepository: Repository<Merchant>,
) {}
public async create(createMerchantDto: CreateMerchantDto) {
try {
const user = this.merchantRepository.create({
...createMerchantDto,
documentType: DocumentType.NIT,
isActive: false,
});
await this.merchantRepository.save(user);
const { password, ...merchantData } = createMerchantDto;
return {
...merchantData,
};
} catch (error) {
if (error.code === '23505') {
// message = This COLUMN VALUE already exists.
const message = error.detail.replace(
/^Key \((.*)\)=\((.*)\) (.*)/,
'The $1 $2 already exists.',
);
throw new BadRequestException(message);
}
throw new InternalServerErrorException();
}
}
public async findOneByEmail(email: string): Promise<Merchant | null> {
return this.merchantRepository.findOneBy({ email });
}
}
I created an exception filter for typeORM errors.
This was the result:
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpStatus,
InternalServerErrorException,
} from '#nestjs/common';
import { Response } from 'express';
import { QueryFailedError, TypeORMError } from 'typeorm';
type ExceptionResponse = {
statusCode: number;
message: string;
};
#Catch(TypeORMError, QueryFailedError)
export class TypeORMExceptionFilter implements ExceptionFilter {
private defaultExceptionResponse: ExceptionResponse =
new InternalServerErrorException().getResponse() as ExceptionResponse;
private exceptionResponse: ExceptionResponse = this.defaultExceptionResponse;
catch(exception: TypeORMError | QueryFailedError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
exception instanceof QueryFailedError &&
this.setQueryFailedErrorResponse(exception);
response
.status(this.exceptionResponse.statusCode)
.json(this.exceptionResponse);
}
private setQueryFailedErrorResponse(exception: QueryFailedError): void {
const error = exception.driverError;
if (error.code === '23505') {
const message = error.detail.replace(
/^Key \((.*)\)=\((.*)\) (.*)/,
'The $1 $2 already exists.',
);
this.exceptionResponse = {
statusCode: HttpStatus.BAD_REQUEST,
message,
};
}
// Other error codes can be handled here
}
// Add more methods here to set a different response for any other typeORM error, if needed.
// All typeORM erros: https://github.com/typeorm/typeorm/tree/master/src/error
}
I set it globally:
import { TypeORMExceptionFilter } from './common';
async function bootstrap() {
//...Other code
app.useGlobalFilters(new TypeORMExceptionFilter());
//...Other code
await app.listen(3000);
}
bootstrap();
And now I don't have to add any code when doing changes in the database:
#Injectable()
export class MerchantsService {
constructor(
#InjectRepository(Merchant)
private merchantRepository: Repository<Merchant>,
) {}
public async create(createMerchantDto: CreateMerchantDto) {
const user = this.merchantRepository.create({
...createMerchantDto,
documentType: DocumentType.NIT,
isActive: false,
});
await this.merchantRepository.save(user);
const { password, ...merchantData } = createMerchantDto;
return {
...merchantData,
};
}
}
Notice that now I don't use try catch because nest is handling the exceptions. When the repository save() method returns an error (actually it is a rejected promise), it is caught in the filter.
I have the multipart form to be validated before file upload in nestjs application. the thing is that I don't want the file to be uploaded if validation of body fails.
here is how I wrote the code for.
// User controller method for create user with upload image
#Post()
#UseInterceptors(FileInterceptor('image'))
create(
#Body() userInput: CreateUserDto,
#UploadedFile(
new ParseFilePipe({
validators: [
// some validator here
]
})
) image: Express.Multer.File,
) {
return this.userService.create({ ...userInput, image: image.path });
}
Tried so many ways to turn around this issue, but didn't reach to any solution
Interceptors run before pipes do, so there's no way to make the saving of the file not happen unless you manage that yourself in your service. However, another option could be a custom exception filter that unlinks the file on error so that you don't have to worry about it post-upload
This is how I created the whole filter
import { isArray } from 'lodash';
import {
ExceptionFilter,
Catch,
ArgumentsHost,
BadRequestException,
} from '#nestjs/common';
import { Request, Response } from 'express';
import * as fs from 'fs';
#Catch(BadRequestException)
export class DeleteFileOnErrorFilter implements ExceptionFilter {
catch(exception: BadRequestException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
const getFiles = (files: Express.Multer.File[] | unknown | undefined) => {
if (!files) return [];
if (isArray(files)) return files;
return Object.values(files);
};
const filePaths = getFiles(request.files);
for (const file of filePaths) {
fs.unlink(file.path, (err) => {
if (err) {
console.error(err);
return err;
}
});
}
response.status(status).json(exception.getResponse());
}
}
I'm trying to build an API with express and pg. Whenever I try to access an endpoint that is related to a query to the database I get the error above.
My handler function is as follows:
import { Request, Response, Router, NextFunction } from 'express';
import { Orders } from '../models/order';
const orders = new Orders;
const index = async (_req: Request, res: Response, next: NextFunction) => {
try {
const ordersList = await orders.index();
res.json(ordersList);
} catch (err) {
next(err)
}
}
const ordersRoute = Router();
ordersRoute.get('/', index);
This handler refers to the following model:
import { Pool } from 'pg';
client = new Pool({
host: POSTGRES_HOST,
database: POSTGRES_DB,
user: POSTGRES_USER,
password: POSTGRES_PASSWORD,
port: parseInt(POSTGRES_PORT as string, 10)
export class Orders {
async index(): Promise<Order[]> {
try {
const conn = await client.connect();
const sql = 'SELECT * FROM orders';
const result = await conn.query(sql);
conn.release();
return result.rows;
} catch (err) {
throw new Error(`Cannot get orders: ${err}`);
}
}
}
anytime I try to access the endpoint I get
Error: Cannot get orders: TypeError: Cannot read properties of
undefined (reading 'connect')
in the console.
any idea how to fix ?
So how things work in your case.
All modules are read by nodejs starting from your index one by one from top to bottom.
In your script, you declare client and then export a class. In that case, your client is setup, then you export a class, and that file is completed, meaning that the only thing that remains is the exported thing. So when you try to use the exported class, you'll not have the same context as in your module.
You need to export the client too and import it where you need that class or to include client definition inside the class
Remote Server
#Catch(RpcException)
export class RpcExceptionHandler implements RpcExceptionFilter<RpcException> {
catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
return throwError(exception.getError());
}
}
#UseFilters(new RpcExceptionHandler())
#GrpcMethod('AppController', 'Accumulate')
async accumulate(numberArray: INumberArray, metadata: any): Promise<ISumOfNumberArray> {
throw new RpcException({
code: 5,
message: 'Data Not Found'
})
}
Client code
#Get('add')
async getSumc(#Query('data') data: number[]) {
try {
let ata = await this.grpcService.accumulate({ data });
return ata;
} catch (err) {
//logic here if error comes
return err;
}
}
Proto defination.
syntax = "proto3";
package app;
// Declare a service for each controller you have
service AppController {
// Declare an rpc for each method that is called via gRPC
rpc Accumulate (NumberArray) returns (SumOfNumberArray);
}
// Declare the types used above
message NumberArray {
repeated double data = 1;
}
message SumOfNumberArray {
double sum = 1;
}
If error comes it is not going to catch block, just showing the server error.
I want to catch the error if remote throwing any error.
Try this one:
#Get('add')
async getSumc(#Query('data') data: number[]) {
try {
let ata = await this.grpcService.accumulate({ data }).toPromise();
return ata;
} catch (e) {
throw new RpcException(e);
}
}
Example here
Situation:
Developing api in nest & grapqhql
Worked on one laptop, everything was working well
Then cloned my repo on other laptops, installed dependencies, created a new local database.
App is being built with no errors
When following localhost:4000 in browser to open graphql playground I'm receiving 500 error end next message:
ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'logIn')
TypeError: Cannot read properties of undefined (reading 'logIn')
at authenticate (/home/gleb/Projects/artwine-api/node_modules/passport/lib/middleware/authenticate.js:96:21)
at /home/gleb/Projects/artwine-api/node_modules/#nestjs/passport/dist/auth.guard.js:91:3
at new Promise (<anonymous>)
at /home/gleb/Projects/artwine-api/node_modules/#nestjs/passport/dist/auth.guard.js:83:83
at JWTAccessAuthGuard.<anonymous> (/home/gleb/Projects/artwine-api/node_modules/#nestjs/passport/dist/auth.guard.js:49:36)
at Generator.next (<anonymous>)
at fulfilled (/home/gleb/Projects/artwine-api/node_modules/#nestjs/passport/dist/auth.guard.js:17:58)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Code of a passport lib function where the error is caught:
return function authenticate(req, res, next) {
req.login =
req.logIn = req.logIn || IncomingMessageExt.logIn;
req.logout =
req.logOut = req.logOut || IncomingMessageExt.logOut;
req.isAuthenticated = req.isAuthenticated || IncomingMessageExt.isAuthenticated;
req.isUnauthenticated = req.isUnauthenticated || IncomingMessageExt.isUnauthenticated;
req._sessionManager = passport._sm;
..............
Link to the repo: https://github.com/Gleb-Gaiduk/artwine-api
Any ideas on what could go wrong after cloning the working repository?
You need to transform the ExecutionContext from Graphql to one Nestjs/Passport can read: https://docs.nestjs.com/graphql/other-features#execution-context
import { ExecutionContext, Injectable } from '#nestjs/common';
import { GqlExecutionContext } from '#nestjs/graphql';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class MySuperGuard extends AuthGuard('jwt') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
try adding this into "subscriptions" section of GraphQL module initialization:
'subscriptions-transport-ws': {
onConnect: (headersRaw: Record<string, unknown>) => {
// Lowercase each header key
const headers = Object.keys(headersRaw).reduce((dest, key) => {
dest[key.toLowerCase()] = headersRaw[key];
return dest;
}, {});
return {
req: {
headers: headers,
},
};
},
},
I have no idea why it is not documented, but it worked for me.
You should look your Guard and strategy you are using and should handle errors from there for example:
#Injectable()
export class PassportLocalGuard extends AuthGuard('local') {
protected readonly logger = new Logger(PassportLocalGuard.name);
canActivate(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const { req } = ctx.getContext();
return super.canActivate(new ExecutionContextHost([req]));
}
handleRequest(err: any, user: any) {
if (err) {
this.logger.error(`Auth Error! ${err.message}`);
throw err;
}
if (!user) {
this.logger.error('Auth Error! User not found');
throw new AuthenticationError('Auth Error! User not found');
}
return user;
}
}