Vikas Kad
Vikas Kad

Reputation: 1021

Express JS calling 2 get methods

I am stuck some silly problem (not sure about this).

I am using Express with Typescript for my API creation.

My problem is as follows:

I have 1 endpoint called Offers, and On that I am doing some operations like findBy Status etc along with CRUD operations, my controller is as follows:

@Get('/findByStatus')
public findByStatus(@QueryParam('status') status: string): Promise<any> {
    try {
        if (OfferValidator.validateStatus(status)) {
            // get data from service
            const data =  this.offerService.findStatus(status);
            if (data) {
                return this.offerService.findStatus(status);
            } else {
                return Promise.reject(new OfferInvalidError(404, 'Offer ID not found'));
            }
        } else {
            return Promise.reject(new OfferInvalidError(404, 'Offer ID not found'));
        }
    } catch (error) {
        return Promise.reject(error);
    }
}

@Get('/:id')
@OnUndefined(OfferNotFoundError)
public async one( @Param('id') id: string): Promise<Offer | undefined> {
    console.log('called id as well');
    try {
        if (OfferValidator.lengthValidator(id)) {
            return Promise.reject(new OfferInvalidError(400, 'Invalid ID supplied'));
        } else {
            console.log('coming in validator again');
            const data = await this.offerService.findOne(id);
            console.log('returned', data);
            if (!data) {
                return Promise.reject(new OfferInvalidError(404, 'Offer ID not found'));
            } else {
                data.category = JSON.parse(data.category);
                data.tags = JSON.parse(data.tags);
                return data;
            }
        }
    }  catch (e) {
        return Promise.reject(new OfferNotFoundError());
    }
}

So now when I call findByStatus with POSTMAN

http://localhost:3000/api/offer/findByStatus?status=sold

then it providing me proper response as well but then it again calling my get by ID method as well, so it showing me errors as follows:

called id as well
coming in validator again
info: [api:middlewares] GET /api/offer/findByStatus?status=available&status=sold 200 61.223 ms - -

returned undefined
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I have checked with adding console logs and it showing me that when my Find My status function gets executed then get by id function is also executing..

EDIT 1:

I am using Express Typescript Boilerplate for basic setup and for routing.

any idea why its happening ?

Please help me to fix the issue.

Thanks in advance.

Upvotes: 0

Views: 566

Answers (1)

Elliot Blackburn
Elliot Blackburn

Reputation: 4164

This is going to happen due to your routing as you've got two conflicting routes. You've got /findByStatus and /:id, both are being triggered because they both technically match the endpoint.

When using /:id you are saying "pick up anything that goes to the / with something after it, then give me that value as req.params.id".

My advice would be to drop the /findByStatus and have it just as / for several reasons.

  1. It will prevent the conflict.
  2. This would be more "RESTful", REST systems are built around CRUD and it is a standard which you can use to help build a CRUD system in a way everyone can understand.
  3. The endpoint will become more extensible.

Since your /findByStatus is basically just a type if filter applied to your indexing route you should be able to do something like this.

@Get('/')
public index(@QueryParam('status') status?: string): Promise<any> {
    if (status) {
        try {
            if (OfferValidator.validateStatus(status)) {
                // get data from service
                const data =  this.offerService.findStatus(status);
                if (data) {
                    return this.offerService.findStatus(status);
                } else {
                    return Promise.reject(new OfferInvalidError(404, 'Offer ID not found'));
                }
            } else {
                return Promise.reject(new OfferInvalidError(404, 'Offer ID not found'));
            }
        } catch (error) {
            return Promise.reject(error);
        }
    }
}

In this scenario, you're now making status an optional parameter, if it is supplied then you'll filter by the status, if it's not supplied then you could do something else or just return the full dataset.

Not only will this stop your conflicting route issue, but you'll also make your application more RESTful and enable you to extend your filter options against a single route.

Upvotes: 2

Related Questions