Skip to content

RequestContext

The RequestContext provides a way to pass data through the request lifecycle. It's available in loaders, actions, and middleware.

Import

ts
import {
  createContext,
  RequestContext,
  getContext,
  attachContext,
  isRequestContext
} from '@ereo/core'

createContext

Creates a new request context.

Signature

ts
function createContext(request: Request): RequestContext

Parameters

NameTypeDescription
requestRequestThe incoming HTTP request

Returns

Returns a RequestContext instance.

RequestContext Methods

get

Retrieves a value from the context.

ts
get<T>(key: string): T | undefined
ts
const user = context.get<User>('user')

set

Stores a value in the context.

ts
set<T>(key: string, value: T): void
ts
context.set('user', { id: 1, name: 'Alice' })

has

Checks if a key exists in the context.

ts
has(key: string): boolean
ts
if (context.has('user')) {
  // User is authenticated
}

delete

Removes a value from the context.

ts
delete(key: string): boolean
ts
context.delete('temporaryData')

Properties

url

The parsed URL of the current request.

ts
url: URL
ts
// Access URL components
console.log(context.url.pathname)   // '/users/123'
console.log(context.url.searchParams.get('sort'))  // 'asc'

env

Environment variables available in the current context.

ts
env: Record<string, string | undefined>
ts
const dbUrl = context.env.DATABASE_URL

cache

Access cache control for the current request.

ts
interface CacheControl {
  set(options: CacheOptions): void
  get(): CacheOptions | undefined
  getTags(): string[]
  addTags(tags: string[]): void
}

interface CacheOptions {
  maxAge?: number
  staleWhileRevalidate?: number
  tags?: string[]
  private?: boolean
}
ts
// Set cache headers
context.cache.set({
  maxAge: 3600,
  staleWhileRevalidate: 600,
  tags: ['posts', 'user:123']
})

// Get current cache settings
const cacheOptions = context.cache.get()

// Get all cache tags (accumulated from multiple set() calls)
const tags = context.cache.getTags()
// ['posts', 'user:123']

// Add additional tags dynamically (without resetting other cache options)
context.cache.addTags(['category:tech'])

responseHeaders

Access response headers.

ts
responseHeaders: Headers
ts
context.responseHeaders.set('X-Custom-Header', 'value')
context.responseHeaders.append('Set-Cookie', 'session=abc123')

Helper Functions

attachContext

Attaches a context to a request for later retrieval.

ts
function attachContext(request: Request, context: RequestContext): void
ts
const context = createContext(request)
context.set('user', user)
attachContext(request, context)

getContext

Retrieves the context attached to a request.

ts
function getContext(request: Request): RequestContext | undefined
ts
const context = getContext(request)
if (context) {
  const user = context.get('user')
}

isRequestContext

Type guard for RequestContext.

ts
function isRequestContext(value: unknown): value is RequestContext
ts
if (isRequestContext(maybeContext)) {
  const data = maybeContext.get('data')
}

Examples

Using Context in Middleware

ts
// routes/_middleware.ts
export const middleware = async (request, context, next) => {
  // Authenticate user
  const token = request.headers.get('Authorization')?.replace('Bearer ', '')

  if (token) {
    const user = await verifyToken(token)
    context.set('user', user)
  }

  return next()
}

Accessing Context in Loaders

ts
// routes/dashboard.tsx
export const loader = createLoader(async ({ context }) => {
  const user = context.get('user')

  if (!user) {
    throw new Response('Unauthorized', { status: 401 })
  }

  const dashboardData = await getDashboardData(user.id)
  return { user, dashboardData }
})

Setting Response Headers

ts
export const loader = createLoader(async ({ context }) => {
  // Set custom headers
  context.responseHeaders.set('X-Request-Id', crypto.randomUUID())

  // Set cookies
  context.responseHeaders.append('Set-Cookie', 'viewed=true; Path=/')

  return { data: 'example' }
})

Cache Control via Context

ts
export const loader = createLoader(async ({ params, context }) => {
  const post = await db.posts.find(params.id)

  // Dynamic cache based on content
  if (post.isStatic) {
    context.cache.set({
      maxAge: 86400, // 24 hours
      tags: ['posts', `post-${post.id}`]
    })
  } else {
    context.cache.set({
      maxAge: 60, // 1 minute
      private: true
    })
  }

  return { post }
})

Passing Data Between Middleware

ts
// First middleware
const timingMiddleware = async (request, context, next) => {
  const start = Date.now()
  context.set('requestStart', start)

  const response = await next()

  const duration = Date.now() - start
  response.headers.set('X-Response-Time', `${duration}ms`)

  return response
}

// Second middleware
const loggingMiddleware = async (request, context, next) => {
  const response = await next()

  const start = context.get('requestStart')
  console.log(`${request.method} ${request.url} - ${Date.now() - start}ms`)

  return response
}

cookies

Cookie jar for reading and writing cookies.

ts
cookies: CookieJar
ts
interface CookieJar {
  get(name: string): string | undefined
  getAll(): Record<string, string>
  set(name: string, value: string, options?: CookieSetOptions): void
  delete(name: string, options?: Pick<CookieSetOptions, 'path' | 'domain'>): void
  has(name: string): boolean
}
ts
// Read a cookie
const sessionId = context.cookies.get('session')

// Set a cookie
context.cookies.set('theme', 'dark', {
  maxAge: 60 * 60 * 24 * 365, // 1 year
  path: '/',
  sameSite: 'Lax',
})

// Delete a cookie
context.cookies.delete('session')

// Check if a cookie exists
if (context.cookies.has('session')) {
  // User has a session cookie
}

Type Safety

Use TypeScript generics with context.get<T>() to type your context values:

ts
// In middleware
context.set('user', { id: '1', name: 'Alice' })

// In loader — provide the type explicitly
const user = context.get<{ id: string; name: string }>('user')

Released under the MIT License.