Skip to content
← All insights
TypeScript5 min read

Where TypeScript backends start to get complicated

TypeScript makes a backend approachable. That does not guarantee it will stay simple as the system grows.

Types stop at the edges

A precise internal type cannot make an incoming request, database row or third-party response trustworthy. Validation still has to happen where untrusted data enters the system.

The trouble starts when a codebase treats a compile-time type as proof of runtime behaviour. Make the boundaries explicit and keep the validation close to them.

A folder structure is not an architecture

Controllers, services and repositories can look tidy while responsibilities leak everywhere. Name the job of each part of the system and keep business decisions away from transport and persistence details.

  • Validate data at runtime boundaries
  • Keep domain decisions out of route handlers
  • Make asynchronous failures visible
  • Test behaviour rather than framework wiring
Narrow unknown input before using ittypescript
type CreateUser = { email: string }

function parseCreateUser(value: unknown): CreateUser {
  if (typeof value !== 'object' || value === null ||
      !('email' in value) || typeof value.email !== 'string') {
    throw new Error('Invalid request body')
  }
  return { email: value.email }
}