From 5373ac6484f93f9748125a012d0cb7bcefc035d4 Mon Sep 17 00:00:00 2001 From: Mohammad Faiz Date: Thu, 5 Mar 2026 21:00:23 +0530 Subject: [PATCH] Create SKILL.md --- skills/api-endpoint-builder/SKILL.md | 324 +++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 skills/api-endpoint-builder/SKILL.md diff --git a/skills/api-endpoint-builder/SKILL.md b/skills/api-endpoint-builder/SKILL.md new file mode 100644 index 00000000..c24d8c76 --- /dev/null +++ b/skills/api-endpoint-builder/SKILL.md @@ -0,0 +1,324 @@ +--- +name: api-endpoint-builder +description: "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 + +```javascript +// 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: + +```javascript +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 + +```javascript +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 Found +- `409` - Conflict (duplicate) +- `500` - Internal Server Error + +### Response Format + +Consistent structure: + +```javascript +// 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 + +```javascript +// 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 + +```javascript +// 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 + +```javascript +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 + +```javascript +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 + +```javascript +/** + * @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 + +```javascript +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