🏠 Home / Hub

🟢 Node.js Lesson 05 — Complete REST API

← Back to Node.js Menu  |  🏠 Hub

1. REST API Design Rules

ActionMethodURLStatus
Get all usersGET/api/users200
Get one userGET/api/users/:id200 / 404
Create userPOST/api/users201
Update userPUT/api/users/:id200 / 404
Delete userDELETE/api/users/:id204 / 404
REST = Representational State Transfer — URL = resource, Method = action
URL ထဲ verb မသုံး: ❌ /getUser | ✅ GET /users/:id

2. Complete In-Memory CRUD API

// server.js — complete users API (no database yet)
const express = require('express');
const app = express();
app.use(express.json());

// In-memory "database"
let users = [
  { id: 1, name: 'Ko Ko',  email: 'ko@example.com',  role: 'admin' },
  { id: 2, name: 'Ma Ma',  email: 'ma@example.com',  role: 'user'  },
];
let nextId = 3;

// GET all — with optional search
app.get('/api/users', (req, res) => {
  const { search, role } = req.query;
  let result = [...users];

  if (search) result = result.filter(u =>
    u.name.toLowerCase().includes(search.toLowerCase()) ||
    u.email.toLowerCase().includes(search.toLowerCase())
  );
  if (role) result = result.filter(u => u.role === role);

  res.json({ data: result, total: result.length });
});

// GET one
app.get('/api/users/:id', (req, res) => {
  const user = users.find(u => u.id === Number(req.params.id));
  if (!user) return res.status(404).json({ error: 'User not found' });
  res.json(user);
});

// POST — create
app.post('/api/users', (req, res) => {
  const { name, email, role = 'user' } = req.body;

  // Validation
  if (!name || !email)
    return res.status(400).json({ error: 'name and email required' });
  if (users.find(u => u.email === email))
    return res.status(409).json({ error: 'Email already exists' });

  const user = { id: nextId++, name, email, role };
  users.push(user);
  res.status(201).json(user);
});

// PUT — update
app.put('/api/users/:id', (req, res) => {
  const idx = users.findIndex(u => u.id === Number(req.params.id));
  if (idx === -1) return res.status(404).json({ error: 'Not found' });

  users[idx] = { ...users[idx], ...req.body, id: users[idx].id };
  res.json(users[idx]);
});

// DELETE
app.delete('/api/users/:id', (req, res) => {
  const idx = users.findIndex(u => u.id === Number(req.params.id));
  if (idx === -1) return res.status(404).json({ error: 'Not found' });

  users.splice(idx, 1);
  res.status(204).end();
});

app.listen(3000, () => console.log('API at http://localhost:3000'));

3. Input Validation

// Manual validation helper
function validateUser(data) {
  const errors = [];

  if (!data.name || typeof data.name !== 'string')
    errors.push('name is required and must be string');
  if (data.name?.length < 2)
    errors.push('name must be at least 2 characters');
  if (!data.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email))
    errors.push('valid email is required');
  if (data.age && (isNaN(data.age) || data.age < 0 || data.age > 150))
    errors.push('age must be 0-150');

  return errors;
}

app.post('/api/users', (req, res) => {
  const errors = validateUser(req.body);
  if (errors.length) return res.status(400).json({ errors });
  // proceed...
});

// Or use express-validator package
// npm install express-validator
const { body, validationResult } = require('express-validator');

app.post('/api/users', [
  body('name').notEmpty().trim().isLength({ min: 2 }),
  body('email').isEmail().normalizeEmail(),
  body('age').optional().isInt({ min: 0, max: 150 })
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });
  // validated data available
});

4. Response Formatting Standard

// Consistent API response format
function success(res, data, statusCode = 200) {
  return res.status(statusCode).json({
    success: true,
    data,
    timestamp: new Date().toISOString()
  });
}

function error(res, message, statusCode = 400) {
  return res.status(statusCode).json({
    success: false,
    error: message,
    timestamp: new Date().toISOString()
  });
}

// Usage
app.get('/api/users', async (req, res) => {
  try {
    const users = await User.findAll();
    return success(res, users);
  } catch (err) {
    return error(res, err.message, 500);
  }
});

// Test with Thunder Client (VS Code) or curl:
// curl http://localhost:3000/api/users
// curl -X POST http://localhost:3000/api/users \
//   -H "Content-Type: application/json" \
//   -d '{"name":"Ko","email":"ko@test.com"}'
💡 Consistent response format → frontend ရေးတဲ့ developer ကို help ဖြစ် + easier debugging

← Node.js 04  |  Next: Node.js 06 → MySQL + .env →

📌 Study Checklist