From orchestrator
Provides patterns for RESTful API design, TypeScript response formats, HTTP status codes, and JWT authentication flows. Use for backend development tasks.
npx claudepluginhub devsforge/orchestrator --plugin orchestratorThis skill is limited to using the following tools:
Modern backend patterns and best practices for building scalable APIs.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
Modern backend patterns and best practices for building scalable APIs.
GET /api/v1/users # List users
GET /api/v1/users/:id # Get user
POST /api/v1/users # Create user
PUT /api/v1/users/:id # Replace user
PATCH /api/v1/users/:id # Update user
DELETE /api/v1/users/:id # Delete user
# Nested resources
GET /api/v1/users/:id/posts
POST /api/v1/users/:id/posts
# Actions as resources
POST /api/v1/auth/login
POST /api/v1/auth/logout
POST /api/v1/passwords/reset
// Success response
interface SuccessResponse<T> {
success: true;
data: T;
meta?: {
page: number;
perPage: number;
total: number;
totalPages: number;
};
}
// Error response
interface ErrorResponse {
success: false;
error: {
code: string;
message: string;
details?: Array<{
field: string;
message: string;
}>;
};
}
| Code | Meaning | Usage |
|---|---|---|
| 200 | OK | Successful GET, PUT, PATCH |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Validation error |
| 401 | Unauthorized | Missing/invalid auth |
| 403 | Forbidden | No permission |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Duplicate resource |
| 422 | Unprocessable | Business logic error |
| 500 | Server Error | Unexpected error |
// Login - generate tokens
async function login(email: string, password: string) {
const user = await db.users.findByEmail(email);
if (!user || !await verifyPassword(password, user.password)) {
throw new HTTPException(401, { message: 'Invalid credentials' });
}
const accessToken = await generateAccessToken(user);
const refreshToken = await generateRefreshToken(user);
// Store refresh token
await db.refreshTokens.create({
userId: user.id,
token: refreshToken,
expiresAt: addDays(new Date(), 7),
});
return { accessToken, refreshToken, user };
}
// Token generation
async function generateAccessToken(user: User) {
return jwt.sign(
{ sub: user.id, email: user.email, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
}
// Token refresh
async function refreshAccessToken(refreshToken: string) {
const stored = await db.refreshTokens.findByToken(refreshToken);
if (!stored || stored.expiresAt < new Date()) {
throw new HTTPException(401, { message: 'Invalid refresh token' });
}
const user = await db.users.findById(stored.userId);
return generateAccessToken(user);
}
export async function authMiddleware(c: Context, next: Next) {
const authHeader = c.req.header('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
throw new HTTPException(401, { message: 'Missing token' });
}
const token = authHeader.slice(7);
try {
const payload = await jwt.verify(token, process.env.JWT_SECRET);
c.set('userId', payload.sub);
c.set('userRole', payload.role);
await next();
} catch {
throw new HTTPException(401, { message: 'Invalid token' });
}
}
// Role-based authorization
export function requireRole(...roles: string[]) {
return async (c: Context, next: Next) => {
const userRole = c.get('userRole');
if (!roles.includes(userRole)) {
throw new HTTPException(403, { message: 'Forbidden' });
}
await next();
};
}
import { z } from 'zod';
// User schemas
export const createUserSchema = z.object({
email: z.string().email('Invalid email'),
name: z.string().min(2).max(100),
password: z.string()
.min(8, 'Password must be at least 8 characters')
.regex(/[A-Z]/, 'Must contain uppercase')
.regex(/[0-9]/, 'Must contain number'),
});
export const updateUserSchema = createUserSchema.partial();
// Query params
export const paginationSchema = z.object({
page: z.coerce.number().min(1).default(1),
perPage: z.coerce.number().min(1).max(100).default(20),
sort: z.enum(['createdAt', 'name']).default('createdAt'),
order: z.enum(['asc', 'desc']).default('desc'),
});
// Using with Hono
app.post('/users', zValidator('json', createUserSchema), async (c) => {
const data = c.req.valid('json');
// data is fully typed
});
export function errorHandler(err: Error, c: Context) {
console.error('Error:', err);
// HTTP exceptions
if (err instanceof HTTPException) {
return c.json({
success: false,
error: { code: 'HTTP_ERROR', message: err.message },
}, err.status);
}
// Validation errors
if (err instanceof ZodError) {
return c.json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid input',
details: err.errors.map(e => ({
field: e.path.join('.'),
message: e.message,
})),
},
}, 400);
}
// Database errors
if (err.code === '23505') { // Unique violation
return c.json({
success: false,
error: { code: 'CONFLICT', message: 'Resource already exists' },
}, 409);
}
// Unknown errors
return c.json({
success: false,
error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' },
}, 500);
}
import { Redis } from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);
// Cache-aside pattern
async function getUserById(id: string): Promise<User> {
const cacheKey = `user:${id}`;
// Try cache first
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// Fetch from database
const user = await db.users.findById(id);
if (!user) throw new HTTPException(404);
// Cache for 5 minutes
await redis.setex(cacheKey, 300, JSON.stringify(user));
return user;
}
// Invalidate on update
async function updateUser(id: string, data: UpdateUserInput) {
const user = await db.users.update(id, data);
await redis.del(`user:${id}`);
return user;
}
import { rateLimiter } from 'hono-rate-limiter';
// Apply rate limiting
app.use(
'/api/*',
rateLimiter({
windowMs: 60 * 1000, // 1 minute
limit: 100, // 100 requests per minute
keyGenerator: (c) => c.get('userId') || c.req.header('x-forwarded-for'),
})
);
// Stricter limits for auth endpoints
app.use(
'/api/auth/*',
rateLimiter({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 5, // 5 attempts
})
);
class UserRepository {
async findById(id: string): Promise<User | null> {
return db.query.users.findFirst({
where: eq(users.id, id),
});
}
async findByEmail(email: string): Promise<User | null> {
return db.query.users.findFirst({
where: eq(users.email, email),
});
}
async create(data: CreateUserInput): Promise<User> {
const [user] = await db.insert(users).values(data).returning();
return user;
}
async update(id: string, data: UpdateUserInput): Promise<User> {
const [user] = await db
.update(users)
.set({ ...data, updatedAt: new Date() })
.where(eq(users.id, id))
.returning();
return user;
}
async delete(id: string): Promise<void> {
await db.delete(users).where(eq(users.id, id));
}
}
async function createOrder(userId: string, items: OrderItem[]) {
return db.transaction(async (tx) => {
// Create order
const [order] = await tx
.insert(orders)
.values({ userId, status: 'pending' })
.returning();
// Create order items
await tx.insert(orderItems).values(
items.map(item => ({
orderId: order.id,
productId: item.productId,
quantity: item.quantity,
}))
);
// Update inventory
for (const item of items) {
await tx
.update(products)
.set({ stock: sql`stock - ${item.quantity}` })
.where(eq(products.id, item.productId));
}
return order;
});
}