Back to Portfolio

Node.js API Development: From Zero to Production

Borifan Dabasa January 2025 10 min read

Building RESTful APIs with Node.js and Express has been a core part of my work as a MERN stack developer. After building APIs for e-commerce platforms, appointment systems, and crypto trackers, I've developed a solid workflow that I'm sharing today.

Why Node.js for APIs?

When I started backend development, I chose Node.js because:

Project Setup

Here's how I start every API project:

mkdir my-api && cd my-api
npm init -y
npm install express mongoose dotenv cors
npm install -D nodemon

Folder Structure

my-api/
├── config/
│   └── db.js           # Database connection
├── models/             # Mongoose models
├── routes/             # API routes
├── controllers/        # Business logic
├── middleware/         # Custom middleware
├── utils/              # Helper functions
├── .env                # Environment variables
└── server.js           # Entry point

1. Setting Up Express Server

// server.js
const express = require('express');
const cors = require('cors');
const dotenv = require('dotenv');
const connectDB = require('./config/db');

dotenv.config();
connectDB();

const app = express();

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

// Routes
app.use('/api/users', require('./routes/userRoutes'));
app.use('/api/products', require('./routes/productRoutes'));

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

2. Database Connection

// 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('MongoDB connection failed:', error.message);
    process.exit(1);
  }
};

module.exports = connectDB;

3. Creating Models

// models/User.js
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'Name is required'],
    trim: true
  },
  email: {
    type: String,
    required: [true, 'Email is required'],
    unique: true,
    lowercase: true
  },
  password: {
    type: String,
    required: [true, 'Password is required'],
    minlength: 6
  }
}, { timestamps: true });

module.exports = mongoose.model('User', userSchema);

4. Controllers Pattern

I separate business logic from routes:

// controllers/userController.js
const User = require('../models/User');

exports.getUsers = async (req, res) => {
  try {
    const users = await User.find().select('-password');
    res.json({ success: true, data: users });
  } catch (error) {
    res.status(500).json({ success: false, message: error.message });
  }
};

exports.createUser = async (req, res) => {
  try {
    const { name, email, password } = req.body;
    const user = await User.create({ name, email, password });
    res.status(201).json({ success: true, data: user });
  } catch (error) {
    res.status(400).json({ success: false, message: error.message });
  }
};

5. Routes

// routes/userRoutes.js
const express = require('express');
const router = express.Router();
const { getUsers, createUser } = require('../controllers/userController');

router.route('/')
  .get(getUsers)
  .post(createUser);

module.exports = router;

Best Practices I Follow

  1. Use async/await - Cleaner than callbacks
  2. Validate input - Never trust user data
  3. Hash passwords - Use bcrypt
  4. Rate limiting - Prevent abuse
  5. CORS properly - Whitelist domains
  6. Log errors - Use Winston or Morgan
  7. API versioning - /api/v1/users

Real-World Example: E-Commerce API

In my e-commerce project, I built:

GET    /api/products          # Get all products
GET    /api/products/:id      # Get single product
POST   /api/products          # Create product (admin)
PUT    /api/products/:id      # Update product (admin)
DELETE /api/products/:id      # Delete product (admin)

Deployment

I deploy my APIs on:

Check out my API projects on GitHub.

#NodeJS #ExpressJS #MongoDB #API #Backend