DEV Community

Cover image for Day 34 of #100DaysOfCode — Creating a Database CRUD API
M Saad Ahmad
M Saad Ahmad

Posted on

Day 34 of #100DaysOfCode — Creating a Database CRUD API

Today, for Day 34 of #100DaysOfCode, the goal was to build a database api that saves the data in the MongoDB database using Mongoose schema and Node.js and Express.js routes.


TL;DR

The CRUD REST API was built using:

  • Node.js
  • Express
  • MongoDB
  • Mongoose

This project implements the four essential backend operations:

Create
Read
Update
Delete
Enter fullscreen mode Exit fullscreen mode

Project Folder Structure

To keep the code organized, I used a separation-of-concerns structure.

project/
│
├── models/
│   └── Todo.js
│
├── routes/
│   └── todoRoutes.js
│
├── controllers/
│   └── todoController.js
│
├── config/
│   └── db.js
│
├── server.js
├── .env
└── package.json
Enter fullscreen mode Exit fullscreen mode

This structure separates responsibilities:

Folder Responsibility
Models Database schema
Routes API endpoints
Controllers Business logic
Config Database connection
Server Application entry point

This pattern scales well for larger APIs and production apps.


Step 1 — Initialize the Project

Create a new Node project:

npm init -y
Enter fullscreen mode Exit fullscreen mode

Install required dependencies:

npm install express mongoose dotenv
Enter fullscreen mode Exit fullscreen mode
  • express — To handle routes
  • mongoose — For creating schema for MongoDB
  • dotenv — For creating environment variables

Install nodemon for auto server restarting (Optional):

npm install nodemon --save-dev
Enter fullscreen mode Exit fullscreen mode

If using nodemon, add this to package.json:

"scripts": {
  "dev": "nodemon server.js"
}
Enter fullscreen mode Exit fullscreen mode

Step 2 — Create the Express Server

File: server.js

const express = require("express")
const dotenv = require("dotenv")
const connectDB = require("./config/db")
const todoRoutes = require("./routes/todoRoutes")

dotenv.config()
connectDB()

const app = express()

app.use(express.json())
app.use("/api/todos", todoRoutes)

const PORT = process.env.PORT || 5000
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`)
})
Enter fullscreen mode Exit fullscreen mode

Environment Variables

Create a .env file.

MONGO_URI=your_mongodb_connection_string
PORT=5000
Enter fullscreen mode Exit fullscreen mode

Step 3 — Connect MongoDB

File: config/db.js

const mongoose = require("mongoose")

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGO_URI)

    console.log("MongoDB Connected")
  } catch (error) {
    console.error(error)
    process.exit(1)
  }
}

module.exports = connectDB
Enter fullscreen mode Exit fullscreen mode

Step 4 — Create the Mongoose Model

File: models/Todo.js

const mongoose = require("mongoose")

const todoSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true
  },

  completed: {
    type: Boolean,
    default: false
  },

  createdAt: {
    type: Date,
    default: Date.now
  }

})

module.exports = mongoose.model("Todo", todoSchema)
Enter fullscreen mode Exit fullscreen mode

This schema defines the Todo document structure in MongoDB.


Step 5 — Create Controllers (CRUD Logic)

File: controllers/todoController.js

const Todo = require("../models/Todo")

// Create Todo
exports.createTodo = async (req, res) => {
  try {
    const todo = await Todo.create(req.body)
    res.status(201).json(todo)
  } catch (error) {
    res.status(500).json({ error: error.message })
  }
}

// Get All Todos
exports.getTodos = async (req, res) => {
  try {
    const todos = await Todo.find()
    res.json(todos)
  } catch (error) {
    res.status(500).json({ error: error.message })
  }
}

// Get Single Todo
exports.getTodo = async (req, res) => {
  try {
    const todo = await Todo.findById(req.params.id)

    if (!todo) {
      return res.status(404).json({ message: "Todo not found" })
    }

    res.json(todo)

  } catch (error) {
    res.status(500).json({ error: error.message })
  }
}

// Update Todo
exports.updateTodo = async (req, res) => {
  try {

    const todo = await Todo.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true }
    )

    if (!todo) {
      return res.status(404).json({ message: "Todo not found" })
    }

    res.json(todo)

  } catch (error) {
    res.status(500).json({ error: error.message })
  }
}

// Delete Todo
exports.deleteTodo = async (req, res) => {
  try {

    const todo = await Todo.findByIdAndDelete(req.params.id)

    if (!todo) {
      return res.status(404).json({ message: "Todo not found" })
    }

    res.json({ message: "Todo deleted" })

  } catch (error) {
    res.status(500).json({ error: error.message })
  }
}
Enter fullscreen mode Exit fullscreen mode

These controllers contain the core business logic of the API.


Step 6 — Create Routes

File: routes/todoRoutes.js

const express = require("express")
const router = express.Router()
const todoController = require("../controllers/todoController")

router.post("/", todoController.createTodo)
router.get("/", todoController.getTodos)
router.get("/:id", todoController.getTodo)
router.put("/:id", todoController.updateTodo)
router.delete("/:id", todoController.deleteTodo)

module.exports = router
Enter fullscreen mode Exit fullscreen mode

Routes connect HTTP requests → controller logic.


REST API Endpoints

Method Endpoint Description
POST /api/todos Create a new todo
GET /api/todos Get all todos
GET /api/todos/:id Get a single todo
PUT /api/todos/:id Update a todo
DELETE /api/todos/:id Delete a todo

Testing the API

The API can be tested using:

  • Postman
  • Thunder Client (VS Code extension)

Example request:

Create Todo

POST /api/todos
Enter fullscreen mode Exit fullscreen mode

Body:

{
  "title": "Learn MERN stack"
}
Enter fullscreen mode Exit fullscreen mode

Practice extensions to try

Once CRUD works, try implementing these real-world API features.


Filtering

Example:

GET /api/todos?completed=true
Enter fullscreen mode Exit fullscreen mode

Pagination

Example:

GET /api/todos?page=1&limit=5
Enter fullscreen mode Exit fullscreen mode

Search

Example:

GET /api/todos?search=mern
Enter fullscreen mode Exit fullscreen mode

Validation

Example rule:

title must be at least 3 characters
Enter fullscreen mode Exit fullscreen mode

You can implement validation using:

  • Mongoose validation
  • Joi
  • Express-validator

What I Learned Today

Key backend concepts from this project:

  • Building a REST API with Express
  • Structuring a backend using MVC pattern
  • Connecting MongoDB with Mongoose
  • Implementing CRUD operations
  • Organizing code for scalable APIs

Conclusion

This project is a great starting point for backend development with the MERN stack.

Next improvements could include:

  • Authentication (JWT)
  • Middleware
  • Error handling middleware
  • Rate limiting
  • Logging

Thanks for reading. Feel free to share your thoughts!

Top comments (0)