TypeDrop

2026-05-22 Challenge

2026-05-22 Hard

Typed Middleware Pipeline with Typed Context & Error Boundaries

You're building the request-processing core for a high-throughput HTTP gateway. Incoming requests pass through a chain of typed middleware layers — auth, rate-limiting, transformation, and logging — each of which can enrich a shared context object or short-circuit the pipeline with a strongly-typed error. The hardest part is making the context type accumulate correctly as it flows through each middleware stage.

Goals

  • Implement `validateRequest` to safely narrow `unknown` to a fully-typed `RawRequest`, returning a discriminated `Result` with a concrete `PipelineError<"VALIDATION_ERROR">` on failure.
  • Implement the three middleware functions (`authMiddleware`, `makeRateLimitMiddleware`, `transformMiddleware`) so each one adds exactly the right fields to the context intersection type and short-circuits with a correctly-kinded `PipelineError` on failure.
  • Implement `runPipeline` to chain all middleware stages sequentially, propagating typed errors without re-wrapping them, and returning a `Result<FullContext, AnyPipelineError>`.
  • Implement `reportError` with an exhaustive `switch` over all `PipelineErrorKind` variants and a `never`-typed exhaustiveness guard so TypeScript enforces full coverage.
challenge.ts
// Core types and main pipeline entry point

export type Brand<T, Tag extends string> = T & { readonly __brand: Tag };
export type UserId   = Brand<string, "UserId">;
export type TraceId  = Brand<string, "TraceId">;
export type TenantId = Brand<string, "TenantId">;

export type Result<T, E> =
  | { readonly ok: true;  readonly value: T }
  | { readonly ok: false; readonly error: E };

export type Middleware<In, Out extends In> = (
  ctx: In
) => Promise<Result<Out, AnyPipelineError>>;

export type FullContext =
  BaseContext & AuthFields & RateLimitFields & TransformFields;

// Validate raw unknown input → typed RawRequest
export function validateRequest(
  raw: unknown
): Result<RawRequest, PipelineError<"VALIDATION_ERROR">> { /* TODO */ }

// Run all middleware stages sequentially, short-circuiting on any error
export async function runPipeline(
  raw: unknown,
  store: RateLimitStore
): Promise<Result<FullContext, AnyPipelineError>> { /* TODO */ }
Hints (click to reveal)

Hints

  • For `Middleware<In, Out extends In>`, the `Out extends In` constraint is the key to making TypeScript enforce that each stage can only *add* fields — use intersection types like `BaseContext & AuthFields` as the `Out` type parameter.
  • In `reportError`, after your `switch` statement, assign `err` to a variable typed as `never` (e.g. `const _exhaustive: never = err`) — this will produce a compile error if you forget a case.
  • In `validateRequest`, build up your checks incrementally: first guard the top-level shape with `typeof`/`!= null`, then use `in` narrowing and `typeof` checks on each field — avoid casting, let the type guards do the work.

Or clone locally

git clone -b challenge/2026-05-22 https://github.com/niltonheck/typedrop.git