From ftitos-claude-code
Backend architecture patterns, API design, database optimization, and server-side best practices for Node.js, Express, and Next.js API routes.
npx claudepluginhub nassimbf/ftitos-claude-codeThis skill uses the workspace's default tool permissions.
Backend architecture patterns and best practices for scalable server-side applications.
Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). **PROACTIVE ACTIVATION**: Use this skill automatically when working in Next.js projects that have `cacheComponents: true` in their next.config.ts/next.config.js. When this config is detected, proactively apply Cache Components patterns and best practices to all React Server Component implementations. **DETECTION**: At the start of a session in a Next.js project, check for `cacheComponents: true` in next.config. If enabled, this skill's patterns should guide all component authoring, data fetching, and caching decisions. **USE CASES**: Implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, debugging cache issues, and reviewing Cache Component implementations.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
Backend architecture patterns and best practices for scalable server-side applications.
GET /api/items # List resources
GET /api/items/:id # Get single resource
POST /api/items # Create resource
PUT /api/items/:id # Replace resource
PATCH /api/items/:id # Update resource
DELETE /api/items/:id # Delete resource
GET /api/items?status=active&sort=volume&limit=20&offset=0
interface ItemRepository {
findAll(filters?: ItemFilters): Promise<Item[]>
findById(id: string): Promise<Item | null>
create(data: CreateItemDto): Promise<Item>
update(id: string, data: UpdateItemDto): Promise<Item>
delete(id: string): Promise<void>
}
class ItemService {
constructor(private itemRepo: ItemRepository) {}
async searchItems(query: string, limit: number = 10): Promise<Item[]> {
const embedding = await generateEmbedding(query)
const results = await this.vectorSearch(embedding, limit)
const items = await this.itemRepo.findByIds(results.map(r => r.id))
return items.sort((a, b) => {
const scoreA = results.find(r => r.id === a.id)?.score || 0
const scoreB = results.find(r => r.id === b.id)?.score || 0
return scoreA - scoreB
})
}
}
export function withAuth(handler: NextApiHandler): NextApiHandler {
return async (req, res) => {
const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) {
return res.status(401).json({ error: 'Unauthorized' })
}
try {
const user = await verifyToken(token)
req.user = user
return handler(req, res)
} catch (error) {
return res.status(401).json({ error: 'Invalid token' })
}
}
}
// Bad: N+1 query problem
const items = await getItems()
for (const item of items) {
item.creator = await getUser(item.creator_id)
}
// Good: Batch fetch
const items = await getItems()
const creatorIds = items.map(m => m.creator_id)
const creators = await getUsers(creatorIds)
const creatorMap = new Map(creators.map(c => [c.id, c]))
items.forEach(item => {
item.creator = creatorMap.get(item.creator_id)
})
async function getItemWithCache(id: string): Promise<Item> {
const cacheKey = `item:${id}`
const cached = await redis.get(cacheKey)
if (cached) return JSON.parse(cached)
const item = await db.items.findUnique({ where: { id } })
if (!item) throw new Error('Item not found')
await redis.setex(cacheKey, 300, JSON.stringify(item))
return item
}
class ApiError extends Error {
constructor(
public statusCode: number,
public message: string,
public isOperational = true
) {
super(message)
}
}
export function errorHandler(error: unknown, req: Request): Response {
if (error instanceof ApiError) {
return NextResponse.json({ success: false, error: error.message }, { status: error.statusCode })
}
if (error instanceof z.ZodError) {
return NextResponse.json({ success: false, error: 'Validation failed', details: error.errors }, { status: 400 })
}
console.error('Unexpected error:', error)
return NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 })
}
async function fetchWithRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
let lastError: Error
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
lastError = error as Error
if (i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000
await new Promise(resolve => setTimeout(resolve, delay))
}
}
}
throw lastError!
}
type Permission = 'read' | 'write' | 'delete' | 'admin'
const rolePermissions: Record<string, Permission[]> = {
admin: ['read', 'write', 'delete', 'admin'],
moderator: ['read', 'write', 'delete'],
user: ['read', 'write']
}
export function hasPermission(user: User, permission: Permission): boolean {
return rolePermissions[user.role].includes(permission)
}
class RateLimiter {
private requests = new Map<string, number[]>()
async checkLimit(identifier: string, maxRequests: number, windowMs: number): Promise<boolean> {
const now = Date.now()
const requests = this.requests.get(identifier) || []
const recentRequests = requests.filter(time => now - time < windowMs)
if (recentRequests.length >= maxRequests) return false
recentRequests.push(now)
this.requests.set(identifier, recentRequests)
return true
}
}
class Logger {
log(level: 'info' | 'warn' | 'error', message: string, context?: Record<string, unknown>) {
console.log(JSON.stringify({ timestamp: new Date().toISOString(), level, message, ...context }))
}
}