Contents

Using JSON Server for demos and prototypes

I build a lot of demos and prototypes. I often have the need to build a back-end API service quickly for these examples. To do this I use JSON server. JSON Server allows me to create a functional REST-ful API without writing any code.

Installing JSON Server

Let’s take a quick look how it works, starting with installing JSON Server. JSON Server is available as a NPM package and can be installed globally. We can use npm to install it.

1
$ npm install -g json-server

The -g intalls JSON Server as a globally available binary: json-server.

1
2
3
4
5
6
$ json-server --help
json-server [options] <source>

Options:

. . .

Configuring JSON Server

The JSON Server is configured using a JSON file in which uses a DSL to define API endpoints. We define each endpoint we want to be able to query, together with some sample data.

Let’s look at an example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "warehouses": [
    { "id": 1, "location": "NJ" },
    { "id": 2, "location": "OR" }
  ],
  "users": [
    { "id": 1, "firstName": "Amala", "lastName": "Smith", "warehouseId": 1  },
    { "id": 2, "firstName": "Beatrice", "lastName": "Jones", "warehouseId": 2 },
    { "id": 3, "firstName": "Charlie", "lastName": "Butler", "warehouseId": 2 }
  ],
  "items": [
    { "id": 1, "name": "Widget", "cost": "1.29", "warehouseId": 1 },
    { "id": 2, "name": "Woobly", "cost": "5.23", "warehouseId": 2 },
    { "id": 3, "name": "Woo", "cost": "10.99", "warehouseId": 1 }
  ]
}

This JSON defines three endpoints and some associated data for each. The first endpoint, warehouses, is a list of warehouse records by location. We’ve defined two warehouses: NJ and OR. The second endpoint, users, returns records with id, firstName, and lastName fields. A fourth field, warehouseId, is a reference to the id field in the warehouses endpoint, linking the two data sets. The third endpoint, items, returns items with the name and cost fields. Each record also has a reference using the warehouseId field to our previous warehouses endpoint.

Tip
If you’re anything like me and you regularly mess up JSON formatting then your new friend is JSON Lint.

Let’s add this JSON to a file, we’ll call it api.json, and run JSON Server.

Starting the JSON Server

We run the json-server binary and pass in our api.json file as a command line argument to execute it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ json-server --watch api.json

  \{^_^}/ hi!

  Loading api.json
  Done

  Resources
  http://localhost:3000/warehouses
  http://localhost:3000/users
  http://localhost:3000/items

  Home
  http://localhost:3000

  Type s + enter at any time to create a snapshot of the database
  Watching...
Tip
The --watch flag tells JSON Server to watch the api.json file for changes and to refresh the server when it changes.

We can now browse to http://localhost:3000/users/ and we’ll get a JSON response to our GET request containing all of the records from the users endpoint. We can also access individual records via their ID, browsing to http://localhost:3000/users/1 will return the first users record in our api.json file.

1
2
3
4
5
6
{
  "id": 1,
  "firstName": "Amala",
  "lastName": "Smith",
  "warehouseId": 1
}

The server also supports query parameters, for example http://localhost:3000/users?firstName=Amala, will return all of the users records with a firstName field of Amala.

Tip
The whole schema and every record can be returned via the /db endpoint: http://localhost:3000/db

You can also refer to linked records, for example to return all of the users associated with the OR warehouse we would query: http://localhost:3000/warehouses/2/users. Or to return all the items in the NJ warehouse: http://localhost:3000/warehouses/1/items:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[
  {
    "id": 1,
    "name": "Widget",
    "cost": "1.29",
    "warehouseId": 1
  },
  {
    "id": 3,
    "name": "Woo",
    "cost": "10.99",
    "warehouseId": 1
  }
]

There’s also a wide variety of other querying mechanisms using filters, pagination, slicing, operators, sorting and even full text search.

You’re also not limited to purely GET requests and can add data to our data sets.

Adding data

To add data (or delete it) we can use POST, PUT, PATCH, or DELETE requests to your API. This functionality uses lowdb, a Lodash-powered JSON database, to update your api.json file with new records.

Let’s POST a new user record.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ curl --header "Content-Type: application/json" \
    --request POST \
    --data '{"firstName":"Claire","lastName":"Standard","warehouseId":1}' \
    http://localhost:3000/users
{
  "firstName": "Claire",
  "lastName": "Standard",
  "warehouseId": 1,
  "id": 4
}

We’ve used curl to POST a new record to the /users endpoint. We’ve specified a Content-Type header of application/json, which is required to ensure the API knows our incoming request is JSON. We’ve then specified a JSON snippet that adds a new user record in the --data flag.

Note
The id values are not mutable. Any id value in the body of a PUT or PATCH request will be ignored. Only an id value set in a POST request will be honored, but only if the POST-ed id value is not already taken.

Now if you go to http://localhost:3000/users/4, you’ll get:

1
2
3
4
5
6
{
  "firstName": "Claire",
  "lastName": "Standard",
  "warehouseId": 1,
  "id": 4
}
Tip
You can also programmatically use JSON Server via JS, which is useful for auto-generating random data.

JSON Server has a bunch of other useful functions: you can add alias and additional routes, load schema’s from remote sources, add SSL, middleware, add static content, or use JSON Server itself in a Node app. It’s a useful addition to your toolkit if you need a quick and easy demo API.

Tip
If you’re interested in something code-based and more sophisticated then MirageJS might be for you.