api-endpoint-builder
Builds production-ready REST API endpoints with validation, error handling, authentication, and documentation. Follows best practices for security and scalability.
- category
- development
- risk
- safe
- source
- community
- date added
- 2026-03-05
API Endpoint Builder
Build complete, production-ready REST API endpoints with proper validation, error handling, authentication, and documentation.
When to Use This Skill
- User asks to "create an API endpoint" or "build a REST API"
- Building new backend features
- Adding endpoints to existing APIs
- User mentions "API", "endpoint", "route", or "REST"
- Creating CRUD operations
What You'll Build
For each endpoint, you create:
- Route handler with proper HTTP method
- Input validation (request body, params, query)
- Authentication/authorization checks
- Business logic
- Error handling
- Response formatting
- API documentation
- Tests (if requested)
Endpoint Structure
1. Route Definition
// Express example router.post('/api/users', authenticate, validateUser, createUser); // Fastify example fastify.post('/api/users', { preHandler: [authenticate], schema: userSchema }, createUser);
2. Input Validation
Always validate before processing:
const validateUser = (req, res, next) => { const { email, name, password } = req.body; if (!email || !email.includes('@')) { return res.status(400).json({ error: 'Valid email required' }); } if (!name || name.length < 2) { return res.status(400).json({ error: 'Name must be at least 2 characters' }); } if (!password || password.length < 8) { return res.status(400).json({ error: 'Password must be at least 8 characters' }); } next(); };
3. Handler Implementation
const createUser = async (req, res) => { try { const { email, name, password } = req.body; // Check if user exists const existing = await db.users.findOne({ email }); if (existing) { return res.status(409).json({ error: 'User already exists' }); } // Hash password const hashedPassword = await bcrypt.hash(password, 10); // Create user const user = await db.users.create({ email, name, password: hashedPassword, createdAt: new Date() }); // Don't return password const { password: _, ...userWithoutPassword } = user; res.status(201).json({ success: true, data: userWithoutPassword }); } catch (error) { console.error('Create user error:', error); res.status(500).json({ error: 'Internal server error' }); } };
Best Practices
HTTP Status Codes
200- Success (GET, PUT, PATCH)201- Created (POST)204- No Content (DELETE)400- Bad Request (validation failed)401- Unauthorized (not authenticated)403- Forbidden (not authorized)404- Not Found409- Conflict (duplicate)500- Internal Server Error
Response Format
Consistent structure:
// Success { "success": true, "data": { ... } } // Error { "error": "Error message", "details": { ... } // optional } // List with pagination { "success": true, "data": [...], "pagination": { "page": 1, "limit": 20, "total": 100 } }
Security Checklist
- Authentication required for protected routes
- Authorization checks (user owns resource)
- Input validation on all fields
- SQL injection prevention (use parameterized queries)
- Rate limiting on public endpoints
- No sensitive data in responses (passwords, tokens)
- CORS configured properly
- Request size limits set
Error Handling
// Centralized error handler app.use((err, req, res, next) => { console.error(err.stack); // Don't leak error details in production const message = process.env.NODE_ENV === 'production' ? 'Internal server error' : err.message; res.status(err.status || 500).json({ error: message }); });
Common Patterns
CRUD Operations
// Create POST /api/resources Body: { name, description } // Read (list) GET /api/resources?page=1&limit=20 // Read (single) GET /api/resources/:id // Update PUT /api/resources/:id Body: { name, description } // Delete DELETE /api/resources/:id
Pagination
const getResources = async (req, res) => { const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 20; const skip = (page - 1) * limit; const [resources, total] = await Promise.all([ db.resources.find().skip(skip).limit(limit), db.resources.countDocuments() ]); res.json({ success: true, data: resources, pagination: { page, limit, total, pages: Math.ceil(total / limit) } }); };
Filtering & Sorting
const getResources = async (req, res) => { const { status, sort = '-createdAt' } = req.query; const filter = {}; if (status) filter.status = status; const resources = await db.resources .find(filter) .sort(sort) .limit(20); res.json({ success: true, data: resources }); };
Documentation Template
/** * @route POST /api/users * @desc Create a new user * @access Public * * @body {string} email - User email (required) * @body {string} name - User name (required) * @body {string} password - Password, min 8 chars (required) * * @returns {201} User created successfully * @returns {400} Validation error * @returns {409} User already exists * @returns {500} Server error * * @example * POST /api/users * { * "email": "user@example.com", * "name": "John Doe", * "password": "securepass123" * } */
Testing Example
describe('POST /api/users', () => { it('should create a new user', async () => { const response = await request(app) .post('/api/users') .send({ email: 'test@example.com', name: 'Test User', password: 'password123' }); expect(response.status).toBe(201); expect(response.body.success).toBe(true); expect(response.body.data.email).toBe('test@example.com'); expect(response.body.data.password).toBeUndefined(); }); it('should reject invalid email', async () => { const response = await request(app) .post('/api/users') .send({ email: 'invalid', name: 'Test User', password: 'password123' }); expect(response.status).toBe(400); expect(response.body.error).toContain('email'); }); });
Key Principles
- Validate all inputs before processing
- Use proper HTTP status codes
- Handle errors gracefully
- Never expose sensitive data
- Keep responses consistent
- Add authentication where needed
- Document your endpoints
- Write tests for critical paths
Related Skills
@security-auditor- Security review@test-driven-development- Testing@database-design- Data modeling