Marketplace
backend-development-patterns
API design, database patterns, REST/GraphQL, microservices architecture, and backend best practices
$ Instalar
git clone https://github.com/Primadetaautomation/primadata-marketplace /tmp/primadata-marketplace && cp -r /tmp/primadata-marketplace/.claude/skills/backend-development-patterns ~/.claude/skills/primadata-marketplace// tip: Run this command in your terminal to install the skill
SKILL.md
name: backend-development-patterns description: API design, database patterns, REST/GraphQL, microservices architecture, and backend best practices triggers: [api, backend, REST, GraphQL, database, microservices, server, endpoint, PostgreSQL, Supabase] version: 1.0.0 agents: [senior-fullstack-developer, database-migration-specialist, solutions-architect] context_levels: minimal: Core API patterns and database best practices detailed: Complete architecture patterns and examples full: Working code examples and templates
Backend Development Patterns Skill
Overview
This skill provides comprehensive backend development patterns including API design, database architecture, authentication, and scalable backend systems.
When to Use This Skill
- Designing and implementing REST/GraphQL APIs
- Database schema design and optimization
- Backend service architecture
- Microservices patterns
- Data layer implementation
Core API Design Principles (Level 1 - Always Loaded)
REST API Best Practices
URL Structure:
✅ GOOD - Resource-oriented URLs
GET /api/users # List users
GET /api/users/:id # Get specific user
POST /api/users # Create user
PUT /api/users/:id # Update user (full replace)
PATCH /api/users/:id # Update user (partial)
DELETE /api/users/:id # Delete user
# Nested resources
GET /api/users/:id/orders # Get user's orders
POST /api/users/:id/orders # Create order for user
❌ BAD - Verb-based URLs
GET /api/getUser?id=123
POST /api/createUser
POST /api/updateUser
POST /api/deleteUser
HTTP Status Codes:
// Success responses
200 OK // Successful GET, PUT, PATCH, DELETE
201 Created // Successful POST
204 No Content // Successful DELETE (no response body)
// Client errors
400 Bad Request // Invalid input data
401 Unauthorized // Missing or invalid authentication
403 Forbidden // Valid auth but insufficient permissions
404 Not Found // Resource doesn't exist
409 Conflict // Resource conflict (e.g., duplicate email)
422 Unprocessable // Validation errors
// Server errors
500 Internal Error // Unexpected server error
503 Service Unavail // Temporary unavailability
// Example usage
app.post('/api/users', async (req, res) => {
try {
const user = await userService.create(req.body);
res.status(201).json(user); // 201 Created
} catch (error) {
if (error instanceof ValidationError) {
res.status(422).json({ error: error.message }); // 422 Validation
} else if (error instanceof ConflictError) {
res.status(409).json({ error: error.message }); // 409 Conflict
} else {
res.status(500).json({ error: 'Internal server error' }); // 500
}
}
});
Request/Response Format:
// ✅ GOOD - Consistent response structure
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: {
code: string;
message: string;
details?: any;
};
meta?: {
timestamp: string;
requestId: string;
};
}
// Success response
{
"success": true,
"data": {
"id": "123",
"email": "user@example.com",
"name": "John Doe"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"requestId": "req_abc123"
}
}
// Error response
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email format",
"details": {
"field": "email",
"value": "invalid-email"
}
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"requestId": "req_abc123"
}
}
Database Design Patterns
Schema Design Best Practices:
-- ✅ GOOD - Proper constraints and indexes
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- Index frequently queried fields
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_created_at ON users(created_at);
-- Foreign key relationships
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
total DECIMAL(10, 2) NOT NULL CHECK (total >= 0),
status VARCHAR(50) NOT NULL DEFAULT 'pending',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
-- ❌ BAD - No constraints, poor naming
CREATE TABLE user_table (
id INT,
mail VARCHAR(1000),
data TEXT
);
Query Optimization:
// ✅ GOOD - Efficient query with specific fields
async function getUserOrders(userId: string) {
return db.query(`
SELECT
o.id,
o.total,
o.status,
o.created_at
FROM orders o
WHERE o.user_id = $1
ORDER BY o.created_at DESC
LIMIT 20
`, [userId]);
}
// ❌ BAD - SELECT * and no limit
async function getUserOrders(userId: string) {
return db.query(`
SELECT * FROM orders WHERE user_id = $1
`, [userId]);
}
// ✅ GOOD - Avoid N+1 queries with JOIN
async function getUsersWithOrders() {
const result = await db.query(`
SELECT
u.id as user_id,
u.name,
u.email,
json_agg(
json_build_object(
'id', o.id,
'total', o.total,
'status', o.status
)
) as orders
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name, u.email
`);
return result.rows;
}
Repository Pattern
// Generic repository interface
interface Repository<T> {
findById(id: string): Promise<T | null>;
findAll(options?: FindOptions): Promise<T[]>;
create(data: Partial<T>): Promise<T>;
update(id: string, data: Partial<T>): Promise<T>;
delete(id: string): Promise<boolean>;
}
// Implementation for User entity
class UserRepository implements Repository<User> {
constructor(private db: Database) {}
async findById(id: string): Promise<User | null> {
const result = await this.db.query(
'SELECT * FROM users WHERE id = $1',
[id]
);
return result.rows[0] ? this.mapToUser(result.rows[0]) : null;
}
async findByEmail(email: string): Promise<User | null> {
const result = await this.db.query(
'SELECT * FROM users WHERE email = $1',
[email]
);
return result.rows[0] ? this.mapToUser(result.rows[0]) : null;
}
async create(data: CreateUserDto): Promise<User> {
const result = await this.db.query(
`INSERT INTO users (email, name, password_hash)
VALUES ($1, $2, $3)
RETURNING *`,
[data.email, data.name, data.passwordHash]
);
return this.mapToUser(result.rows[0]);
}
async update(id: string, data: Partial<User>): Promise<User> {
const updates: string[] = [];
const values: any[] = [];
let paramCount = 1;
Object.entries(data).forEach(([key, value]) => {
updates.push(`${key} = $${paramCount}`);
values.push(value);
paramCount++;
});
values.push(id);
const result = await this.db.query(
`UPDATE users
SET ${updates.join(', ')}, updated_at = NOW()
WHERE id = $${paramCount}
RETURNING *`,
values
);
return this.mapToUser(result.rows[0]);
}
private mapToUser(row: any): User {
return {
id: row.id,
email: row.email,
name: row.name,
createdAt: row.created_at,
updatedAt: row.updated_at,
};
}
}
Service Layer Pattern
// Business logic layer
class UserService {
constructor(
private userRepo: UserRepository,
private emailService: EmailService,
private cacheService: CacheService,
private logger: Logger
) {}
async createUser(dto: CreateUserDto): Promise<User> {
// 1. Validate input
this.validateUserDto(dto);
// 2. Check for duplicates
const existing = await this.userRepo.findByEmail(dto.email);
if (existing) {
throw new ConflictError('Email already registered');
}
// 3. Hash password
const passwordHash = await bcrypt.hash(dto.password, 12);
// 4. Create user
const user = await this.userRepo.create({
email: dto.email,
name: dto.name,
passwordHash,
});
// 5. Send welcome email (async, don't block)
this.emailService.sendWelcomeEmail(user.email)
.catch(err => this.logger.error('Failed to send welcome email', err));
// 6. Invalidate cache
await this.cacheService.delete(`users:all`);
// 7. Log event
this.logger.info('User created', { userId: user.id });
return user;
}
async getUserById(id: string): Promise<User> {
// Try cache first
const cacheKey = `user:${id}`;
const cached = await this.cacheService.get<User>(cacheKey);
if (cached) {
return cached;
}
// Fetch from database
const user = await this.userRepo.findById(id);
if (!user) {
throw new NotFoundError('User not found');
}
// Cache for 5 minutes
await this.cacheService.set(cacheKey, user, 300);
return user;
}
private validateUserDto(dto: CreateUserDto): void {
if (!validator.isEmail(dto.email)) {
throw new ValidationError('Invalid email format');
}
if (dto.password.length < 8) {
throw new ValidationError('Password must be at least 8 characters');
}
if (dto.name.trim().length < 2) {
throw new ValidationError('Name must be at least 2 characters');
}
}
}
Authentication & Authorization
JWT Authentication
import jwt from 'jsonwebtoken';
interface JwtPayload {
userId: string;
email: string;
role: string;
}
class AuthService {
private readonly JWT_SECRET = process.env.JWT_SECRET!;
private readonly JWT_EXPIRES_IN = '1h';
private readonly REFRESH_TOKEN_EXPIRES_IN = '7d';
generateAccessToken(user: User): string {
const payload: JwtPayload = {
userId: user.id,
email: user.email,
role: user.role,
};
return jwt.sign(payload, this.JWT_SECRET, {
expiresIn: this.JWT_EXPIRES_IN,
algorithm: 'HS256',
});
}
generateRefreshToken(user: User): string {
return jwt.sign(
{ userId: user.id },
this.JWT_SECRET,
{ expiresIn: this.REFRESH_TOKEN_EXPIRES_IN }
);
}
verifyToken(token: string): JwtPayload {
try {
return jwt.verify(token, this.JWT_SECRET) as JwtPayload;
} catch (error) {
throw new UnauthorizedError('Invalid or expired token');
}
}
}
// Middleware
function authenticateJWT(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing authentication token' });
}
const token = authHeader.substring(7);
try {
const payload = authService.verifyToken(token);
req.user = payload;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
}
// Authorization middleware
function requireRole(...roles: string[]) {
return (req: Request, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// Usage
app.get('/api/admin/users', authenticateJWT, requireRole('admin'), async (req, res) => {
// Only authenticated admins can access
const users = await userService.getAllUsers();
res.json(users);
});
API Versioning
// URL-based versioning
app.use('/api/v1', v1Routes);
app.use('/api/v2', v2Routes);
// v1 route (old)
v1Router.get('/users/:id', async (req, res) => {
const user = await userService.getUser(req.params.id);
res.json(user); // Old response format
});
// v2 route (new - includes additional fields)
v2Router.get('/users/:id', async (req, res) => {
const user = await userService.getUser(req.params.id);
const enriched = await userService.enrichUserData(user);
res.json(enriched); // New response format with more data
});
Pagination & Filtering
// Query parameters for pagination
interface PaginationParams {
page: number;
limit: number;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
filters?: Record<string, any>;
}
async function getUsers(params: PaginationParams) {
const { page = 1, limit = 20, sortBy = 'created_at', sortOrder = 'desc' } = params;
const offset = (page - 1) * limit;
const result = await db.query(`
SELECT * FROM users
WHERE email LIKE $1
ORDER BY ${sortBy} ${sortOrder}
LIMIT $2 OFFSET $3
`, [`%${params.filters?.email || ''}%`, limit, offset]);
const countResult = await db.query('SELECT COUNT(*) FROM users');
const total = parseInt(countResult.rows[0].count);
return {
data: result.rows,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
hasNext: page * limit < total,
hasPrev: page > 1,
},
};
}
// API endpoint
app.get('/api/users', async (req, res) => {
const result = await getUsers({
page: parseInt(req.query.page as string) || 1,
limit: parseInt(req.query.limit as string) || 20,
sortBy: req.query.sortBy as string,
sortOrder: req.query.sortOrder as 'asc' | 'desc',
filters: req.query.filters as any,
});
res.json(result);
});
Rate Limiting
import rateLimit from 'express-rate-limit';
// Global rate limit
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests, please try again later',
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/', limiter);
// Strict limit for authentication endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // Only 5 login attempts per 15 minutes
skipSuccessfulRequests: true,
});
app.post('/api/auth/login', authLimiter, loginHandler);
Detailed Patterns (Level 2 - Load on Request)
See companion files:
graphql-patterns.md- GraphQL schema design and resolversmicroservices-patterns.md- Service communication and orchestrationcaching-strategies.md- Redis patterns and cache invalidation
Code Examples (Level 3 - Load When Needed)
See examples directory:
examples/rest-api-complete.ts- Full REST API implementationexamples/graphql-server.ts- Complete GraphQL setupexamples/auth-flow-complete.ts- Authentication implementation
Integration with Agents
senior-fullstack-developer:
- Primary agent for backend implementation
- Uses these patterns for all API development
- References for database design
database-migration-specialist:
- Uses database patterns for schema design
- References optimization patterns
solutions-architect:
- Uses these patterns for system design
- Evaluates architecture decisions
Version 1.0.0 | REST & GraphQL Ready | Scalable Patterns
Repository

Primadetaautomation
Author
Primadetaautomation/primadata-marketplace/.claude/skills/backend-development-patterns
3
Stars
0
Forks
Updated1d ago
Added6d ago