{ Introduction to building JSON APIs. }


By the end of this chapter, you should be able to:

  • Explain what an API is
  • Test an API using curl and postman
  • Build a simple API with Express and Mongoose

API Intro / Terms

Since we are building an API, we are going to shift our mindset a bit in terms of how our server responds to requests. Instead of rendering HTML or redirecting, we will be responding with JSON using res.json. This means we will not have routes (also called endpoints) for rendering forms or any kind of server-side templating. This also means that for a resource like instructors, our routes are as follows:

HTTP Verb Path Description
GET /instructors Show all instructors
GET /instructors/:id Show a single instructor
POST /instructors Create an instructor
PATCH /instructors/:id Update an instructor
DELETE /instructors/:id Delete an instructor

But what about GET /instructors/:id/edit and GET /instructors/new? Remember, we are not doing any kind of rendering. Instead, our API will be listening directly for POST / PATCH and DELETE requests instead of requiring a form submission.

These requests will come in via AJAX or other servers making requests, but NOT by clicking on a link or submitting a form (unless the default event is prevented and AJAX is used).

Building an API

Since we're using Mongoose, we can easily respond with JSON just by using res.json.

So, all we need to do is respond with JSON and a status code.

Here is a guide to HTTP status codes:

Status Range Description Translation Example
1xx Information Wait a sec... 100 Continue
2xx Success Here you go! 201 Created
3xx Redirection Go away! 301 Moved Permanently
4xx Client Error You goofed up. 404 Not Found
5xx Server Error I goofed up. 502 Bad Gateway

Here are some of the most important that we will use:

  • 200 OK (for successful GET, PATCH, or DELETE)
  • 201 Created (for successful POST)
  • 400 Bad Request (for example, you POSTed malformed JSON or invalid keys)
  • 404 Not Found (for attempting to retrieve a resource that doesn't exist)
  • 409 Conflict (for trying to POST but the object already exists)
  • 500 Internal Server Error (if something goes wrong on our end)

Let's rebuild our pets app but without the views!

mkdir pets-app-api && cd pets-app-api
touch app.js
npm init -y
npm install express body-parser mongoose morgan # we don't need method-override or pug because we will not be dealing with any views/forms
mkdir routes models
touch models/{index,pet}.js
touch routes/pets.js

Let's start with our models/pet.js:

const mongoose = require("mongoose");
const petSchema = new mongoose.Schema({
  name: String

const Pet = mongoose.model("Pet", petSchema);

module.exports = Pet;

Next, our models/index.js:

const mongoose = require("mongoose");
mongoose.Promise = Promise;
mongoose.set("debug", true);
  .connect("mongodb://localhost/pets-app-api", {
    useMongoClient: true
  .then(() => {
    console.log("Connected to MongoDB");
  .catch(err => {

exports.Pet = require("./pet");

Now let's move to our routes/pets.js:

const express = require("express");
const router = express.Router();
const { Pet } = require("../models"); // by default, require will look for a file called index.js so we dont need ../models/index

router.get("/", (req, res, next) => {
  return Pet.find().then(pets => {
    // no rendering or redirecting!
    return res.json(pets);

router.get("/:id", (req, res, next) => {
  return Pet.findById(req.params.id).then(pet => {
    // no rendering or redirecting!
    return res.json(pet);

router.post("/", (req, res, next) => {
  return Pet.create(req.body).then(pet => {
    // 201 for created instead of 200 to be more specific
    return res.status(201).json(pet);

router.patch("/:id", (req, res, next) => {
  return Pet.findByIdAndUpdate(req.params.id, req.body).then(pet => {
    // no redirecting!
    return res.json(pet);

router.delete("/:id", (req, res, next) => {
  return Pet.findByIdAndRemove(req.params.id).then(pet => {
    return res.json({
      status: 200,
      title: "Success",
      message: "Successfully Deleted!"

module.exports = router;

Finally, let's finish things up with by writing our app.js:

// npm packages
const bodyParser = require("body-parser");
const express = require("express");
const morgan = require("morgan");
const petsRoutes = require("./routes/pets");

// globals
const app = express();
const PORT = process.env.PORT || 3000;

app.use(bodyParser.json()); // we need this to parse JSON!
app.use(bodyParser.urlencoded({ extended: true }));

app.use("/api/pets", petsRoutes);

// catch 404 and forward to error handler
app.use((req, res, next) => {
  const err = new Error("Not Found");
  err.status = 404;
  return next(err);

app.use((err, req, res, next) => {
  return res.status(err.status || 500).json({
    message: err.message,
    error: app.get("env") === "development" ? err : {}

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);

Using an API

To test our API we can use the curl command or a more visual tool like postman. Here are examples of what the curl commands you can issue to your server:

# add some pets
# make sure you set the appropriate header!
curl localhost:3000/api/pets -d '{"name": "Fido"}' -H "Content-type: application/json"
# {"__v":0,"name":"Fido","_id":"58c0479844de069cac98ccf1"}%
curl localhost:3000/api/pets -d '{"name": "Lassie"}' -H "Content-type: application/json"
# {"__v":0,"name":"Lassie","_id":"58c047af44de069cac98ccf2"}%

# get all pets
curl localhost:3000/api/pets
# [{"_id":"58c0479844de069cac98ccf1","name":"Fido","__v":0},{"_id":"58c047af44de069cac98ccf2","name":"Lassie","__v":0}]%

# get a single pet
curl localhost:3000/api/pets/58c0479844de069cac98ccf1
# {"_id":"58c0479844de069cac98ccf1","name":"Fido","__v":0}%

# update a pet - we need to set the verb!
curl localhost:3000/api/pets/58c0479844de069cac98ccf1 -X PATCH -d '{"name": "Marmaduke"}' -H "Content-type: application/json"
# {"_id":"58c0479844de069cac98ccf1","name":"Fido","__v":0}%
# note that by default we get the previous version of the pet. But it has been updated, you can verify this if you issue another GET

# delete a pet - we need to set the verb!
curl localhost:3000/api/pets/58c047af44de069cac98ccf2 -X DELETE
# no response data

# check the database again
curl localhost:3000/api/pets
# [{"_id":"58c0479844de069cac98ccf1","name":"Marmaduke","__v":0}]%

Sample application

You can see a sample application with JSON API here.

When you're ready, move on to Authentication with JSON APIs


Creative Commons License