loading...
Cover image for 💻 Build a CRUD Express API In ⌚ five minutes!

💻 Build a CRUD Express API In ⌚ five minutes!

brandonkylebailey profile image Brandon Updated on ・6 min read

Interested in Express? Unsure how to get started with the MERN (MongoDB, Express.js, React.js, Node.js) ? This tutorial will get you up and running with a full CRUD (Create, Read, Update, DELETE) REST (Representational State Transfer) API in FIVE minutes! 🐼

Getting Started

This tutorial assumes you have nodejs and npm installed and configured, along with MongoDB. If not, leave a comment and I'll personally forward you my follow up post on Installing and Running Node.js

MVC 💎

Our application is going to follow the MVC (Model, View, Controller) design pattern

I will talk about design patterns In a later post but, for now, all you need to know is that the MVC pattern is divided In to three sections 📝 :

  • Model - Model represents an object carrying data. It can also have logic to update the controller if its data changes.
  • View - View represents the visualization of the data that model contains.
  • Controller - Controller acts on both model and view. It controls the data flow into model object and updates the view whenever data changes. It keeps view and model separate.

The Beginning ↙️

To begin, let's create a new directory to work from:

  • Navigate to your desired directory
  • Run mkdir my-express-app (to create your project folder) Alt Text
  • Run npm init -y (To initialize your node project) Alt Text
  • Create the following folders/files (or copy and paste the commands provided!)
    • mkdir -p ./server/models; mkdir ./server/controllers; mkdir ./server/routes
    • Then touch ./server/index.js; touch ./server/models/user.model.js; touch ./server/controllers/user.controller.js; touch ./server/routes/user.routes.js

Now your project should look like this! 👀

Alt Text

Creating the Server! 🌀

The server needs three things to get running:

  • Create a server application
  • Consume route middleware
  • Listen for requests

Let's break this down part by part

Create a server application

To begin with, we will need to install a few dependencies.

Open your terminal in the root of your project and run the following command:
npm install express mongoose
Alt Text
This installs two package dependencies. What are package dependencies? They are packages are APIs that can be used by your application to write code with. Your project then depends on this package to work (if you use the package).

Express is the API we are going to use to create our server, routes and controllers.
mongoose is an API that functions similarly to an ORM (Object Relational-mapper) that we are going to use to create our database model.

Open your ./server/index.js file and paste the following code

const express = require('express');

const app = express();

app.use(express.json());

app.get('/', (req, res) => {
    res.status(200).json({message: "Hello from my-express-app!"});
});

const PORT = 8080;

