DEV Community

Bipon Biswas
Bipon Biswas

Posted on

Build a REST API with Node JS & Express for Beginner

We can create web server using HTTP module

const http = require('http');

const server = http.createServer((req, res) => {
    if(req.url === '/'){
        res.write('Hellow world');
        res.end();
    }
    if(req.url === '/api/customers'){
        res.write(JSON.stringify([1,2,3]));
        res.end();
    }
});

server.listen(3000);
console.log('Listening on port 3000...')
Enter fullscreen mode Exit fullscreen mode

Here we have a callback function that takes two parameters request and response and with this request object we can check the URL of of the incoming request we with this we can define various routes for our application.

So if you have a request for let's say slash API slash customers this is how we're going to respond to the client. Now while this approach certainly works it's not very maintainable because as we defined more routes for our application we need to add more if blocks in this callback function.

That's when a framework comes into the picture framework. A framework gives our application a proper structure so we can easily more routes while keeping our application code maintainable. Now there are various framework out there for building web application and web servers on top of note. The most popular one is Express.

It's also very fast lightweight and perfectly documented.

Now create a restapi folder. Let's go inside this folder and run npm init --yes. So now we have a package JSON file. Finally we can install npm i express.

Build web server using Express

Create new file index.js. Express module so we use our require function give it the name our module which is Express.

require('express') This returns a function we call that express.

const express = require('express')
Enter fullscreen mode Exit fullscreen mode

We need to call this function like this and as you can see this returns an object of type Express by convention we call this object app so we store the result in a constant called app

const app = express();
Enter fullscreen mode Exit fullscreen mode

So this represents our application now app object has bunch of useful methods we have methods like get post put delete

app.get()
app.post()
app.put()
app.delete()
Enter fullscreen mode Exit fullscreen mode

All these methods correspond HTTP verbs or HTTP methods.

You want to HTTP post request to an endpoint you would use app.post().

HTTP GET request so this method takes two arguments the first arguments is the pass or the URL so here I'm going to use slash to represent the route of the website. Now the second argument is the callback function this is the function that will be called when we have an HTTP GET request to this endpoint. So callback function shall have two arguments request request, response. So this goes to a code block now this request object has a bunch of useful properties that gives us information about the incoming request.

When we get an HTTP GET request to the root of our website you're gonna respond with Hello World message. So this is how we define a route we specify the path or the URL and callback function which is also called route handler.

Now finally we need to listen on a given point so we call app that listen we give a port number like 3000 and optionally we can pass a function that will be called when the application stats listening on the given port so once again we use the arrow function syntax to display something on the console.

app.listen(3000, () => console.log('Listening on port 3000'))
Enter fullscreen mode Exit fullscreen mode

Now back in the terminal and run node index.js

Listening port

Now let's define another route

app.get('/api/courses', (req, res) => {
    res.send([1,2,3])
})
Enter fullscreen mode Exit fullscreen mode

I'm gonna simply return an array of numbers so response that's send and it passed an array of three numbers.

Back in the terminal we have to stop this process and started again. So press ctrl + c one more time node index.js

In this implementation we don't have those if block we define new routes.

Express gives our application is skeleton is structure.

nodemon

So far you have noticed that every time we make a change to this code you have to go back in the terminal and stop this process and started again. I'm gonna show you better way we're gonna install node package called nodemon. Install the command npm i -g nodemon

Instead of running our application using node we use nodemon.
Back to the terminal and run nodemon index.js

using nodemon

Environment Variables

Now one thing we need to improve in this code is this hard-coded value for the port so we have used 3000 as number while this may work on you development machine it's unlikely that this gonna not work in production environment because when deploy this application to a hosting environment the port is dynamically assigned by the hosting environment. So we can't rely on 3000 to be available. So the way to fix this is by using an environment variable.

Outside the application I'm gonna show you how that works. In this application we need to read the value of this port environment variable and the way we do that is by using process object.

