319 lines
7.1 KiB
Markdown
319 lines
7.1 KiB
Markdown
# Create API Endpoint Command
|
|
|
|
You are helping the user create a new API endpoint following Sngular's backend development best practices.
|
|
|
|
## Instructions
|
|
|
|
1. **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
|
|
|
|
2. **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)
|
|
|
|
3. **Determine API style**:
|
|
- REST API
|
|
- GraphQL (query/mutation/subscription)
|
|
- gRPC
|
|
- WebSocket
|
|
|
|
## Implementation Tasks
|
|
|
|
### For REST Endpoints:
|
|
|
|
1. **Create route handler** with:
|
|
- HTTP method and path
|
|
- Request validation middleware
|
|
- Business logic / controller method
|
|
- Response formatting
|
|
- Error handling
|
|
|
|
2. **Add request validation**:
|
|
- Query parameters validation
|
|
- Path parameters validation
|
|
- Request body validation (using Zod, Joi, class-validator)
|
|
- File upload validation (if needed)
|
|
|
|
3. **Implement authentication/authorization**:
|
|
- JWT token verification
|
|
- Role-based access control (RBAC)
|
|
- Permission checks
|
|
- API key validation
|
|
|
|
4. **Add error handling**:
|
|
- Try-catch blocks
|
|
- Custom error classes
|
|
- HTTP status codes
|
|
- Error response formatting
|
|
|
|
5. **Create tests**:
|
|
- Unit tests for controller logic
|
|
- Integration tests for full endpoint
|
|
- Mock database/external services
|
|
- Test authentication flows
|
|
|
|
6. **Add documentation**:
|
|
- OpenAPI/Swagger annotations
|
|
- JSDoc/docstrings
|
|
- Request/response examples
|
|
- Error codes documentation
|
|
|
|
### For GraphQL:
|
|
|
|
1. **Define schema**:
|
|
- Type definitions
|
|
- Input types
|
|
- Custom scalars
|
|
|
|
2. **Create resolver**:
|
|
- Query/Mutation/Subscription resolver
|
|
- Field resolvers
|
|
- DataLoader for N+1 prevention
|
|
|
|
3. **Add validation & auth**:
|
|
- Schema directives
|
|
- Resolver-level authorization
|
|
- Input validation
|
|
|
|
## Files to Create/Update
|
|
|
|
1. **Route/Controller file**: Define the endpoint handler
|
|
2. **Validation schema**: Request/response validation
|
|
3. **Service layer**: Business logic (separate from controller)
|
|
4. **Tests**: Comprehensive endpoint testing
|
|
5. **Types/Interfaces**: TypeScript types or Pydantic models
|
|
6. **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
|
|
```typescript
|
|
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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
/**
|
|
* @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?"
|