app.listen(PORT, () => {
    console.log(`Server listening at http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Line by line:

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

Imports the Express API so that we can use it's features in our application.

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

Constructs a new Express application that functions as our server.

app.use(express.json());
Enter fullscreen mode Exit fullscreen mode

Tells the Express application to use the JSON middleware (This is so we can see our request bodies with JSON)

app.get('/', (req, res) => {
    res.status(200).json({message: "Hello from my-express-app!"});
});
Enter fullscreen mode Exit fullscreen mode

Creates a GET route and sends an initial response.

Lastly, In our package.json file we need to add a script!
"server": "node server/index.js"
Alt Text

Running

npm run server
Enter fullscreen mode Exit fullscreen mode

In a terminal at the root of the project, then navigating to http://localhost:8080/ should show us:
Alt Text

The Controllers! 🔧

As we are creating a CRUD API, we need to create at least four controllers:

  • create
  • read
  • update
  • delete Go ahead and add the following code to ./server/controllers/user.controller.js
exports.createOneRequest = (req, res) => {
    res.status(201).json({message: "New resource created!"});
}

exports.readOneRequest = (req, res) => {
    res.status(302).json({message: "Resource found!"});
}

exports.updateOneRequest = (req, res) => {
    res.status(301).json({message: "Resource updated!"});
}

exports.deleteOneRequest = (req, res) => {
    res.status(202).json({message: "Resource deleted!"});
}
Enter fullscreen mode Exit fullscreen mode

Each function is responsible for a corresponding HTTP request, and returns the following appropriate response status code, along with some JSON data to look at!

  • 201 - Resource Created
  • 302 - Resource Found
  • 301 - Resource Moved Permanently
  • 202 - Resource Accepted

These are the controllers that will handle our requests!

The Routes! 🚲

Now we have our controllers, we need some routes for them to handle. We are going to use Express router to handle our CRUD routes!
In your ./server/routes/user.routes.js add the following:

const express = require('express');
const urlRoutes = express.Router();

const controller = require('../controllers/user.controller');

urlRoutes.post('/', controller.createOneRequest);
urlRoutes.get('/:id', controller.readOneRequest);
urlRoutes.put('/:id', controller.updateOneRequest);
urlRoutes.delete('/:id', controller.deleteOneRequest);

module.exports = urlRoutes;

Enter fullscreen mode Exit fullscreen mode

Then add the following to your ./server/index.js file:

const userRouter = require('./routes/user.routes');
...
app.use('/users', userRouter);
...
Enter fullscreen mode Exit fullscreen mode

This mounts our newly created router to the /users sub route. Meaning any path we specify must be prepended with /users in order for the URL to be correct.

For example: http://localhost:8080/<SOME_OBJECT_ID> would be an example of a URL that would not work given our current project structure.
http://localhost:8080/users/<SOME_OBJECT_ID> Would be a correct URL as it has the /users prepended!

Now, navigating to any of the URLs should return a response that looks something like this!
Alt Text

Build and integrate the Models! :octocat:

We're almost at the final section of this tutorial. If you made it this far, congratulations! you're one step away from an awesome REST API 😉

Add the following code to your ./server/models/user.model.js file:

const mongoose = require('mongoose');

const UserModel = mongoose.model('User', 
    mongoose.Schema(
        {
            name: {
                type: String
            },
        },
        {timestamps: true}
    )
);

module.exports = UserModel;
Enter fullscreen mode Exit fullscreen mode

This creates a User Schema in your local MongoDB instance to be used.

Then, back in the ./server/controllers/user.controller.js file:

Replace the contents of the createOneRequest request with:

exports.createOneRequest = async (req, res) => {
    // req.body is for POST requests. Think 'body of the postman'
    // destruct the name value from the request body
    const {name} = req.body;

    // check if database already contains this name
    const foundUser = await UserModel.find({name});

    // if no user is found, we can add this user to the database.
    if(!foundUser || foundUser.length == 0) {
        const user = new UserModel({name});
        const response = await user.save();
        res.status(201).json(response);
    } else {
        res.status(409).json({message: "User already exists!"});
    }
}
Enter fullscreen mode Exit fullscreen mode

This controller now handles three things!

  • Check if a user already exists by the name provided.
  • If no user exists, create one
  • return response to client

Do the same for the readOneRequest:

exports.readOneRequest = async (req, res) => {
    // Best request is GET, we can get the ID from the request 
    // parameters.
    const {id} = req.params;

    // attempt to retrieve user
    const foundUser = await UserModel.findOne({_id: id});

    // return 404 if no user found, return user otherwise.
    if(!foundUser || foundUser.length == 0) {
        res.status(404).json({message: "User not found!"});
    } else {
        res.status(302).json(foundUser);
    }
}
Enter fullscreen mode Exit fullscreen mode

And for the putOneRequest:


exports.updateOneRequest = async (req, res) => {
    const {id} = req.body;
    const foundUser = await UserModel.findOne({_id: id});
    if(foundUser || foundUser.length == 0) {
        const response = await foundUser.updateOne({_id: id});
        res.status(301).json(response);
    } else {
    res.status(404).json({message: `User not found...`});
    }
}
Enter fullscreen mode Exit fullscreen mode

And lastly, the deleteOneRequest:

exports.deleteOneRequest = async (req, res) => {
    const {id} = req.params;
    const foundUser = await UserModel.findOne({_id: id});
    if(foundUser || foundUser.length == 0) {
        const response = await foundUser.deleteOne({_id: id});
        res.status(202).json(response);
    } else {
        res.status(404).json({message: `User not found...`});
    }
}
Enter fullscreen mode Exit fullscreen mode

Now that we have our CRUD operations built, all we need to do is configure the database and we are ready to go!

The Database connection! 📫

We need to open a connection to our Mongo Database so that our application can communicate with the database!

To do this, open your ./server/index.js script and add the following code:

...
const mongoose = require('mongoose');

const db = mongoose.connect('mongodb://localhost:27017/db', {
    useCreateIndex: true,
    useNewUrlParser: true,
    useUnifiedTopology: true
}).then((response) => {
    console.log('Connected to the database...');
    return response;
});
...
Enter fullscreen mode Exit fullscreen mode

Run npm run server and sit back and relish in the brand new Full CRUD Rest API you have majestically built! 🙌

Summary 👋

There you have it! a fully functioning, full featured CRUD methods Restful API! We went through constructing an express application, consuming middleware and routes, route controllers and finally database models. A very bus tutorial! 😫

Feel free to leave comments below, any feedback is welcome! Link me to some of the apps you've built from this!

I hope you enjoyed this tutorial and feel like checking out my other social media! This is my first post on this site to please be kind 😄

Discussion

pic
Editor guide
Collapse
bruno091 profile image
bRuNo091

I know it may sound irrelevant, but what is the benefit of [ -Virtualbox:] and how to apply it ?
Is it like virtual environment (venv) in python ?

Collapse
brandonkylebailey profile image
Brandon Author

Hey Bruno! Thats a great question!

I am running a virtual machine using Oracles VirtualBox. I use this as my sandbox for all of my dev work and sometimes even use virtual environments like venv and docker containers within this virtual machine!

It is somewhat similar to a virtual environment, however it differs because it is designed to function as a full virtualized operating system! You can install different Linux distros and even MacOS and use it as you would a regular computer.

If you want to check out what virtualbox is and how to use it I'll be making a post about it very soon!

Collapse
devwhoruns profile image
devwhoruns

With Win10 you can do in WSL2 whatever you are doing in Virtual Box I think

Thread Thread
brandonkylebailey profile image
Brandon Author

I like to keep my environments separated :)

Collapse
calag4n profile image
calag4n

Very cool post 👍 thanks.

Collapse
brandonkylebailey profile image
Brandon Author

You're very welcome Calagan! I hope I was able to provide some value for your time! 😄

Collapse
harrymckillen profile image
Harry McKillen

Hey Brandon, one small fix
exports.deleteOneRequest is missing the async in the above snippet, so if you run it, you get a syntax error with regards to the await below it.
HTH

Collapse
brandonkylebailey profile image
Brandon Author

Hey Harry! Thanks for commenting. I appreciate the keen eye and I've updated the code snippet ! 😄

Collapse
rickavmaniac profile image
rickavmaniac

Great post. One question: In the updateOneRequest function where the {slug} came from?

Collapse
brandonkylebailey profile image
Brandon Author

Hey Rick, Nice catch! that was an oversite on my part! it should read : {_id:id} not {slug}! my apologies!

Collapse
filoret265 profile image
Filoret

Hi, I think, some were in
/server/controllers/user.controller.js
is required "usermodel"
?

Collapse
brandonkylebailey profile image
Brandon Author

Hi Filoret, You're totally right, nice catch! I will update that section now ! 😄

Collapse
andrewbaisden profile image
Andrew Baisden

Nice post short and concise.

Collapse
brandonkylebailey profile image
Brandon Author

Thanks Andrew!

Collapse
brandonkylebailey profile image
Brandon Author

Thank you Max! I'm hoping to improve with each post 😅