Mock RESTful Servers Fast With JSON Server

Haaris Chaudhry Technology Snapshot, Tutorial Leave a Comment

As a frontend developer, have you ever found yourself in a situation where the backend didn’t have a RESTful API that you could call to test out your user interface? Have you ever wanted to prototype an idea and found yourself getting down in the weeds setting up RESTful routes in a mock backend server?

That’s where json-server comes to the rescue!

With json-server, you simply create a JSON file that follows json-server’s conventions, and you can have a mock RESTful server up in no time. This blog post will go over the features of json-server that I have found most useful as a frontend developer.

Creating the “Database”

We’ll create a simple grade-book database that is intended to model a single grade-school class. The database will reflect the following schema:

The Subjects table represents the various subjects for the class. The Exams table represents the exams that a Subject may have. The Scores table holds records that contain a score that a student achieved on a particular exam. It facilitates the many-to-many relationship between students and exams.

Now that we know how the schema should look, let’s start creating the “database” using json-server. On your computer, in whatever directory you desire, execute the following command using the command line:

mkdir grade-book && cd grade-book

Now that you’re in the grade-book folder, make this an npm project by running:

npm init -y

Then, run the following command to install json-server:

npm install json-server

Creating the JSON File

With json-server installed, we can now work on creating a JSON file that represents our database. Create a file named db.json in the root of the project and add the following JSON to it:

{
   "subjects":[
  	{
     	  "id":1,
     	  "name":"math"
  	},
  	{
     	  "id":2,
     	  "name":"reading"
  	},
  	{
     	  "id":3,
     	  "name":"english"
  	},
  	{
     	  "id":4,
     	  "name":"science"
  	},
  	{
     	  "id":5,
     	  "name":"social studies"
  	}
   ]
}

This will represent our Subjects table (I’ll be using the term “table” loosely here). Each Subject record has two fields: name and id. Every record for every table that you create should have an id field, and by default, json-server identifies records by their id field.

You’ll see what I mean shortly, but for now, let’s start up json-server with our new “database” with the following command:

json-server --watch -p 4000 db.json

This will start up json-server on port 4000 (the port defaults to 3000 if you don’t use the -p flag to specify a port). The --watch flag will allow updates to the db.json file to occur in real-time as we use POST, PATCH, PUT, and DELETE requests.

Open up a browser and navigate to http://localhost:4000. You’ll be greeted by the json-server default main page where you can also see which resources are available to you. In this case, you’ll have the/subjects resource available to you with 5 items in it. Clicking on the /subjects link will redirect your browser to that resource, which can also be accessed by navigating to http://localhost:4000/subjects. Here, you’ll see all of the subject records in the Subjects table. This is what the json-server home page looks like:

And this is the result of navigating to http://localhost:4000/subjects in Firefox:

At this point, I would recommend using some sort of RESTful API tool to fire off requests to json-server.
I’ll be using Postman for the remainder of this tutorial, but you should be able to replicate my requests with the tool of your choosing.

HTTP Verbs

GET Requests

In Postman, let’s fire off a GET request to http://localhost:4000/subjects/2. This will give us a JSON response for the record in the Subjects table that has an id value that is equal to 2, as shown below.

This is where those id fields come in handy. If you send a GET, PUT, PATCH, or DELETE request to our running instance of json-server with an id value that doesn’t exist, it would result in a 404 error being returned.

In addition to the standard GET requests, json-server supports query parameters.

For example, if you send a GET request to http://localhost:4000/subjects?name=social%20studies, then you’ll get all of the records in the Subjects table that have a name equal to “social studies”.
Json-server has utility query parameters that can be used to prepare your data before returning it to you.

If you want your subjects returned in ascending alphabetical order, you can send a GET request to http://localhost:4000/subjects?_sort=name&_order=asc. The _sort query parameter is used to define which field to sort by, and the _order query parameter is used to define the order the results should be sorted in (either “asc” for ascending or “desc” for descending).

POST Requests

POST requests sent to json-server are expected to have some sort of JSON in the body of the request. In our case, we only have two fields for a subject: id and name. If you send a body with just the name field, then json-server will automatically create a new unique id.

For example, let’s send a POST request to http://localhost:4000/subjects with the following body (note: in Postman, make sure to select the “raw” radio button and select “JSON” from the dropdown menu):

{
  "name":"history"
}

This will create a new record in the Subjects table where the id field has been automatically set to 6 and the name field has been set to “history.”

See Also:  Part 5: Div​ing into the Vue.js SPA