We have this global object called process this object has bunch of property.

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}`))
Enter fullscreen mode Exit fullscreen mode

The name of our environment variable in this case port so if this is set we're gonna use this otherwise we're gonna use 3000. Now we can store the result in a constant called port.

Finally let's delete this and finally we need to replace with port and also change message accordingly.

Handling GET request

Create a route to get a single course.

app.get('/api/courses/:id', (req, res)=> {
    res.send(req.params.id)
})
Enter fullscreen mode Exit fullscreen mode

Could be course ID or courseId but ID is shorter and more conventional now we had our route handler function.

Now we have two endpoints what to get all the courses and the other two get a single course.

app.get('/api/courses', (req, res) => {
    res.send([1,2,3])
})
// /api/courses/1
app.get('/api/courses/:id', (req, res)=> {
    res.send(req.params.id)
})
Enter fullscreen mode Exit fullscreen mode
app.get('/api/courses/:id', (req, res)=> {
    // res.send(req.params.id)
   const course = courses.find(c => c.id === parseInt(req.params.id))
   if(!course){
       res.status(404).send('The course with the give ID was not found')
   }
   res.send(course)

})
Enter fullscreen mode Exit fullscreen mode

Handling POST request

So far we have created two routes that respond HTTP GET request and use this route to get all the courses as well as single course.

We use an HTTP POST request to create a new course.

app.post('/api/courses', (req, res) => {

})
Enter fullscreen mode Exit fullscreen mode

In this route handler we need to read the course object that should be in the body of the request use these properties to create a new course object and then add that course object to our courses array.

app.post('/api/courses', (req, res) => {
    const course = {
        id: courses.length + 1,
        name: req.body.name
    }
})
Enter fullscreen mode Exit fullscreen mode

We need to call app that app.use(express.json()). Adding a piece of middleware. To use this middleware in the request processing pipeline again.

app.post('/api/courses', (req, res) => {
    const course = {
        id: courses.length + 1,
        name: req.body.name
    }
    courses.push(course)
})
Enter fullscreen mode Exit fullscreen mode

And finally by convention when we post an object to the server when the server creates a new object or new resources you should return that object in the body of the response.

app.post('/api/courses', (req, res) => {
    const course = {
        id: courses.length + 1,
        name: req.body.name
    }
    courses.push(course)
    res.send(course)
})
Enter fullscreen mode Exit fullscreen mode

Post request
we can see the status of the request is 200 which means the request was handled successfully. Also see body of the response.

We send to the server so this is how we test HTTP service is in Postman.

We have assumed that there is an object with name property in the body of the request what if the client forgets to send this property or send an invalid name.

Input validation

As a security best practice you should never ever ever trust what client send you. You should always validate the input.

app.post('/api/courses', (req, res) => {
    if(!req.body.name || req.body.name.length < 3){
    }
})
Enter fullscreen mode Exit fullscreen mode

Doesn't exist or request that body the name that length is less than 3 then we're gonna an error to the client.

The restful convention is to return a response with the HTTP status code or 400 that means bad request.

app.post('/api/courses', (req, res) => {
    if(!req.body.name || req.body.name.length < 3){
        res.status(400).send('Name is required and should be minimum 3 character.')
    }   
    return;
    const course = {
        id: courses.length + 1,
        name: req.body.name
    }
    courses.push(course)
    res.send(course)
})
Enter fullscreen mode Exit fullscreen mode

validation with joi npm i joi
Store it in a constant called Joi

const Joi = require('joi')
Enter fullscreen mode Exit fullscreen mode

So we have this Joi class now packing our route handler now with Joi first we need to define a schema. Schema defines the shape of our objects.

const schema = {
        name: Joi.string().min(3).required();
    }
    Joi.validate(req.body, schema)
Enter fullscreen mode Exit fullscreen mode

This validate method returns an object let's store that in a constant result

Handling PUT request

Update a course so let's add a new route handler app we use the put method for updating resources.

app.put('/api/courses/:id', (req, res)=> {
    // look up the course
    // if not existing, return 404
    const course = courses.find(c => c.id === parseInt(req.params.id))
    if(!course){
        res.status(404).send('The course with the give ID was not found')
    }
    // validate the coruse
    // if invalid, return 400 - Bad request
    if(!req.body.name || req.body.name.length < 3){
        res.status(400).send('Name is required and should be minimum 3 character.');
        return;
    } 

    // update course
    // return the update course
   course.name = req.body.name;  
   res.send(course)
})
Enter fullscreen mode Exit fullscreen mode

Handling DELETE request

It is very simple and similar to what we have done so far. We need to find the index of this course in our courses array. We can use this splice method to remove and object from our courses array.

app.delete('/api/courses/:id', (req, res)=> {
    // look up the course
    // if not existing, return 404
    const course = courses.find(c => c.id === parseInt(req.params.id))
    if(!course){
        res.status(404).send('The course with the given ID was not found')
    }
    // delete course
    const index = courses.indexOf(course)
    console.log(index)
    courses.splice(index, 1)
    // if not delete otherse return the same course
   res.send(course)
})
Enter fullscreen mode Exit fullscreen mode

Delete course

index.js file

const Joi = require('joi')
const express = require('express');
const app = express();
app.use(express.json())

const courses = [
    {id: 1, name: 'course1'},
    {id: 2, name: 'course2'},
    {id: 3, name: 'course3'},
]

app.get('/', (req, res) => {
    res.send('Hello World!!')
})
app.get('/api/courses', (req, res) => {
    res.send(courses)
})
// /api/courses/1
app.get('/api/courses/:id', (req, res)=> {
    // res.send(req.params.id)
   const course = courses.find(c => c.id === parseInt(req.params.id))
   if(!course){
       res.status(404).send('The course with the given ID was not found')
   }
   res.send(course)

})
//http://localhost:3000/api/courses/2018/1
app.get('/api/courses/:year/:month', (req, res)=> {
    res.send(req.params)
})
// Query params:  http://localhost:3000/api/courses/2018/1?sortBy=name
app.get('/api/posts/:year/:month', (req, res)=> {
    res.send(req.query)
})

app.post('/api/courses', (req, res) => {
    // const schema = {
    //     name: Joi.string().min(3).required()
    // }
    // const result = Joi.validate(req.body, schema);
    // console.log(result);

    if(!req.body.name || req.body.name.length < 3){
        res.status(400).send('Name is required and should be minimum 3 character.');
        return;
    }   

    const course = {
        id: courses.length + 1,
        name: req.body.name
    }
    courses.push(course)
    res.send(course)
})


app.put('/api/courses/:id', (req, res)=> {
    // look up the course
    // if not existing, return 404
    const course = courses.find(c => c.id === parseInt(req.params.id))
    if(!course){
        res.status(404).send('The course with the given ID was not found')
    }
    // validate the coruse
    // if invalid, return 400 - Bad request
    if(!req.body.name || req.body.name.length < 3){
        res.status(400).send('Name is required and should be minimum 3 character.');
        return;
    } 

    // update course
    // return the update course
   course.name = req.body.name;  
   res.send(course)
})

app.delete('/api/courses/:id', (req, res)=> {
    // look up the course
    // if not existing, return 404
    const course = courses.find(c => c.id === parseInt(req.params.id))
    if(!course){
        res.status(404).send('The course with the given ID was not found')
    }

    // delete course
    const index = courses.indexOf(course)
    console.log(index)
    courses.splice(index, 1)
    // if not delete otherse return the same course
   res.send(course)
})


// port
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`))
Enter fullscreen mode Exit fullscreen mode

Top comments (0)