Skip to main content

Runtime Controllers

Controllers are the HTTP edge of the runtime. They should stay thin.

Core controller rule

Controllers should:
  • bind Express routes
  • read request params and body
  • delegate persistence to services
  • return HTTP responses
Controllers should not:
  • implement model persistence directly
  • own cache policy
  • own relation loading policy
  • introduce driver-specific branching
The preferred runtime contract behind controllers is:
  • User.find(...) for one-record reads
  • User.create(...) for one-shot inserts
  • loaded-instance update(...); save() for readable updates
  • User.deleteById(...) and User.restoreById(...) for explicit by-id writes inside services

Express wiring

Only expose the restore route when soft-delete recovery is part of the API.

Soft deletes

If the model supports soft deletes:
  • destroy() should stay a soft delete path
  • restore() should be exposed only when recovery is intentional
Use one schema declaration, not both:
or:

Serialization, eager loading, and cache

  • keep toObject() normalization in the service boundary when needed
  • keep eager loading in services
  • keep cache logic in services

Driver note

The controller contract stays the same for SQL and Mongo models. Keep driver-specific behavior in the model and service layers.