# REST Endpoint Example

RESTful API endpoints

## Code Example

```javascript
// RESTful API Endpoints Example
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const { body, validationResult } = require('express-validator');

const app = express();

// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));

// Rate limiting
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per windowMs
  message: 'Too many requests from this IP, please try again later.'
});
app.use('/api/', limiter);

// Sample data (use database in production)
let users = [
  { id: 1, name: 'John Doe', email: 'john@example.com', age: 30, createdAt: new Date() },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com', age: 25, createdAt: new Date() }
];

let posts = [
  { id: 1, title: 'First Post', content: 'This is my first post', userId: 1, published: true },
  { id: 2, title: 'Second Post', content: 'This is my second post', userId: 1, published: false },
  { id: 3, title: 'Third Post', content: 'This is my third post', userId: 2, published: true }
];

// Validation middleware
const validateRequest = (req, res, next) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({
      error: 'Validation failed',
      details: errors.array()
    });
  }
  next();
};

// Error handling middleware
const errorHandler = (err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({
    error: 'Internal Server Error',
    message: process.env.NODE_ENV === 'development' ? err.message : 'Something went wrong'
  });
};

// User validation rules
const userValidationRules = [
  body('name').notEmpty().withMessage('Name is required'),
  body('email').isEmail().withMessage('Valid email is required'),
  body('age').optional().isInt({ min: 0, max: 120 }).withMessage('Age must be between 0 and 120')
];

const postValidationRules = [
  body('title').notEmpty().withMessage('Title is required'),
  body('content').notEmpty().withMessage('Content is required'),
  body('userId').isInt().withMessage('Valid user ID is required'),
  body('published').optional().isBoolean().withMessage('Published must be boolean')
];

// Helper functions
const findUserById = (id) => users.find(u => u.id === parseInt(id));
const findPostById = (id) => posts.find(p => p.id === parseInt(id));
const getNextId = (array) => Math.max(...array.map(item => item.id), 0) + 1;

// USER ENDPOINTS

// GET /api/users - Get all users with pagination and filtering
app.get('/api/users', (req, res) => {
  try {
    const { page = 1, limit = 10, search, sortBy = 'id', sortOrder = 'asc' } = req.query;
    const pageNum = parseInt(page);
    const limitNum = parseInt(limit);
    const offset = (pageNum - 1) * limitNum;

    let filteredUsers = [...users];

    // Search functionality
    if (search) {
      const searchLower = search.toLowerCase();
      filteredUsers = filteredUsers.filter(user => 
        user.name.toLowerCase().includes(searchLower) ||
        user.email.toLowerCase().includes(searchLower)
      );
    }

    // Sorting
    filteredUsers.sort((a, b) => {
      const aVal = a[sortBy];
      const bVal = b[sortBy];
      
      if (sortOrder === 'desc') {
        return bVal > aVal ? 1 : -1;
      }
      return aVal > bVal ? 1 : -1;
    });

    // Pagination
    const paginatedUsers = filteredUsers.slice(offset, offset + limitNum);
    const totalPages = Math.ceil(filteredUsers.length / limitNum);

    res.json({
      users: paginatedUsers,
      pagination: {
        page: pageNum,
        limit: limitNum,
        total: filteredUsers.length,
        totalPages,
        hasNext: pageNum < totalPages,
        hasPrev: pageNum > 1
      }
    });
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch users' });
  }
});

// GET /api/users/:id - Get user by ID
app.get('/api/users/:id', (req, res) => {
  const user = findUserById(req.params.id);
  
  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }

  res.json(user);
});

// POST /api/users - Create new user
app.post('/api/users', userValidationRules, validateRequest, (req, res) => {
  try {
    const { name, email, age } = req.body;

    // Check if email already exists
    const existingUser = users.find(u => u.email === email);
    if (existingUser) {
      return res.status(409).json({ error: 'Email already exists' });
    }

    const newUser = {
      id: getNextId(users),
      name,
      email,
      age: age || null,
      createdAt: new Date()
    };

    users.push(newUser);
    res.status(201).json(newUser);
  } catch (error) {
    res.status(500).json({ error: 'Failed to create user' });
  }
});

// PUT /api/users/:id - Update user
app.put('/api/users/:id', userValidationRules, validateRequest, (req, res) => {
  try {
    const user = findUserById(req.params.id);
    
    if (!user) {
      return res.status(404).json({ error: 'User not found' });
    }

    const { name, email, age } = req.body;

    // Check if email is being changed and if it's already taken
    if (email && email !== user.email) {
      const existingUser = users.find(u => u.email === email && u.id !== user.id);
      if (existingUser) {
        return res.status(409).json({ error: 'Email already exists' });
      }
    }

    // Update user
    Object.assign(user, { name, email, age });
    user.updatedAt = new Date();

    res.json(user);
  } catch (error) {
    res.status(500).json({ error: 'Failed to update user' });
  }
});

// DELETE /api/users/:id - Delete user
app.delete('/api/users/:id', (req, res) => {
  try {
    const userIndex = users.findIndex(u => u.id === parseInt(req.params.id));
    
    if (userIndex === -1) {
      return res.status(404).json({ error: 'User not found' });
    }

    // Delete user's posts
    posts = posts.filter(p => p.userId !== parseInt(req.params.id));
    
    // Delete user
    users.splice(userIndex, 1);

    res.json({ message: 'User deleted successfully' });
  } catch (error) {
    res.status(500).json({ error: 'Failed to delete user' });
  }
});

// POST ENDPOINTS

// GET /api/posts - Get all posts with filtering
app.get('/api/posts', (req, res) => {
  try {
    const { userId, published, page = 1, limit = 10 } = req.query;
    const pageNum = parseInt(page);
    const limitNum = parseInt(limit);
    const offset = (pageNum - 1) * limitNum;

    let filteredPosts = [...posts];

    // Filter by user
    if (userId) {
      filteredPosts = filteredPosts.filter(p => p.userId === parseInt(userId));
    }

    // Filter by published status
    if (published !== undefined) {
      const isPublished = published === 'true';
      filteredPosts = filteredPosts.filter(p => p.published === isPublished);
    }

    // Pagination
    const paginatedPosts = filteredPosts.slice(offset, offset + limitNum);
    const totalPages = Math.ceil(filteredPosts.length / limitNum);

    res.json({
      posts: paginatedPosts,
      pagination: {
        page: pageNum,
        limit: limitNum,
        total: filteredPosts.length,
        totalPages,
        hasNext: pageNum < totalPages,
        hasPrev: pageNum > 1
      }
    });
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch posts' });
  }
});

// GET /api/posts/:id - Get post by ID
app.get('/api/posts/:id', (req, res) => {
  const post = findPostById(req.params.id);
  
  if (!post) {
    return res.status(404).json({ error: 'Post not found' });
  }

  res.json(post);
});

// POST /api/posts - Create new post
app.post('/api/posts', postValidationRules, validateRequest, (req, res) => {
  try {
    const { title, content, userId, published = false } = req.body;

    // Check if user exists
    const user = findUserById(userId);
    if (!user) {
      return res.status(404).json({ error: 'User not found' });
    }

    const newPost = {
      id: getNextId(posts),
      title,
      content,
      userId: parseInt(userId),
      published: Boolean(published),
      createdAt: new Date()
    };

    posts.push(newPost);
    res.status(201).json(newPost);
  } catch (error) {
    res.status(500).json({ error: 'Failed to create post' });
  }
});

// PUT /api/posts/:id - Update post
app.put('/api/posts/:id', postValidationRules, validateRequest, (req, res) => {
  try {
    const post = findPostById(req.params.id);
    
    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    const { title, content, published } = req.body;

    // Update post
    Object.assign(post, { title, content, published: Boolean(published) });
    post.updatedAt = new Date();

    res.json(post);
  } catch (error) {
    res.status(500).json({ error: 'Failed to update post' });
  }
});

// DELETE /api/posts/:id - Delete post
app.delete('/api/posts/:id', (req, res) => {
  try {
    const postIndex = posts.findIndex(p => p.id === parseInt(req.params.id));
    
    if (postIndex === -1) {
      return res.status(404).json({ error: 'Post not found' });
    }

    posts.splice(postIndex, 1);
    res.json({ message: 'Post deleted successfully' });
  } catch (error) {
    res.status(500).json({ error: 'Failed to delete post' });
  }
});

// Apply error handling middleware
app.use(errorHandler);

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`REST API server running on port ${PORT}`);
});
```

## Files

- server.js
- routes/users.js
- routes/posts.js
- middleware/validation.js

## Usage

```bash
# Install dependencies
npm install

# Run the example
npm start
```
