Dev Dissection — Week 2: Connecting Your API to MongoDB

Welcome back to Dev Dissection!
Last week, we built a simple TODO API using Node.js, Express, and TypeScript. This week, we’re going to persist our data by integrating a real database — MongoDB.

By the end of this lesson, your TODOs won’t vanish when the server restarts.

Prerequisites

Make sure you have:

  • MongoDB installed locally or a free MongoDB Atlas cluster set up
  • MongoDB compass or any tool that helps navigating DB
  • The Week 1 TODO API working
  • A .env file ready for storing secrets
  • Basic familiarity with command line and JavaScript

SQL vs NoSQL (Quick Breakdown)

SQL (Relational):
Structured tables (like Excel sheets) with rows and columns. You define a schema up front.
Examples: MySQL, PostgreSQL.

NoSQL (Non-relational):
Flexible, nested documents (JSON-like). You can have varying data structures.
Example: MongoDB.

FeatureSQLNoSQL (MongoDB)
SchemaStrict, pre-definedFlexible, dynamic
StructureTables, rowsCollections, documents
Query LangSQLBSON-based (JSON-like)

MongoDB is great when data structures evolve rapidly — perfect for early-stage projects and agile development.

Let’s Set Up MongoDB with Mongoose

Step 1: Install dependencies

npm install mongoose dotenv
npm install -D @types/mongoose

Step 2: Create a .env file

In your project root, add:

MONGO_URI=mongodb://localhost:27017/todos-dev

Step 3: Update your src/index.ts

import express, { Request, Response } from 'express';
import cors from 'cors';
import mongoose from 'mongoose';
import dotenv from 'dotenv';

dotenv.config();

const app = express();
const PORT = 4000;

app.use(cors());
app.use(express.json());

// Connect to MongoDB
mongoose
  .connect(process.env.MONGO_URI!)
  .then(() => console.log('Connected to MongoDB'))
  .catch((err) => console.error('MongoDB connection error:', err));

// Define interfaces for request bodies
interface CreateTodoBody {
  task?: string;
}

interface UpdateTodoBody {
  task?: string;
  completed?: boolean;
}

// Define a Mongoose schema and model
const todoSchema = new mongoose.Schema(
  {
    task: { type: String, required: true },
    completed: { type: Boolean, default: false },
  },
  { timestamps: true },
);

const Todo = mongoose.model('Todo', todoSchema);

// CRUD Routes

// Create
app.post(
  '/todos',
  async (req: Request<{}, {}, CreateTodoBody>, res: Response) => {
    try {
      const task = req.body.task;
      if (!task) {
        res.status(400).json({ error: 'Task is required' });
        return;
      }
      const newTodo = await Todo.create({ task: task });
      res.status(201).json(newTodo);
    } catch (err) {
      res.status(400).json({ error: 'Failed to create TODO' });
    }
  },
);

// Read
app.get('/todos', async (_req: Request, res: Response) => {
  const todos = await Todo.find();
  res.status(200).json(todos);
});

// Update
app.put(
  '/todos/:id',
  async (req: Request<{ id: string }, {}, UpdateTodoBody>, res: Response) => {
    try {
      if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
        res.status(400).json({ error: 'Invalid ID format' });
        return;
      }

      if (!req.body) {
        res.status(400).json({ error: 'Please send correct inputs' });
        return;
      }

      const { task, completed } = req.body;

      if (!task || completed) {
        res.status(400).send('Please send correct inputs');
        return;
      }

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

      if (!updated) {
        res.status(404).json({ error: 'Not found' });
        return;
      }
      res.json(updated);
    } catch (err) {
      console.log(err);
      res.status(400).json({ error: 'Failed to update TODO' });
    }
  },
);

// Delete
app.delete(
  '/todos/:id',
  async (req: Request<{ id: string }>, res: Response) => {
    if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
      res.status(400).json({ error: 'Invalid ID format' });
      return;
    }
    const deleted = await Todo.findByIdAndDelete(req.params.id);
    if (!deleted) {
      res.status(404).json({ error: 'Not found' });
      return;
    }
    res.status(204).send('Todo was completed successfully');
  },
);

app.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`);
});

Try it Out

Start your server:

npm run dev

Test endpoints using:


What You Learned

This week, you:

  • Learned SQL vs NoSQL basics
  • Set up and configured MongoDB
  • Used Mongoose to define models
  • Built a persistent TODO API

Coming Up: React Frontend

In Week 3, we’ll:

  • Build a React UI to interact with your API
  • Use Axios or fetch to get data
  • Show and update TODOs live