ZeroGravitySkin-Ron avatar

backend-dev-guidelines

Comprehensive backend development guide for Node.js/Express/TypeScript microservices. Use when creat

作者 ZeroGravitySkin-Ron|オープンソース

Backend Development Guidelines

Purpose

Establish consistency and best practices for the Quantum Skincare backend API using modern Node.js/Express/TypeScript patterns with Clerk authentication, Prisma ORM, and structured error handling.

When to Use This Skill

Automatically activates when working on:

  • Creating or modifying routes, endpoints, APIs
  • Building controllers and services
  • Implementing middleware (auth, validation, error handling, rate limiting)
  • Database operations with Prisma and DAOs
  • Error handling with custom error classes
  • Input validation with Zod
  • Clerk authentication integration
  • Backend testing and refactoring

Quick Start

New Backend Feature Checklist

  • Route: Clean definition in routes/, delegate to controller
  • Controller: Request handlers with asyncHandler wrapper
  • Service: Business logic (if needed for complex operations)
  • DAO: Database access via @quantum/data-access
  • Validation: Zod schema from @quantum/shared-validation
  • Error Handling: Use custom error classes (ValidationError, NotFoundError, etc.)
  • Auth: Apply requireAuthClerk middleware for protected routes
  • Tests: Unit + integration tests with Jest
  • Logging: Use Pino logger with structured logging

Key Architecture Components

  • Express app setup in app.ts
  • Middleware chain (helmet, cors, rate limiting, auth)
  • Clerk authentication with requireAuthClerk
  • Centralized error handling via errorHandler
  • Structured logging with Pino
  • Versioned API routes under /v1

Architecture Overview

Layered Architecture

HTTP Request
    ↓
Routes (routing only)
    ↓
Controllers (request handling)
    ↓
Services (business logic)
    ↓
Repositories (data access)
    ↓
Database (Prisma)

Key Principle: Each layer has ONE responsibility.

See architecture-overview.md for complete details.


Directory Structure

apps/backend/src/
├── controllers/         # Request handlers (e.g., users.controller.ts)
│   └── quota/          # Feature-specific controllers
├── services/            # Business logic organized by domain
│   ├── clerk/          # Clerk user synchronization
│   ├── consent/        # Consent management
│   ├── geo/            # IP geolocation
│   ├── quota/          # Quota management
│   ├── scans/          # Scan processing
│   ├── skin-analysis/  # Skin analysis orchestration
│   ├── storage/        # S3 storage
│   └── treatment/      # Treatment recommendations
├── routes/              # Route definitions (e.g., users.ts, skin-analysis.ts)
├── middleware/          # Express middleware (auth, validation, error handling)
├── types/               # Backend-specific TypeScript types
├── utils/               # Utilities (logger, errors)
├── __tests__/           # Tests (unit + integration)
├── app.ts               # Express app setup + middleware chain
└── main.ts              # Server entry point + secret initialization

Naming Conventions:

  • Controllers: kebab-case.controller.ts - users.controller.ts
  • Services: kebab-case.service.ts - user-deletion.service.ts
  • Routes: kebab-case.ts - skin-analysis.ts
  • Middleware: kebab-case.ts - auth-clerk.ts

Core Principles (8 Key Rules)

1. Routes Only Route, Controllers Handle Requests

// ❌ NEVER: Business logic in routes
router.post('/submit', async (req, res) => {
    // 200 lines of logic
});

// ✅ ALWAYS: Delegate to controller function
router.post('/submit', asyncHandler(submitController));

2. All Controllers Use asyncHandler

// Wraps async functions to catch errors automatically
export const getUser = asyncHandler(async (req: Request, res: Response) => {
    const user = await getUserById(req.params.id);
    res.json(createSuccessResponse(user, 'User retrieved'));
});

3. Use Custom Error Classes

import { ValidationError, NotFoundError, UnauthorizedError } from '../utils/errors.js';

// Throw specific errors - errorHandler will format response
if (!user) {
    throw new NotFoundError('User');
}

if (!isValid) {
    throw new ValidationError('Invalid email format');
}

4. Environment Variables Directly (No Wrapper Config)

// ✅ Access environment variables directly
const port = process.env.PORT || 5000;
const clerkSecretKey = process.env.CLERK_SECRET_KEY;

// Validate critical env vars at startup (see main.ts)
if (!process.env.CLERK_SECRET_KEY) {
    logger.fatal('Missing CLERK_SECRET_KEY');
    process.exit(1);
}

5. Validate All Input with Zod

import { validateRequest } from '../middleware/validation.js';
import { consentSchemas } from '@quantum/shared-validation';