If you send in the id as well, then you need to make sure it’s unique with respect to already existing subject records. Otherwise, json-server will respond with a 500 error.

PUT Requests

To replace a record with a new one, you target the id of that record with a PUT request that contains the replacement data in its body. Let’s send a PUT request to http://localhost:4000/subjects/6 with the following body:

{
  "name":"art",
  "duration":60
}

This will target the record with an id value of 6 and replace the rest of its properties with the body that we sent in with the PUT request.

PATCH Requests

A Patch request will replace particular fields in a record with those specified in the body. You can also add new fields to a record using a PATCH request. So, again, targeting our newly created record with an id value of 6, let’s send a PATCH request to http://localhost:4000/subjects/6 with the following body:

{
  "name": "music",
  "instrument": "xylophone"
}

This request will replace the name field value of “art” with “music”. It will also add a new field named instrument with a value of “xylophone”. The duration field will be left unchanged.

DELETE Requests

Delete requests are pretty simple. You just target the record that you want to delete by its id. Let’s send a DELETE request to http://localhost:4000/subjects/6. This will remove the record with an id field equal to 6.

Relationships

One-to-many relationships

Let’s see how we can implement the one-to-many relationship from subjects to exams as defined by our schema. Json-server can automatically create relationships between different tables. Let’s demonstrate this using an example. Replace the JSON of your db.json file with the JSON in the following link: https://raw.githubusercontent.com/in-the-keyhole/grade-book/master/db-one-to-many.json.

Because we used the --watch flag, your database should update automatically without restarting json-server. We’ve just added a new Exams table. Each record in this table references a record in the Subjects table by way of the subjectId field, which makes subjectId a foreign key. Json-server is smart enough to know that subjectId references a particular id in the Subjects table.

Let’s say that we want to find all of the exams associated with the “math” subject. We know that the id of the “math” subject record is 1, so we can find the associated exams by sending a GET request to http://localhost/subjects/1/exams. This will give us the three exam records that reference the “math” subject. No need for us to mock this route and write how the response will look because json-server has done all of the work for us! Here is the result:

Let’s say you want to find the exams associated with the “social studies” subject, but you don’t know the id of the “social studies” subject. Since we know the name, we can use query parameters to find the two exams associated with the “social studies” subject. Simply send a GET request to http://localhost:4000/subjects?name=social%20studies&_embed=exams. The _embed utility query parameter will embed children into our response. Since we set it equal to “exams,” we’ll get all of the exam children embedded in the subject that we queried.

Many-to-many relationships

Modeling many-to-many relationships is a little trickier in json-server. Let’s start by adding the Students and Scores tables to our db.json file. Modify the JSON in your db.json file so that it matches the contents of the following link: https://raw.githubusercontent.com/in-the-keyhole/grade-book/master/db-many-to-many.json

With the way json-server implements its database, we need an intermediate table in order to model the many-to-many relationship between students and exams. The Scores table functions as that intermediate table.

Let’s say you want to find which students scored less than 80 on the “short story” exam. The “short-story” exam record has an id value of 7, so we can get the data that we’re looking for by sending a GET request to http://localhost:4000/scores?_expand=student&examId=7&score_lte=80. The _expand query parameter can be used to get the full record being referenced by another record. Here, we’re getting the student record that is being referenced by each of the scores that are returned by our GET request. The _lte query parameter can be prefixed with a field name and then set to a value so that returned records are less than or equal to that value. In this case, the field name we’ve used is score and the value is 80.

Note: you can also obtain the previous result by sending a GET request to http://localhost:4000/exam/7/scores?_expand=student&score_lte=80

Using Middleware

What if you wanted to enforce a rule where students could only have one score per exam? Right now, json-server doesn’t prevent us from adding multiple score records with the same studentId and examId pair. We’ll need to add custom middleware to json-server so that it knows to check for invalid scores. To do this, we’ll need to move our json-server code to a file with the middleware in place and then have Node.js execute that file. We’re going to need the “cloneDeep” utility function from the lodash library before we can proceed, so let’s go ahead and install that:

npm install lodash.clonedeep

Then create a file named index.js at the root of your project with the following code:

const jsonServer = require("json-server");
const cloneDeep = require("lodash.clonedeep");
const server = jsonServer.create();
const router = jsonServer.router("db.json");
const middlewares = jsonServer.defaults();

let clonedScores = [];

const scoresCloner = (req, res, next) => {
  clonedScores = cloneDeep(router.db.get("scores").valueOf());
  next();
};

