Skip to content

[Docs]: Middleware context initialization (Error: No value found for context) #13252

@vladinator1000

Description

@vladinator1000

Describe what's incorrect/missing in the documentation

Hey folks, I'm trying out the new middleware API, followed these docs for v7.3.0 and got an error when I tried check if a context value exists:

import {
  type unstable_MiddlewareFunction,
  unstable_createContext,
} from "react-router"
import { appContext } from "~/app-context"
import { createAuthServer } from "./auth-server"

type AuthServer = ReturnType<typeof createAuthServer>

export const authServerContext = unstable_createContext<AuthServer>()

export const authMiddleware: unstable_MiddlewareFunction = async (
  { request, context },
  next,
) => {
  let authServer = context.get(authServerContext) // <-- error here

  if (!authServer) {
    let app = context.get(appContext)
    authServer = createAuthServer(app)
    context.set(authServerContext, authServer)
  }

// ...
}
Error: No value found for context
    at unstable_RouterContextProvider.get (/home/vlady/code/gyarns/node_modules/.vite/deps_ssr/chunk-QZO7VHZY.js:1384:11)
    at authMiddleware (/home/vlady/code/gyarns/app/routes/auth/auth-middleware.ts:19:28)
    at callRouteMiddleware (/home/vlady/code/gyarns/node_modules/.vite/deps_ssr/chunk-QZO7VHZY.js:4660:24)
    at runMiddlewarePipeline (/home/vlady/code/gyarns/node_modules/.vite/deps_ssr/chunk-QZO7VHZY.js:4600:24)
    at Object.query (/home/vlady/code/gyarns/node_modules/.vite/deps_ssr/chunk-QZO7VHZY.js:3696:30)
    at handleDocumentRequest (/home/vlady/code/gyarns/node_modules/.vite/deps_ssr/chunk-QZO7VHZY.js:10745:40)
    at requestHandler (/home/vlady/code/gyarns/node_modules/.vite/deps_ssr/chunk-QZO7VHZY.js:10671:24)

Why is context.get erroring here? Are we expected to initialize all contexts that can ever be used before context.get calls?

My idea was to do a "self-initialization" pattern, where the middleware creates its own dependencies if needed. I am creating an initial context like this, but I was hoping that I don't have to add stuff to that any time I create a new context type.

// app/worker.ts
import { createRequestHandler } from "react-router"
import { createAppContext } from "./app-context"

const handler = createRequestHandler(
  // @ts-expect-error - virtual module provided by React Router at build time
  () => import("virtual:react-router/server-build"),
  import.meta.env.MODE,
)

export default {
  async fetch(request, env, ctx) {
    try {
      const appContext = await createAppContext(request, env, ctx)

      // @ts-expect-error unstable types,
      // import {AppContext} from './app-context' instead of AppLoadContext
      return handler(request, appContext)
    } catch (error) {
      console.error(error)
      return new Response("An unexpected error occurred", { status: 500 })
    }
  },
} satisfies ExportedHandler<Env>
// app-context.ts
import { unstable_createContext } from "react-router"
import { type DatabaseClient, createDbClient } from "./db/db-client.server"
import { currencyCookie } from "./internationalization/cookies"
import type { Currency } from "./internationalization/currency-context"
import { i18next } from "./internationalization/i18n.server"
import { type ServerConfig, getServerConfig } from "./lib/config.server"

export type AppContext = {
  env: Env
  executionContext: ExecutionContext
  db: DatabaseClient
  config: ServerConfig
  language: string
  currency: Currency
}

/**
 * Contains data and bindings needed by most requests.
 */
export const appContext = unstable_createContext<AppContext>()

// https://github.com/remix-run/react-router/issues/13181
// https://github.com/alexanderson1993/cloudflare-middleware/pull/1/files
export async function createAppContext(
  request: Request,
  env: Env,
  executionContext: ExecutionContext,
) {
  let map = new Map()

  let db = createDbClient(env.DB)
  let config = getServerConfig(env, request)
  let language = await i18next.getLocale(request)
  let cookie = request.headers.get("Cookie")
  let currency = (await currencyCookie.parse(cookie)) ?? "EUR"

  let contextValue: AppContext = {
    env,
    executionContext,
    db,
    config,
    language,
    currency,
  }

  map.set(appContext, contextValue)

  return map
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions