DEV Community

Cover image for REST With Node.js
Rodrigo Rojas
Rodrigo Rojas

Posted on

REST With Node.js

I know, I know… I said I would post more often. In my defense, I moved to another city and it’s exhausting. However, I’m here now and my gift to you before the holidays is finishing up our node.js backend. Let’s dive in.

chappelle rick james gif

Give it to me baby

We’re going to expand our application so that it provides the same RESTful HTTP API as a json-server.

We’re not going to deeply examine Fielding's definition of REST or spend time pondering about what is and isn't RESTful. Instead, we’ll take a simplified view and concern ourselves with how RESTful APIs are understood in web applications.

One convention is to create the unique address for resources by combining the name of the resource type with the resource's unique identifier.

Let's assume that the root URL of our service is example.com/api.

If we define the resource type of person to be people, then the address of a person resource with the identifier 10, has the unique address example.com/api/people/10.
The URL for the entire collection of all note resources is example.com/api/people.

We can execute different operations on resources. The operation to be executed is defined by the HTTP verb:

URL verb functionality
people/10 GET fetches a single resource
people GET fetches all resources in the collection
people POST creates a new resource based on the request data
people/10 DELETE removes the identified resource
people/10 PUT replaces the entire identified resource with the request data
people/10 PATCH replaces a part of the identified resource with the request data

Fetching a Single Resource

I know I’ve been rambling about REST for a while, but I swear I wouldn’t do it if it weren’t important. Let’s circle back to our backend application and fetch a single resource.

app.get('/api/people/:id', (request, response) => {
  const id = Number(request.params.id)
  const person = people.find(p => p.id === id)


 if (person) {
   response.json(person)
 } else {
   response.status(404).end()
 }
})

Enter fullscreen mode Exit fullscreen mode

Lots to unpack here, so I’ll try to break it down.

  • app.get('/api/people/:id', ...) will handle all HTTP GET requests that are of the form /api/notes/SOMETHING, where SOMETHING is an arbitrary string.
  • The id parameter in the route of the request is accessed through the request object.
  • The Number constructor wraps our id parameter and turns it into an integer (This is just in case our id turns out to be a string, we’re thinking preemptively here).
  • Our if block leverages the fact that all JavaScript objects are truthy, meaning that it will evaluate to true in a comparison operation. However, undefined is falsy meaning that our block will evaluate to false. Thus sending an error status code if no person is found.

Delete a Resource

Deletion happens by making an HTTP DELETE request to the url of the resource.

Delete

Looking at you 2021

Since we just retrieved a single resource, this should be easy.

app.delete('/api/people/:id', (request, response) => {
  const id = Number(request.params.id)
  people = people.filter(p => p.id !== id)

  response.status(204).end()
})
Enter fullscreen mode Exit fullscreen mode

Once deletion of the resource is successful, meaning that the person exists and it is removed, we can respond to the request with the status code 204 no content and return no data with the response.

POST and Receiving Data

Ok, let's make it possible to add new people to the server. Adding a person happens by making an HTTP POST request to the address localhost:3001/api/people, and by sending all the information for the new person in the request body in the JSON format.

Post-it Malone

Not that kind of POST

To access the data easily, we’ll need the help of the express json-parser that is taken to use with command app.use(express.json()).

app.use(express.json())

//...


app.post('/api/people', (request, response) => {
 const person = request.body
 console.log(person)
 response.json(person)
})
Enter fullscreen mode Exit fullscreen mode
  • Here, the event handler function can access the data from the body property of the request object.

  • Without the json-parser, the body property would be undefined. The json-parser functions so that it takes the JSON data of a request, transforms it into a JavaScript object and then attaches it to the body property of the request object before the route handler is called.

For the time being, the application does not do anything with the received data besides printing it to the console and sending it back in the response.

Let's return to the application and finalize the handling of the request!

const generateId = () => {
  const maxId = people.length > 0
    ? Math.max(...people.map(p => p.id))
    : 0
  return maxId + 1
}

app.post('/api/people', (request, response) => {
  const body = request.body

  if (!body.name) {
    return response.status(400).json({ 
      error: 'name missing' 
    })
  }

  const person = {
    name: body.name,
    number: body.number,
    id: generateId(),
  }

  people = people.concat(person)

  response.json(person)
})

Enter fullscreen mode Exit fullscreen mode
  • We need a unique id for each new person so we find out the largest id number in the current list and assign it to the maxId variable. The id of the new person is then defined as maxId + 1.

  • If the received data is missing a value for the name property, the server will respond to the request with the status code 400 bad request

  • Calling return is crucial, otherwise the code will execute to the very end and a malformed person object gets saved to the application!

Here's how our backend looks now:

const express = require('express')
const app = express()
app.use(express.json())

let people = [
    {
      name: "Hannah Rickard",
      number: "06-51-99-56-83",
      id: 1
    },
    {
      name: "Hyun Namkoong",
      number: "10987654",
      id: 2
    },
    {
      name: "Courtney Martinez",
      number: "3691215",
      id: 3
    }
  ]

  app.get('/', (request, response) => {
      response.send('<h1>Phonebook</h1>')
  })

  app.get('/api/people', (request, response) => {
      response.json(people)
  })

app.get('/api/people/:id', (request, response) => {
  const id = Number(request.params.id)
  const person = people.find(p => p.id === id)


 if (person) {
   response.json(person)
 } else {
   response.status(404).end()
 }
})

app.delete('/api/people/:id', (request, response) => {
  const id = Number(request.params.id)
  people = people.filter(p => p.id !== id)

  response.status(204).end()
})

const generateId = () => {
  const maxId = people.length > 0
    ? Math.max(...people.map(p => p.id))
    : 0
  return maxId + 1
}

app.post('/api/people', (request, response) => {
  const body = request.body

  if (!body.name) {
    return response.status(400).json({ 
      error: 'name missing' 
    })
  }

  const person = {
    name: body.name,
    number: body.number,
    id: generateId(),
  }

  people = people.concat(person)

  response.json(person)
})

  const PORT = 3001
  app.listen(PORT, () => {
      console.log(`Server running on port ${PORT}`)
  })
Enter fullscreen mode Exit fullscreen mode

Antonio Banderas Laptop

You made it!

Aaaand we're done! We've managed to GET a single resource with the id. With that same logic, you were able to DELETE a resource. Finally, we received and persisted data with POST.

As we approach the holidays and the end of the year, I want to wish you all the best, and please, if you go shopping, return your shopping cart to its designated spot.

Resources

JSON Server
REST
Number Constructor
HTTP Status Codes
JSON Parser

Top comments (3)

Collapse
 
llbbl profile image
Logan Lindquist • Edited

Nice Post it Maloney 😀 Fellow photographer? ❤️

Collapse
 
crrojas88 profile image
Rodrigo Rojas

And yes! analog or bust!

Collapse
 
crrojas88 profile image
Rodrigo Rojas

lol my terrible attempt at humor