Back to Blog

Full-Stack Development with Node.js and MongoDB

Full-Stack Development with Node.js and MongoDB

March 5, 2024

12 min read
Node.js
MongoDB
Backend
Database

Modern Backend Development

Modern backend development encompasses a wide range of technologies and practices. This guide focuses on building scalable APIs using Node.js and Express.

Basic Express Server Setup

import express from 'express';
import cors from 'cors';

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

app.get('/api/health', (req, res) => {
  res.json({ status: 'ok' });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Environment Configuration

To keep sensitive data secure and manage different environments (development, staging, production), use environment variables. Tools like dotenv allow you to load variables from a .env file.

// .env
PORT=3000
MONGO_URI=mongodb+srv://username:password@cluster.mongodb.net/mydb

// src/server.ts
import dotenv from 'dotenv';
dotenv.config();
const PORT = process.env.PORT || 3000;

Connecting to MongoDB with Mongoose

Mongoose provides a straightforward, schema-based solution to model your application data and includes built-in type casting, validation, query building, and business logic hooks.

import mongoose from 'mongoose';

mongoose.connect(process.env.MONGO_URI!, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.error('MongoDB connection error:', err));

Error Handling Middleware

Centralizing error handling helps maintain clean and readable code. Create custom error handlers to catch and respond to errors in a consistent format.

// src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';

export function errorHandler(err: any, req: Request, res: Response, next: NextFunction) {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: {
      message: err.message || 'Internal Server Error',
    },
  });
}

// In your server setup:
app.use(errorHandler);

Testing with Jest and Supertest

Automated tests ensure your API behaves as expected. Jest is a powerful testing framework, and Supertest makes HTTP assertions easy.

import request from 'supertest';
import app from './server';

describe('GET /api/health', () => {
  it('should return status ok', async () => {
    const res = await request(app).get('/api/health');
    expect(res.status).toBe(200);
    expect(res.body).toEqual({ status: 'ok' });
  });
});

Deployment with Docker

Containerizing your application ensures consistency across environments. A simple Dockerfile can package your Node.js server.

# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
CMD ["node", "dist/server.js"]