Search code examples
node.jsapirestexpressthemoviedb-api

Odd behavior while calling ExpressJS REST route


I'm making a simple REST server that wraps around the APIs of TMDb. And I'm using MovieDB Node module which has functions to directly call APIs of TMDb by providing just API key.

I have following app folder structure.

myproject
|
---configuration.json
---index.js
---app.js
---/routes
     |
     --- index.js
     --- /movie
           |
           --- index.js

In myproject/index.js, I have script that simply starts Express server, and app.js is where I'm initializing routes.

myproject/app.js

Here's how routes are initialized.

var myApp = require("express")(),
    configuration = require("./configuration.json");

myApp.locals.tmdbApiKey = configuration.tmdbApiKey;
myApp.use('/api', require('./routes'));

As shown above, server will have API root as localhost:9000/api/ (I'm running it on port 9000).

myproject/routes/index.js

Here's how I'm loading routes for movie.

var router = require('express').Router();
router.use('/movie', require('./movie'));

myproject/routes/movie/index.js

And finally, I have defined end-points as follows.

var router = require('express').Router();

router.get('/genrelist', function(req, res) {
    var tmdb = require('moviedb')(req.app.locals.tmdbApiKey);

    tmdb.genreList(function(err, tmdbRes) {
        if (err)
            res.send(err);

        res.json(tmdbRes);
    });
});

---
---

Usage

Now, calling http://localhost:9000/api/movie/genrelist should respond me with list of Genres that TMDb API provides, via moviedb's genreList() method, in case my understand of routes and map of route is correct.

However, I'm getting following response.

{
  "original": null,
  "response": {
    "req": {
      "method": "GET",
      "url": "https://api.themoviedb.org/3/movie/genrelist"
    },
    "header": {
      "access-control-allow-origin": "*",
      "cache-control": "public, max-age=28800",
      "content-type": "application/json;charset=utf-8",
      "date": "Wed, 15 Jun 2016 11:33:51 GMT",
      "etag": "\"37a6259cc0c1dae299a7866489dff0bd\"",
      "server": "openresty",
      "status": "404 Not Found",
      "x-memc": "MISS",
      "x-memc-age": "0",
      "x-memc-expires": "28800",
      "x-memc-key": "35bba1a0c9464e4e471cdb466209d8b3",
      "x-ratelimit-limit": "40",
      "x-ratelimit-remaining": "38",
      "x-ratelimit-reset": "1465990436",
      "content-length": "84",
      "connection": "Close"
    },
    "status": 404,
    "text": "{\"status_code\":34,\"status_message\":\"The resource you requested could not be found.\"}"
  },
  "status": 404
}

If you see value of response.req.url, it tried calling https://api.themoviedb.org/3/movie/genrelist which is obviously incorrect as TMDb API doesn't have any such end-point.

However, if I change my router.get within myproject/routes/movie/index.js to something like this:

var router = require('express').Router();

router.get('/top_rated', function(req, res) {
    var tmdb = require('moviedb')(req.app.locals.tmdbApiKey);

    tmdb.miscTopRatedMovies(function(err, tmdbRes) {
        if (err)
            res.send(err);

        res.json(tmdbRes);
    });
});

It works just fine, notice that I also changed call from genreList() to miscTopRatedMovies(). Shockingly, if I keep call to genreList() as it is, then also it retrieves list of top_rated movies.

Even scary part is that I'm doing console.log() within GET method and it doesn't log anything. Even debugging the server using node-debug doesn't break execution if I set break-point within GET. So I have feeling that my REST calls via REST client are directly translated to TMDb API calls (may be due to identical REST signatures) and MovieDB is not getting called at all, I'm not 100% sure though.

So, to identify if something is wrong with MovieDB module itself, I created simple JS file that directly uses MovieDB, and ran it with node command, and it worked fine for all the methods I'm using.

So what is wrong that I'm doing here?

Any help is appreciated.


Solution

  • I found what the problem was, and it was a silly one.

    In my Movie routes, I had one end-point as follows.

    router.get('/:movieId', function(req, res) {
        // Call tmdb.movieInfo()
    });
    

    And remaining end-points also started with /, as follows.

    router.get('/popular', function(req, res) {
        // Call tmdb.movieInfo()
    });
    

    So clearly, it is a bad design and will cause troubles, since /:movieId will be invoked when a valid movie ID is passed, but I also have other points which use keywords (exact same as TMDb), so when I tried GET on /popular, MovieDB internally called API as determined by TMDb (/api/movie/popular is valid end-point for TMDb too), and hence I got results correctly.

    However, if I do GET on /genres, my API thought of word genres as a /:movieId, due to identical signature, and as no such ID exists, it failed.

    I have now corrected my end-point as

    router.get('/info/:movieId', function(req, res) {
        // Call tmdb.movieInfo()
    });
    

    And everything works as expected.

    Lesson learned, never have identical end-points where param values and keywords are involved.