nksaraf / vinxi

The Full Stack JavaScript SDK
https://vinxi.vercel.app
MIT License
1.33k stars 56 forks source link

Context is not available in cf worker #233

Open FlatMapIO opened 4 months ago

FlatMapIO commented 4 months ago

In solid-start 0.6.0, getEvent() works fine in the development environment to get the request event context, but when deployed to a cloudflare worker, the following occurs.

image
compatibility_flags = [ "nodejs_compat", "streams_enable_constructors" ]

app.config.ts

import { defineConfig } from '@solidjs/start/config'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
    middleware: './src/middleware.ts',

    server: {
        // ===================================================
        // TARGET=cloudflare-pages
        // TARGET=cloudflare-module
        // TARGET=vercel-edge
        experimental: {
            asyncContext: true,
        },
        dev: 'DEV' in process.env,
    },

    // @ts-ignore
    vite(option) {
        return {
            envPrefix: ['X_', 'VITE_'],
            plugins: [tsconfigPaths()],
            build: {
                rollupOptions: {
                    external: ['node:async_hooks'],
                },
            },
        }
    },
})
nksaraf commented 4 months ago

Hmm weird, i think some of our docs sites are deployed on CF worker and we don't see this issue right @ryansolid

sabercoy commented 3 months ago

Came here to report this, to shed more light: The error occurs in CF environment if the event.nativeEvent is not passed to vinxi/http functions

export default createMiddleware({
  onRequest: [
    (event) => {
      const headers = getRequestHeaders()
      console.log(headers)
    }
  ]
})

It works if passed in:

export default createMiddleware({
  onRequest: [
    (event) => {
      const headers = getRequestHeaders(event.nativeEvent)
      console.log(headers)
    }
  ]
})

The same issue occurs with <HttpStatusCode />: if you go to a 404 page that renders this you will get a 500 error of Context is not available

Not passing event.nativeEvent will work in dev and also a local build, but not when ran in CF (or wrangler)

billyhawkes commented 3 months ago

Works for me @sabercoy, thanks!

Simply appending event as the first parameter to each "vinxi/http" function seems to work. Here are some examples:

export default eventHandler(async (event) => {
    const url = getRequestURL(event);
        const storedState = getCookie(event, "state");
        return sendRedirect(event, url.toString());
})

I don't have access to event.nativeEvent though.

FlatMapIO commented 3 months ago

I only use getRequestEvent now, and I have not configured any options related to als. The cf worker works fine

defineConfig({
  middleware: './src/middleware.ts',
  // @ts-ignore
  vite(option) {
    return {
      plugins: [tsconfigPaths()],
    }
  },
})

middleware.ts

export default createMiddleware({
  onRequest: [
    async (event) => {
      if (import.meta.env.DEV) {
        // @ts-ignore
        if (!globalThis.__cf) {
          console.log('# attach cloudflare development environment')
          const cf = await import('wrangler')
          const proxy = await cf.getPlatformProxy<Env>({ persist: true })
          // @ts-ignore
          globalThis.__cf = proxy
        }
        if (!event.locals.cloudflare) {
          event.locals.cloudflare = {
            env: {
              // ...process.env
              // @ts-ignore
              ...globalThis.__cf.env,
            },
          } as any
        }
      } else {
        event.locals.cloudflare = event.nativeEvent.context.cloudflare
      }
    },
  ],
  onBeforeResponse: [(event, { body }) => {}],
})

server-context.ts

export type Env = {
  GOOGLE_CLIENT_ID: string
  GOOGLE_CLIENT_SECRET: string
  GOOGLE_REDIRECT_URL: string

  // =================================================
  KV: KVNamespace
  DB: D1Database
  AI: any
}
declare module '@solidjs/start/server' {
  interface RequestEventLocals {
    env: Env
    cloudflare: {
      request: Request
      env: Env
      context: ExecutionContext
    }
  }
}

export function getServerContext() {
  if (!isServer) {
    throw new Error('You must call this function only in server environment')
  }

  const event = getRequestEvent()!
  const cf = event.locals.cloudflare
  return {
    env: cf.env,
    cloudflare: cf,
    event,
  }
}