Initial commit
This commit is contained in:
318
commands/sng-endpoint.md
Normal file
318
commands/sng-endpoint.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# 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?"
|
||||
Reference in New Issue
Block a user