Deploying & Monitoring REST APIs

🔗 REST API Lesson 15 Advanced

Getting your REST API to production requires careful environment configuration, process management, and observability setup. This lesson covers Dockerization, cloud deployment, logging, and health checks.

1 Health Check Endpoint
JavaScript — Health & Readiness Checks
const mongoose = require("mongoose");

// Liveness probe — is the process alive?
app.get("/health", (req, res) => {
  res.json({
    status: "ok",
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    version: process.env.npm_package_version
  });
});

// Readiness probe — is the app ready to serve traffic?
app.get("/ready", async (req, res) => {
  const checks = {
    database: mongoose.connection.readyState === 1 ? "ok" : "fail"
  };
  const allReady = Object.values(checks).every(v => v === "ok");
  res.status(allReady ? 200 : 503).json({
    status: allReady ? "ready" : "not ready",
    checks
  });
});
2 Dockerfile for Production
Dockerfile — Multi-Stage Build
# Stage 1: Install deps
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# Stage 2: Production image
FROM node:20-alpine
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY src/ ./src/

ENV NODE_ENV=production
USER node
EXPOSE 3000
HEALTHCHECK --interval=30s CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "src/server.js"]
YAML — docker-compose.yml
version: "3.9"
services:
  api:
    build: .
    ports: ["3000:3000"]
    environment:
      - NODE_ENV=production
      - MONGODB_URI=mongodb://mongo:27017/mydb
      - JWT_SECRET=${JWT_SECRET}
    depends_on: [mongo]
    restart: unless-stopped

  mongo:
    image: mongo:7
    volumes:
      - mongo_data:/data/db
    restart: unless-stopped

volumes:
  mongo_data:
3 Structured Logging with Winston
JavaScript — Production Logging
const winston = require("winston");

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || "info",
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()   // structured JSON logs
  ),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: "logs/error.log", level: "error" }),
    new winston.transports.File({ filename: "logs/combined.log" })
  ]
});

// Request logger middleware
app.use((req, res, next) => {
  const start = Date.now();
  res.on("finish", () => {
    logger.info("HTTP request", {
      method: req.method, url: req.url,
      status: res.statusCode,
      duration: Date.now() - start + "ms",
      ip: req.ip
    });
  });
  next();
});
4 Code Challenge
Challenge: Deploy your REST API to Railway.app or Render.com (both have free tiers). Configure your environment variables through their dashboard, set up a MongoDB Atlas database, and verify your API is live at a public URL.