RESTful APIs - Introduction to Express

6 minute read

Learn how to write a RESTful API using Node.js and Express.

In this article, you will learn to write a simple RESTful API using Node.js and Express.

This article will be useful if you are familiar with these concepts:

  • ES6 syntax: arrow function, object notation, string templates.
  • callbacks.
  • JSON format.

What we’ll cover:

  • Setting up the project.
  • Adding express.
  • Running the server.
  • Introducing middlewares.
  • jq

This article does not cover the design of RESTful APIs. We’ll cover that in another article. The aim of this article is to introduce you to the Express web framework and show you how great is for creating RESTful APIs. I use it often when I have to provide testing APIs for client’s integrations or when I’m developing full stack project and the front client needs to consume an API.

All sorts of microservices can be made with Node.js and Express, within a few minutes you can have a nice and small server listening for requests. In this article, I’ll go through the whole process of setting up the project and running the server for the first time.

Setting up the project

Let’s start by creating the project.

$ mkdir micro-server
$ cd micro-server
$ npm init -y

As you can see npm init creates a package.json, this is the configuration file for our project. It contains scripts and dependencies. The -y option tells npm to autocomplete the questions that otherwise would ask you. Let’s add some elements to our package.json file, open your favourite editor and add the author, the description and a start script.

{
  "name": "temp",
  "version": "1.0.0",
  "description": "A hello world example",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node ."
  },
  "keywords": [],
  "author": "S.Espinoza",
  "license": "ISC"
}

Adding Express.

To add express to the dependencies is straight forward by running npm install express --save. This will create and install express inside a node_modules folder and the --save option will save the stable version of Express to our dependencies in the package.json. With express added, we now proceed to create an index.js file and start using express.

$ touch index.js

Open your editor and put this in your file.

const express = require('express')
const app = express()

app.get('/user/:name', (req, res) => {
  return res.status(200).json({
    msg: `Hi ${req.params.name}`
  })
})

app.all('*', (req, res) => {
  return res.status(404).json({
    msg: 'These are not the droids you are looking for'
  })
})

app.listen(4000, () => console.log('listening in on 4000'))

In general, every method in express receives as arguments a path and a callback, except listen which receives the port to listen as the first argument. Once the request matches one of the methods, it executes its callback.

An instance of express (app) has may methods, with different arguments. Here are some of the most important.

method arguments Description
get (path, callback) match GET request
post (path, callback) match POST request
put (path, callback) match PUT request
delete (path, callback) match DELETE request
all (path, callback) match all request
listen (port, callback) start the server in port and then executes a callback

In our example

  • app.listen start the server to listen to requests on port 4000.
  • app.get match requests to localhost:4000/user/:name.
  • app.all match everything that doesn’t match all the previews methods.

Yes, the order in which you write you matches matter, they should be structured top to bottom according to it’s relevance.

The callback

In our callback for the main methods, excluding listen, the first parameter is the path which in our case is /user/:name and the second parameter is the callback that receives two arguments:

  • the request object req holds all the data that the user sends in this case, the params.
  • the response object res formats the response to be sent to the user.

Running the server.

Let’s run the server now. For that we could type node index.js or node . but as we added a start script in our package.json we can run our server with npm start. The message listening in on 4000 should appear if everything went well.

$ npm start

> micro-server@1.0.0 start /home/sam/micro-server
> node .

listening in on 4000

And now we can test it using curl.

$ curl localhost:4000/user/Alexander
{"msg":"Hi Alexander"}

Great! we are up and running. If we forgot to the add the param or if we try to place any other path we would get Owi One’s words:

$ curl localhost:4000/this/path/does/not/exist
{"msg":"These are not the droids you are looking for"}

Introducing middlewares.

Understanding middlewares

A middleware is a piece of code that executes sequentially before hitting the last callback to respond to the user, this is useful, for example, to parse the content in the request object or for authentication proposes. Here we’ll make a little example in our code and then we’ll add some useful Third-party libraries to use as middlewares.

A middleware is a function with the following structure:

(req, res, next) => { 
  /* do something*/
  next()
}

Once a middleware finish whatever it does, it should always call the next() function so that the next middleware is called. You can have multiple middlewares that performs different tasks.

app.get('/', (req, res, next) => { /* do something*/ next() },
             (req, res, next) => { /* do something*/ next() },
             (req, res)      => res.status(200).json({}))

For our example we’ll use something very simple: put the greeting message in the request object.

const formatGreetings = (req, res, next) => {
  req.greetings = `Hi ${req.params.name}, coffee?`
  next()
}

app.get('/user/:name', formatGreetings, (req, res) => {
  return res.status(200).json({ msg: req.greetings })
})

app.all('*', (req, res) => {
  return res.status(404).json({
    msg: 'These are not the droids you are looking for' 
  })
})

app.listen(4000, () => console.log('listening in on 4000'))

If we test it again

$ curl localhost:4000/user/Alexander
{"msg":"Hi Alexander, coffee?"}

Excellent!

Third-party libraries

Our example as it is wont handle Cross-Origin Resource Sharing (CORS) nor will format well the body of POST requests. So we need to add two more libraries to our dependencies: cors and body-parser.

$ npm install cors body-parser --save

Let’s add a new POST match just before our GET.

const express = require('express')
const app = express()
const bparser = require('body-parser')
const cors = require('cors')
app.use(cors())
app.use(bparser.json({ type: 'application/json' })) // parse JSON
app.use(bparser.urlencoded({ extended: true })) // parse URL-encoded

const formatGreetings = (req, res, next) => {
  req.greetings = `Hi ${req.params.name}, coffee?`
  next()
}

app.post('/', (req, res) => {
  return res.status(200).json({
    msg: `creating user ${req.body.name}`
  })
})

app.get('/user/:name', formatGreetings, (req, res) => {
  return res.status(200).json({
    msg: `Hi ${req.params.name}`
  })
})

app.all('*', (req, res) => {
  return res.status(404).json({
    msg: 'These are not the droids you are looking for'
  })
})

app.listen(4000, () => console.log('listening in on 4000'))

Now if we test it.

$ curl -s -XPOST -H 'Content-type: application/json' \
  -d '{ "name": "sam" }' \
  localhost:4000/
{"msg":"creating user sam"}

Perfect. The app.use() call tells express to use a top-level middleware before every request. In this case to parse JSON and URL-encoded.

The urlencoded option in the body-parser just tells the middleware how to parse the body. Here I quote from the docs.

A new body object containing the parsed data is populated on the request object after the middleware (i.e. req.body). This object will contain key-value pairs, where the value can be a string or array (when extended is false), or any type (when extended is true).

jq

If you are like me and you always consume APIs in the terminal, one of the best tools I’ve found is jq. It’s an excellent parser and if you have a huge JSON object it’s very useful. Here is an example.

$ curl -s localhost:4000/Alexander | jq .
{
  "msg": "Hi Alexander"
}
$ curl -s localhost:4000/Alexander | jq .msg
"Hi Alexander"

That’s it for this article, I really hope this was useful for you, if you think there’s a mistake in this article or if there’s room to improve it let me know in the comments.

If you want me to talk about a specific topic just leave a comment or contact me through my page.

References

About Me

I’m a Software Engineer, writer, tech enthusiast, origami lover, amateur photographer, I love trees.

My tech: JavaScript, Node.js, React, Ruby, Bash, Docker.

You can follow me on X, LinkedIn or check on more articles.

If you liked this posts, please consider supporting me by buying me a coffee! :)