Back to Learn
Backend
11 min read

Claude Rules for Backend Development: Go, Python, Node.js

clauderules.net

Backend-Specific Rule Patterns

Backend projects have unique concerns that frontend rules don't cover: API design, error handling, database patterns, security, and logging. Good backend CLAUDE.md files encode your team's decisions on each of these.

Node.js / TypeScript Backend Rules

CLAUDE.md
## Node.js API Server (Fastify + TypeScript)

### Project Structure
- src/routes/ — Route handlers, one file per resource
- src/services/ — Business logic (no DB calls here)
- src/repositories/ — Database access layer
- src/types/ — Shared TypeScript types

### API Design
- RESTful conventions: GET (read), POST (create), PUT (replace), PATCH (update), DELETE
- Always return { data } or { error } at the top level
- Use HTTP 422 for validation errors, 401 for auth errors, 403 for permission errors
- Version prefix: /api/v1/

### Database (Prisma)
- Never call db in route handlers — go through a repository
- Use transactions for multi-step operations
- Never use `db.X.findMany()` without a `take` limit
- Soft-delete pattern: set `deletedAt` field, filter in queries

### Security
- Validate all inputs with Zod before using them
- Never interpolate user input into SQL strings
- Rate limit all public endpoints
- Never log request bodies (may contain PII)

### Error Handling
```ts
// Always wrap route handlers
export const getUser = async (req: FastifyRequest, reply: FastifyReply) => {
  try {
    const user = await userRepository.findById(req.params.id)
    if (!user) return reply.status(404).send({ error: 'User not found' })
    return reply.send({ data: user })
  } catch (error) {
    req.log.error(error, 'getUser failed')
    return reply.status(500).send({ error: 'Internal server error' })
  }
}
```

Python / FastAPI Rules

CLAUDE.md
## Python API (FastAPI + SQLAlchemy)

### Style
- Type annotations on all function signatures (Python 3.11+)
- Use Pydantic v2 for request/response models
- Prefer dataclasses over plain dicts for internal data transfer

### Project Layout
- app/routers/ — FastAPI route files
- app/services/ — Business logic
- app/models/ — SQLAlchemy ORM models
- app/schemas/ — Pydantic schemas for API contracts
- app/crud/ — Database CRUD operations

### Database Patterns
```python
# Always use dependency injection for DB sessions
def get_user(user_id: int, db: Session = Depends(get_db)) -> User:
    user = crud.user.get(db, id=user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user
```

### Error Handling
- Raise HTTPException with specific status codes — never generic 500
- Log with structlog (not print or stdlib logging)
- Use background tasks (BackgroundTasks) for fire-and-forget operations

### Don'ts
- Don't use mutable default arguments
- Don't do sync I/O in async routes
- Don't put business logic in models

Go Backend Rules

CLAUDE.md
## Go API Server (net/http + pgx)

### Code Style
- Error handling: always check errors explicitly, never ignore with _
- Use named return values only when they improve clarity
- Package names: lowercase, single word, no underscores
- Exported types/funcs: PascalCase; internal: camelCase

### Error Pattern
```go
// Always wrap errors with context
if err != nil {
    return fmt.Errorf("getUserByID %d: %w", id, err)
}
```

### HTTP Handlers
```go
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
    id, err := strconv.Atoi(r.PathValue("id"))
    if err != nil {
        respondError(w, http.StatusBadRequest, "invalid user ID")
        return
    }
    user, err := h.svc.GetUser(r.Context(), id)
    if errors.Is(err, ErrNotFound) {
        respondError(w, http.StatusNotFound, "user not found")
        return
    }
    if err != nil {
        h.log.Error("GetUser failed", "error", err)
        respondError(w, http.StatusInternalServerError, "internal error")
        return
    }
    respondJSON(w, http.StatusOK, user)
}
```

### Constraints
- NEVER use global variables for state (use struct fields)
- NEVER use init() functions
- ALWAYS pass context.Context as the first argument to DB calls

Cross-Language Backend Rules

Regardless of language, these rules apply to all our backend services:

CLAUDE.md
## Universal Backend Rules

### Logging
- Structured logs only (JSON format in production)
- Log at entry and error points — not throughout business logic
- Never log: passwords, tokens, full credit card numbers, PII

### API Contracts
- All APIs must have OpenAPI/Swagger documentation
- Use semantic versioning for API versions
- Deprecate endpoints for 6 months before removing

### Testing
- Unit test all business logic in services/
- Integration test all API endpoints
- Use table-driven tests for validation logic