Search code examples
typescriptexpresstypeorm

Express delete working but gives not found error


I am using this boilerplate for my api. It uses express and typeorm with typescript.

When I want want to delete a question, it works fine, but the response is a 404 not found.

Here is my Question.ts class:

@Entity()
export class Question extends BaseEntity {

    @PrimaryColumn('uuid')
    public id: string;

    @IsNotEmpty()
    @Column()
    public title: string;

    @IsNotEmpty()
    @Column({
        length: 2000,
    })
    public description: string;

    @IsNotEmpty()
    @Column()
    public answered: boolean;

    @ManyToOne(type => User, user => user.questions, { onDelete: 'SET NULL', onUpdate: 'CASCADE' })
    public user: User;

    @IsNotEmpty()
    @ManyToMany(type => Tag)
    @JoinTable()
    public tags: Tag[];

    @OneToMany(type => Answer, answer => answer.question)
    public answers: Answer[];

    @OneToMany(type => Comment, comment => comment.question)
    public comments: Comment[];
}

Here is my request in the controller:

@Delete('/:id')
public delete(@Param('id') id: string): Promise<void> {
    return this.questionService.delete(id);
}

Here is my method in the service:

public async delete(id: string): Promise<void> {
    this.log.info('Delete questions: ', id);
    try {
        await Question.delete(id);
    } catch (error) {
        this.log.error('Could not delete question: ', id, ' Message: ', error);
    }
}

In the console I get the following error: console error

When I get all the questions after the delete, the question isn't there anymore, so the question has been succesfully deleted. Why am I getting a 404 although my delete works?

Update

On Request of aRvi, here is the full file of the controller:

import { Request } from 'express';
import {
    Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam, Req
} from 'routing-controllers';
import { ResponseSchema } from 'routing-controllers-openapi';

import { QuestionNotFoundError } from '../errors/QuestionNotFoundError';
import { Answer, Comment, PagedResult, Question, Tag, User } from '../models/Models';
import { QuestionService } from '../services/Services';
import { CreateQuestionBody } from './Bodies/CreateQuestionBody';
import { PutQuestionBody } from './Bodies/PutQuestionBody';
import { QuestionResponse } from './responses/Responses';

@JsonController('/questions')
export class QuestionController {
    constructor(
        private questionService: QuestionService
    ) { }

    @Get()
    // tslint:disable-next-line:max-line-length
    public async find(@Req() req: Request, @QueryParam('skip') skip: number, @QueryParam('take') take: number, @QueryParam('orderBy') orderBy: string, @QueryParam('where') where: string): Promise<PagedResult<Question>> {
        const questions = await this.questionService.find();

        return new PagedResult<Question>().Create(req, questions, skip, take, orderBy, where);
    }

    @Get('/:id')
    @ResponseSchema(QuestionResponse)
    @OnUndefined(QuestionNotFoundError)
    public findOne(@Param('id') id: string): Promise<Question | undefined> {
        return this.questionService.findOne(id);
    }

    @Get('/:id/answers')
    @OnUndefined(QuestionNotFoundError)
    // tslint:disable-next-line:max-line-length
    public async findAnswers(@Req() req: Request, @Param('id') id: string, @QueryParam('skip') skip: number, @QueryParam('take') take: number, @QueryParam('orderBy') orderBy: string, @QueryParam('where') where: string): Promise<PagedResult<Answer> | undefined> {
        const answers = await this.questionService.findAnswers(id);

        return new PagedResult<Answer>().Create(req, answers, skip, take, orderBy, where);
    }

    @Get('/:id/comments')
    @OnUndefined(QuestionNotFoundError)
    // tslint:disable-next-line:max-line-length
    public async findComments(@Req() req: Request, @Param('id') id: string, @QueryParam('skip') skip: number, @QueryParam('take') take: number, @QueryParam('orderBy') orderBy: string, @QueryParam('where') where: string): Promise<PagedResult<Comment> | undefined> {
        const comments = await this.questionService.findComments(id);

        return new PagedResult<Comment>().Create(req, comments, skip, take, orderBy, where);
    }

    @Get('/:id/tags')
    @OnUndefined(QuestionNotFoundError)
    // tslint:disable-next-line:max-line-length
    public async findTags(@Req() req: Request, @Param('id') id: string, @QueryParam('skip') skip: number, @QueryParam('take') take: number, @QueryParam('orderBy') orderBy: string, @QueryParam('where') where: string): Promise<PagedResult<Tag> | undefined> {
        const tags = await this.questionService.findTags(id);

        return new PagedResult<Tag>().Create(req, tags, skip, take, orderBy, where);
    }

    @Post()
    @ResponseSchema(QuestionResponse)
    public async create(@Body() body: CreateQuestionBody): Promise<Question | undefined> {
        const question = new Question();
        question.title = body.title;
        question.description = body.description;
        question.tags = body.tags;
        // TODO: change to current user
        const users = await User.find();
        const user = users[0];
        question.user = user;
        return this.questionService.create(question);
    }

    @Put()
    @ResponseSchema(QuestionResponse)
    public updateMerge(@Body() body: PutQuestionBody): Promise<Question | undefined> {
        return this.questionService.updateMerge(body);
    }

    @Post('/:id')
    @ResponseSchema(QuestionResponse)
    public updateOne(@Param('id') id: string, @Body() body: CreateQuestionBody): Promise<Question | undefined> {
        const question = new Question();
        question.title = body.title;
        question.description = body.description;
        question.tags = body.tags;

        return this.questionService.updateFull(id, question);
    }

    @Delete('/:id')
    public delete(@Param('id') id: string): Promise<void> {
        return this.questionService.delete(id);
    }
}


Solution

  • I recently had an incursion. @Jerin D Joy kind of brought me to this, so thank you for that. The boilerplate uses routing-controllers. So I looked into the options class in the repository and found, that you can override the default response code on undefined returns. The default setting of course is 404. I changed this to 200 and with some slight modifications this works perfect for me. My configuration as of the current looks like the following:

    const app: Application = createExpressServer({
      cors: true,
      routePrefix: env.app.routePrefix,
      controllers: env.app.dirs.controllers,
      middlewares: env.app.dirs.middlewares,
      classTransformer: true,
      authorizationChecker: authorizationChecker(connection),
      currentUserChecker: currentUserChecker(connection),
      defaults: {
        undefinedResultCode: 200,
      },
    });
    

    This solves my Issue, since on every request I define a custom error when the response is undefined. The only thing I have to do is to remove the @OnUndefined decorator from the delete requests.

    Note: I had to change some requests, so if you adopt my solution, check which requests you need to adjust.

    Note2: The solution of course could also be solved by just returning a string like Successfully deleted object or so but I prefer the solution above, since I think it's the better best practice.