// Use validateRequest middleware
export const acceptConsent: RequestHandler[] = [
    validateRequest({ body: consentSchemas.acceptConsent }),
    asyncHandler(async (req, res) => {
        // req.body is now type-safe and validated
    })
];

6. Use DAOs from @quantum/data-access

import { getUserByClerkId, upsertUserFromClerk } from '@quantum/data-access';

// ✅ Use DAOs for database access
const user = await getUserByClerkId(clerkUserId);

// ❌ Don't use Prisma directly in controllers
const user = await prisma.users.findUnique(...);

7. Protect Routes with requireAuthClerk

import { requireAuthClerk } from '../middleware/auth-clerk.js';

// Apply auth middleware to protected routes
router.get('/me', requireAuthClerk, getUserController);

// User payload available via getUserPayload(req)
const user = getUserPayload(req); // { id, email, tier, consent }

8. Structured Logging with Pino

import { logger } from '../utils/logger.js';

// Include requestId and relevant context
logger.info({
    userId: user.id,
    requestId: req.requestId,
    event: 'consent.accepted'
}, 'Consent accepted successfully');

logger.error({ error, userId: user.id }, 'Failed to process request');

Common Imports

// Express
import express, { Request, Response, NextFunction, Router, RequestHandler } from 'express';

// Error handling
import { asyncHandler } from '../middleware/error-handler.js';
import {
    ValidationError,
    NotFoundError,
    UnauthorizedError,
    ForbiddenError,
    ConflictError,
    QuotaExceededError
} from '../utils/errors.js';

// Response helpers
import { createSuccessResponse, createErrorResponse } from '../types/api-responses.js';

// Validation
import { validateRequest } from '../middleware/validation.js';
import { z } from 'zod';

// Database (via DAOs)
import {
    getUserByClerkId,
    upsertUserFromClerk,
    createConsentAudit,
    getPersonalInfo
} from '@quantum/data-access';

// Shared validation schemas
import { consentSchemas } from '@quantum/shared-validation';

// Shared types
import type { UserApp, ConsentProfile } from '@quantum/shared-types';

// Auth
import { requireAuthClerk, requireClerkToken } from '../middleware/auth-clerk.js';
import { getUserPayload } from '../types/express-auth.js';
import { clerkClient } from '@clerk/express';

// Logging
import { logger } from '../utils/logger.js';

Quick Reference

HTTP Status Codes

CodeUse Case
200Success
201Created
400Bad Request
401Unauthorized
403Forbidden
404Not Found
500Server Error

Example Services

apps/backend/src/ - Quantum Skincare Backend API

  • Routes: routes/users.ts, routes/skin-analysis.ts
  • Controllers: controllers/users.controller.ts
  • Services: services/consent/, services/skin-analysis/
  • Middleware: middleware/auth-clerk.ts, middleware/validation.ts

Anti-Patterns to Avoid

❌ Business logic in routes ❌ Direct Prisma access in controllers (use DAOs) ❌ Missing error handling or asyncHandler wrapper ❌ No input validation ❌ console.log instead of logger ❌ Not using custom error classes ❌ Forgetting requireAuthClerk on protected routes ❌ Accessing req.user without getUserPayload helper


Navigation Guide

Need to...Read this
Understand architecturearchitecture-overview.md
Create routes/controllersrouting-and-controllers.md
Organize business logicservices-and-repositories.md
Validate inputvalidation-patterns.md
Add error trackingsentry-and-monitoring.md
Create middlewaremiddleware-guide.md
Database accessdatabase-patterns.md
Manage configconfiguration.md
Handle async/errorsasync-and-errors.md
Write teststesting-guide.md
See examplescomplete-examples.md

Resource Files

architecture-overview.md

Layered architecture, request lifecycle, separation of concerns

routing-and-controllers.md

Route definitions, BaseController, error handling, examples

services-and-repositories.md

Service patterns, DI, repository pattern, caching

validation-patterns.md

Zod schemas, validation, DTO pattern

sentry-and-monitoring.md

Sentry init, error capture, performance monitoring

middleware-guide.md

Auth, audit, error boundaries, AsyncLocalStorage

database-patterns.md

PrismaService, repositories, transactions, optimization

configuration.md

UnifiedConfig, environment configs, secrets

async-and-errors.md

Async patterns, custom errors, asyncErrorWrapper

testing-guide.md

Unit/integration tests, mocking, coverage

complete-examples.md

Full examples, refactoring guide


Related Skills

  • frontend-dev-guidelines - React/TypeScript patterns for Next.js frontend
  • route-tester - Testing authenticated routes with Clerk JWT
  • skill-developer - Meta-skill for creating and managing skills

Skill Status: COMPLETE ✅ Line Count: < 500 ✅ Progressive Disclosure: 11 resource files ✅