7.1 KiB
7.1 KiB
Create API Endpoint Command
You are helping the user create a new API endpoint following Sngular's backend development best practices.
Instructions
-
Detect the backend framework:
- Node.js with Express
- Node.js with Fastify
- NestJS
- Python with FastAPI
- Python with Flask/Django
- Go with Gin/Echo
- Other framework
-
Ask for endpoint details:
- HTTP method (GET, POST, PUT, PATCH, DELETE)
- Route path (e.g.,
/api/users,/api/posts/:id) - Purpose and description
- Request body schema (if applicable)
- Response schema
- Authentication required (yes/no)
- Rate limiting needed (yes/no)
-
Determine API style:
- REST API
- GraphQL (query/mutation/subscription)
- gRPC
- WebSocket
Implementation Tasks
For REST Endpoints:
-
Create route handler with:
- HTTP method and path
- Request validation middleware
- Business logic / controller method
- Response formatting
- Error handling
-
Add request validation:
- Query parameters validation
- Path parameters validation
- Request body validation (using Zod, Joi, class-validator)
- File upload validation (if needed)
-
Implement authentication/authorization:
- JWT token verification
- Role-based access control (RBAC)
- Permission checks
- API key validation
-
Add error handling:
- Try-catch blocks
- Custom error classes
- HTTP status codes
- Error response formatting
-
Create tests:
- Unit tests for controller logic
- Integration tests for full endpoint
- Mock database/external services
- Test authentication flows
-
Add documentation:
- OpenAPI/Swagger annotations
- JSDoc/docstrings
- Request/response examples
- Error codes documentation
For GraphQL:
-
Define schema:
- Type definitions
- Input types
- Custom scalars
-
Create resolver:
- Query/Mutation/Subscription resolver
- Field resolvers
- DataLoader for N+1 prevention
-
Add validation & auth:
- Schema directives
- Resolver-level authorization
- Input validation
Files to Create/Update
- Route/Controller file: Define the endpoint handler
- Validation schema: Request/response validation
- Service layer: Business logic (separate from controller)
- Tests: Comprehensive endpoint testing
- Types/Interfaces: TypeScript types or Pydantic models
- Documentation: API docs/Swagger definitions
Best Practices to Follow
Code Structure
src/
├── routes/
│ └── users.routes.ts # Route definitions
├── controllers/
│ └── users.controller.ts # Request handlers
├── services/
│ └── users.service.ts # Business logic
├── validators/
│ └── users.validator.ts # Input validation
├── types/
│ └── users.types.ts # TypeScript types
└── tests/
└── users.test.ts # Endpoint tests
Request Validation
import { z } from 'zod'
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
age: z.number().int().positive().optional(),
})
Error Handling
// Custom error classes
class BadRequestError extends Error {
statusCode = 400
}
class UnauthorizedError extends Error {
statusCode = 401
}
// Error handling middleware
app.use((err, req, res, next) => {
res.status(err.statusCode || 500).json({
error: {
message: err.message,
code: err.code,
},
})
})
Authentication
// JWT middleware
const authMiddleware = async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1]
if (!token) {
throw new UnauthorizedError('No token provided')
}
const decoded = jwt.verify(token, process.env.JWT_SECRET)
req.user = decoded
next()
}
Rate Limiting
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
})
app.use('/api/', limiter)
Response Formatting
// Success response
res.status(200).json({
success: true,
data: result,
meta: {
page: 1,
limit: 20,
total: 100,
},
})
// Error response
res.status(400).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid email format',
details: validationErrors,
},
})
Database Operations
// Use transactions for multiple operations
await db.transaction(async (trx) => {
const user = await trx('users').insert(userData)
await trx('profiles').insert({ user_id: user.id, ...profileData })
})
Logging
import logger from './utils/logger'
app.post('/api/users', async (req, res) => {
logger.info('Creating new user', { email: req.body.email })
try {
const user = await createUser(req.body)
logger.info('User created successfully', { userId: user.id })
res.status(201).json({ data: user })
} catch (error) {
logger.error('Failed to create user', { error, body: req.body })
throw error
}
})
Testing Example
import request from 'supertest'
import app from '../app'
describe('POST /api/users', () => {
it('creates a new user with valid data', async () => {
const response = await request(app)
.post('/api/users')
.send({
email: 'test@example.com',
name: 'Test User',
})
.expect(201)
expect(response.body.data).toHaveProperty('id')
expect(response.body.data.email).toBe('test@example.com')
})
it('returns 400 for invalid email', async () => {
await request(app)
.post('/api/users')
.send({
email: 'invalid-email',
name: 'Test User',
})
.expect(400)
})
it('requires authentication', async () => {
await request(app)
.post('/api/users')
.send({ email: 'test@example.com' })
.expect(401)
})
})
OpenAPI/Swagger Documentation
/**
* @swagger
* /api/users:
* post:
* summary: Create a new user
* tags: [Users]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - email
* - name
* properties:
* email:
* type: string
* format: email
* name:
* type: string
* responses:
* 201:
* description: User created successfully
* 400:
* description: Invalid input
* 401:
* description: Unauthorized
*/
Security Considerations
- Always validate and sanitize input
- Use parameterized queries to prevent SQL injection
- Implement rate limiting
- Use HTTPS in production
- Never expose sensitive data in responses
- Hash passwords with bcrypt
- Implement CORS properly
- Use security headers (helmet.js)
- Validate JWT tokens properly
- Implement proper session management
Ask the user: "What API endpoint would you like to create?"