typicode / json-server

Get a full fake REST API with zero coding in less than 30 seconds (seriously)
Other
72.83k stars 7.02k forks source link

Unable to create custom routes using middleware #690

Closed goldmont closed 6 years ago

goldmont commented 6 years ago

index.js

module.exports = () => {
    return {
        home: getHome()
    }
}

function getHome() {
    const items = []
    for (let i = 0; i < 5; i++) {
        items.push({
            id: i
        })
    }
    return items
}

routes.json

{
    "/home/:id": "/home?id=:id"
}

middleware.js

const JSON_SERVER = require('json-server')
const SERVER = JSON_SERVER.create()
const MIDDLEWARES = JSON_SERVER.defaults()
const ROUTER = JSON_SERVER.router({})

var isAuthenticated = true

SERVER.use(MIDDLEWARES)
SERVER.get('/logout', function(req, res) {
    isAuthenticated = false
})
SERVER.use(JSON_SERVER.bodyParser)
SERVER.use(ROUTER)

module.exports = function(req, res, next) {
    if (isAuthorized(req)) {
        next()
    } else {
        res.status(401).send("Error")
    }
}

function isAuthorized(req) {
    return isAuthenticated
}

Hi,

I made the following configuration and it works pretty fine except for the middleware's /logout custom route. The index.js generates five tiny JSON Objects with only the ID field; the routes.json has custom route that allow me to easily select one of the five JSON Objects using an ID as filter; for last, there is the middleware.js which I use to authenticate the user who makes a request. The command I use to launch the server is the following:

json-server index.js -m middleware.js --routes routes.json --port 3000

The issue I'm experiencing with the middleware's /logout custom route is that when I do a GET request to that endpoint, Json-Server tells me that the route does not exist by returning a 404 status code. Can anybody help me with this?

Nilegfx commented 6 years ago

Hi @peppe130

You are mixing the main two approaches/ways of using json-server library.

json-server should be used either as CLI command or as a nodejs module (not both ways at the same time)

So, let's start analysis/clarify the confusions here:

var isAuthenticated = true;

module.exports = function(req, res, next) {
  if (isAuthorized(req)) {
    next()
  } else {
    res.status(401).send("Error")
  }
}

function isAuthorized(req) {
  return isAuthenticated;
}

Given that, your SERVER.get('/logout') or the whole SERVER logic did NOT even ran, what made json-server works is the command line you used:

json-server index.js -m middleware.js --routes routes.json --port 3000

So, what json-server get from the middleware.js file is only the simple middleware that checks if the isAuthenticated variable is true.

in order to fix this confusion, you could run json-server as module only (not CLI command) as follows:

server.js

const JSON_SERVER = require( 'json-server' );
const SERVER      = JSON_SERVER.create();
const MIDDLEWARES = JSON_SERVER.defaults();

//YOUR SEED FILE
const seed        = require( './db.js' );

// because your seedfile exports a function, you should run it to get the generated json object
const data        = seed();

// pass the `data` json object to `json-server`'s router function to generate the necessary routes
const ROUTER      = JSON_SERVER.router(data);

let isAuthenticated = true;

// it is recommended to use the bodyParser middleware before any other middleware in your application
SERVER.use( JSON_SERVER.bodyParser );

//your Authentication middleware (simplified to check the variable directly
// feel free to go back to the function approach if your authentication checks are more complex
SERVER.use( function ( req, res, next ) {
  if ( isAuthenticated ) {
    next();
  } else {
    res.status( 401 ).send( "Unauthorized!" )
  }
} );

// the logout endpoint
SERVER.get( '/logout', function ( req, res ) {
  isAuthenticated = false;
  res.status(200).send('logged out');
} );

//middlewares required by json-server to run correctly
// this sets up the static serving, Gzip, logger,
// the internal json-server body-parser .. etc
SERVER.use( MIDDLEWARES );

/*
I am not sure why you are rewriting this route like this,
because this subtle rewrite changes how you get/patch a single resource
so instead of getting single resource you made it search for
all resources that has the provided `id`, thus, it returns
an array of resources which in your case will always be an array
with single resource, plus it will prevent patching "modifying"
all `/home/:id` resources.

anyways, I kept it as it is.
if you really know what you are doing here, ignore all the above,
otherwise, try to disable this middleware to know what I mean.
*/
SERVER.use(JSON_SERVER.rewriter({
  '/home/:id': '/home?id=:id'
}));

// this is where `json-server`'s magic happens ;)
SERVER.use( ROUTER );

/*
start the application by listening to port 3000,
Although this won't print the nice starting message you see when
running `json-server` as CLI command, it still runs the app correctly.
*/
SERVER.listen( 3000, function () {
  console.log( 'http://localhost:3000' );
} );

start the server by running (don't use json-server command)

node index.js

I tried to add detailed comments above, additionally, you can fork this repo which could help you in case you still have some confusions:

git clone https://github.com/Nilegfx/json-server-issue-690 && cd json-server-issue-690 && npm install && node server.js
goldmont commented 6 years ago

Amazing explanation. Thank you so much man!

PLQin commented 4 years ago

Very detailed explanation. Great!
I have a question, if I want to watch the router.json and other separate API files, how do I edit them in server.js ? @Nilegfx