TriFrost

TriFrost Docs

Learn and build with confidence, from a first project with workers to global scale on bare metal.

Understanding Context

In TriFrost, context is not a magic object, it's a first-class, explicitly constructed entity.

When a request hits your app:

  • The runtime adapter (Node, Bun, Workerd) parses the inbound request
  • TriFrost constructs a new ctx instance
  • TriFrost automatically initializes the
    - context body using a bodyParser specific for the runtime
    - the route params, eg: /use/:userId/asset/:file -> /user/123/asset/me.jpg -> ctx.state: {userId: '123', file: 'me.jpg})
  • That context flows through your middleware stack and handler chain
  • It accumulates state, logs, mutations, headers, and eventually a response

Context is:

  • Immutable in structure, but extensible in state (ctx.setState(...)), headers (ctx.setHeaders(...)), etc
  • Isolated per request
  • Safe to use across async boundaries
  • Fully typed, including env and state

Where does it show up?

Every route handler and middleware receives ctx as its first argument:

app.get('/greet', async (ctx) => {
  ctx.text(`Hello from ${ctx.path}`);
});

app.use(async (ctx) => {
  return ctx.setState({ startedAt: Date.now() });
});

What can I do with it?

See the Context API for full details, but here’s a quick summary.

Request Metadata
ctx.method      // 'GET', 'POST', etc
ctx.path        // URL path
ctx.headers     // Inbound headers
ctx.query       // URLSearchParams instance
ctx.body        // Parsed body (if available)
ctx.ip          // IP address (if determinable)
...
Response Utilities
ctx.text('hello world')
ctx.json({ ok: true })
ctx.html(<Page />)
ctx.redirect('/login')
ctx.file('public/logo.png')
State Management
ctx.setState({ user })
ctx.state.user
ctx.delState(['user'])
Logging & Observability
ctx.logger.info('Request started')
ctx.logger.setAttributes({ userId })
...
Timeout Control
ctx.setTimeout(5000)
ctx.clearTimeout()
Lifecycle Hooks
ctx.abort(503)
ctx.end()
ctx.addAfter(fn)
...

Can I extend context?

TriFrost's context is built to operate on multiple runtimes, as such not directly, but you can pass anything into ctx.state, which is the canonical way to attach context-aware data throughout your app.

app.use(async (ctx, next) => {
  const user = await auth(ctx);
  ctx.setState({ user });
  await next();
});

All handlers down the chain will now have access to ctx.state.user


Design Goals

The ctx object in TriFrost is designed with the following principles:

  • Runtime fidelity: it reflects real request/response lifecycle
  • Zero magic: you always see what's being passed
  • Traceability: logs and spans flow through it
  • DX forward: it's typed, extensible, and predictable

Next Steps

Feeling more adventurous?

Loved the read? Share it with others