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.
Useful resources
Or clone locally
git clone -b challenge/2026-05-22 https://github.com/niltonheck/typedrop.git