const checkScores = () => {
  const newScores = router.db.get("scores").valueOf();
  const examStudentIdPairs = {};
  newScores.forEach(s => {
    const examStudentIdPairKey = `${s.examId}${s.studentId}`;
	if (examStudentIdPairs[examStudentIdPairKey]) {
  	  router.db.set("scores", clonedScores).write();
  	  throw new Error(
    	    `A score with examId ${s.examId} and studentId ${s.studentId} already exists!`
  	  );
	}
    examStudentIdPairs[examStudentIdPairKey] = true;
  });
};

router.render = (req, res) => {
  checkScores();
  res.jsonp(res.locals.data);
};

// Add custom middleware before JSON Server router
server.use(middlewares);
server.use(scoresCloner);
server.use(router);
server.listen(4000, () => {
  console.log("JSON Server is running");
});

Let me explain what’s going on here. First, we do our imports. The most notable import is the router middleware import. The router import contains our mock database and uses lowdb internally (view on github).

const jsonServer = require("json-server");
const cloneDeep = require("lodash.clonedeep");
const server = jsonServer.create();
const router = jsonServer.router("db.json");
const middlewares = jsonServer.defaults();

Next, we instantiate a clonedScores variable that will be used to contain the values in the Scores table before a request gets a chance to modify it.

let clonedScores = [];

The next bit of code is where we create the scoresCloner middleware function, which will be used to clone the scores table into the clonedScores variable. We get the table by way of the db property from json-server’s router.

const scoresCloner = (req, res, next) => {
  clonedScores = cloneDeep(router.db.get("scores").valueOf());
  next();
};

We then create the checkScores function. This function will obtain the values from the modified Scores table and check them against clonedScores to make sure that we don’t have any scores with the same examId and studentId pair.

const checkScores = () => {
  const newScores = router.db.get("scores").valueOf();
  const examStudentIdPairs = {};
  newScores.forEach(s => {
	const examStudentIdPairKey = `${s.examId}${s.studentId}`;
	if (examStudentIdPairs[examStudentIdPairKey]) {
  	router.db.set("scores", clonedScores).write();
  	throw new Error(
    	`A score with examId ${s.examId} and studentId ${s.studentId} already exists!`
  	);
	}
	examStudentIdPairs[examStudentIdPairKey] = true;
  });
};

We get the Scores table after it has been modified by defining a function for the render method on json-server’s router. The render method is called by json-server internally right before a response is sent back to a client. We call checkScores in this method, which in turn, obtains the most recent version of the database by executing the following by way of router.db.get(“scores”).valueOf().

router.render = (req, res) => {
  checkScores();
  res.jsonp(res.locals.data);
};

If a duplicate pair is found when executing checkScores, we reset the database by using the set method on the database and overwriting the Scores table with whatever is in clonedScores. After resetting the database, we throw an error.

if (examStudentIdPairs[examStudentIdPairKey]) {
  	router.db.set("scores", clonedScores).write();
  	throw new Error(
    	`A score with examId ${s.examId} and studentId ${s.studentId} already exists!`
  	);
	}

The last few lines are where we pass in the default middleware, our custom middleware, and the router middleware (taking care to make sure that the router middleware is provided last). We then run the server on port 4000.

// Add custom middleware before JSON Server router
server.use(middlewares);
server.use(scoresCloner);
server.use(router);
server.listen(4000, () => {
  console.log("JSON Server is running");
});

Stop the execution of your current json-server instance. You’ll instead run json-server by executing the following command:

node index.js

Json-server will now throw an error when you try to post a new score with an examId/subjectId combo that already exists. You can test this by sending a POST request to http://localhost:4000/scores with the following body:

{
	"examId": 10,
	"studentId": 3,
	"score": 100
}

Which will result in an error being thrown by json-server.

See Also:  C# On The Client Side With Blazor

Admittedly, the method that I used to validate scores is heavy-handed. Copying the entire Scores table during each request isn’t very good for performance. However, I’ve found such methods to be fine for testing UI/RESTful API interactions because my mock datasets tend to be small, and I’m not firing off a lot of requests during the prototyping phase. I also wanted to show how to use middleware in json-server in order to get some sort of custom functionality. Hopefully, you’re using an actual application server and database by the time you’re working with large datasets and/or a large request load.

Final Thoughts

I’ve found json-server to be a very helpful tool when developing frontend applications, especially in the early stages when the backend services are still under development. I hope the examples I’ve provided will help you quickly set up a mock RESTful server that resembles what will be available in production.

Be sure to check out the docs on Github. If you want to gain a better understanding of how json-server handles data, you may also find these docs handy. Lastly, you can find the ending code used in this tutorial.

What Do You Think?