T
TanStack•6mo ago
continuing-cyan

Deployments to Cloudflare Workers

Is there a particular reason why Start don't support the cloudflare_module preset even though Nitro supports it? I assumed since Start is built on Nitro that it would support any presets that Nitro does out of the box.
11 Replies
continuing-cyan
continuing-cyanOP•6mo ago
Or is it a Vinxi issue? The error/warning seems to be from Vinxi. The app seems to build okay and I can run it locally using wrangler, so not sure how it will work when deployed.
genetic-orange
genetic-orange•6mo ago
it might magically disappear once we are off vinxi
continuing-cyan
continuing-cyanOP•6mo ago
Any ~ETA when v1 will be out without Vinxi? I'm busy looking at an alternative to Next.js and currently between TanStack Start and React Router v7 - leaning towards Start since it feels like it took the best parts of Next.js and RR, but also aware of the pre-v1 release. Cloudflare Workers support is important to me.
genetic-orange
genetic-orange•6mo ago
version without vinxi will hopefully take no more than a few weeks
adverse-sapphire
adverse-sapphire•6mo ago
I'm running cloudflare worker module in prod. Have run into some limitations. But overall works. app.config.js
export default defineConfig({
server: {
preset: "cloudflare-module",
unenv: cloudflare,
},
vite: {
build: {
rollupOptions: {
external: ["node:async_hooks"],
}
}
}
...
}
export default defineConfig({
server: {
preset: "cloudflare-module",
unenv: cloudflare,
},
vite: {
build: {
rollupOptions: {
external: ["node:async_hooks"],
}
}
}
...
}
To get cloudflare env in dev and prod I have helper function
import { getEvent } from "vinxi/http";
import { Env } from "worker-configuration";

type CfEnv = {
env: Env;
waitUntil: (promise: Promise<unknown>) => void;
};

const getDevProxy = async () => {
const cf = await import("wrangler");
return await cf.getPlatformProxy<Env>({ persist: true });
};

let ___devProxy: ReturnType<typeof getDevProxy> | undefined = undefined;

export const getCloudflareContext = async (): Promise<CfEnv> => {
const event = getEvent();
if (import.meta.env.DEV) {
// Attach the cloudflare context
if (!___devProxy) {
___devProxy = getDevProxy();
}
const proxy = await ___devProxy;
return {
env: proxy.env,
waitUntil: (promise: Promise<unknown>) => proxy.ctx.waitUntil(promise),
};
} else {
return {
// @ts-expect-error fighting with getting the types injected
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
env: event.context.cloudflare.env as Env,
waitUntil: (promise: Promise<unknown>) => {
// @ts-expect-error fighting with getting the types injected
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
event.context.cloudflare.context.waitUntil(promise);
},
};
}
};
import { getEvent } from "vinxi/http";
import { Env } from "worker-configuration";

type CfEnv = {
env: Env;
waitUntil: (promise: Promise<unknown>) => void;
};

const getDevProxy = async () => {
const cf = await import("wrangler");
return await cf.getPlatformProxy<Env>({ persist: true });
};

let ___devProxy: ReturnType<typeof getDevProxy> | undefined = undefined;

export const getCloudflareContext = async (): Promise<CfEnv> => {
const event = getEvent();
if (import.meta.env.DEV) {
// Attach the cloudflare context
if (!___devProxy) {
___devProxy = getDevProxy();
}
const proxy = await ___devProxy;
return {
env: proxy.env,
waitUntil: (promise: Promise<unknown>) => proxy.ctx.waitUntil(promise),
};
} else {
return {
// @ts-expect-error fighting with getting the types injected
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
env: event.context.cloudflare.env as Env,
waitUntil: (promise: Promise<unknown>) => {
// @ts-expect-error fighting with getting the types injected
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
event.context.cloudflare.context.waitUntil(promise);
},
};
}
};
The only problem I'm really running is that for some reason I cannot use that in server function in loader functions. Especially beforeLoad, for some reason in that context the getEvent evnet doesn't have the cloudflare there 😱 but when server function is called from client side it works But as I'm running with Clerk auth that doens't really matter, as Clerk doesn't have true SSR, so the app is more or less just SPA with server functions. And most of my backend logic is deployed as trpc server else where as I have multiple different consumers. And migrated away from RRv7. The migration from Remix was not fun. And started running into all kind of annoying issues here and there. And this comes from almost 3 years of happy remix use
genetic-orange
genetic-orange•6mo ago
as soon as we have something testable without vinxi I will let you know so you can test with cloudflare
sharp-indigo
sharp-indigo•6mo ago
I solve it by using tanstack query: const test = createServerFn({ method: 'GET' }) .handler(async () => { const ctx = await getCloudflareCtx() console.log(ctx.env.MODE) return ctx.env.MODE }) export const Route = createFileRoute('/_auth/dashboard')({ component: RouteComponent, beforeLoad: async ({ context }) => { context.queryClient.fetchQuery({ queryKey: ['test'], queryFn: test, }) }, })
correct-apricot
correct-apricot•5mo ago
Can you share a repo of this?
sharp-indigo
sharp-indigo•5mo ago
I not sure is this help, but i get the code running by doing this:
// Example: Work

// cloudflare.ts
import { getEvent, H3EventContext } from '@tanstack/react-start/server'

export const getCloudflareCtx = async (): Promise<H3EventContext['cloudflare']> => {
if (import.meta.env.DEV) {
const wrangler = await import('wrangler')
const proxy = await wrangler.getPlatformProxy<Env>()
return proxy
}

const event = getEvent()
return event.context.cloudflare
}

// index.tsx
const test = createServerFn({ method: 'GET' })
.handler(async () => {
const ctx = await getCloudflareCtx()

console.log(ctx.env.MODE)

return ctx.env.MODE
})

export const Route = createFileRoute('/')({
component: RouteComponent,
beforeLoad: async ({ context }) => {
context.queryClient.fetchQuery({
queryKey: ['test'],
queryFn: test,
})
},
})
// Example: Work

// cloudflare.ts
import { getEvent, H3EventContext } from '@tanstack/react-start/server'

export const getCloudflareCtx = async (): Promise<H3EventContext['cloudflare']> => {
if (import.meta.env.DEV) {
const wrangler = await import('wrangler')
const proxy = await wrangler.getPlatformProxy<Env>()
return proxy
}

const event = getEvent()
return event.context.cloudflare
}

// index.tsx
const test = createServerFn({ method: 'GET' })
.handler(async () => {
const ctx = await getCloudflareCtx()

console.log(ctx.env.MODE)

return ctx.env.MODE
})

export const Route = createFileRoute('/')({
component: RouteComponent,
beforeLoad: async ({ context }) => {
context.queryClient.fetchQuery({
queryKey: ['test'],
queryFn: test,
})
},
})
// Example: Not working

// index.tsx
export const Route = createFileRoute('/')({
component: RouteComponent,
beforeLoad: async ({ context }) => {
test() // direct call the server function, the getCloudflareCtx will return you undefined in the test server function.
},
})
// Example: Not working

// index.tsx
export const Route = createFileRoute('/')({
component: RouteComponent,
beforeLoad: async ({ context }) => {
test() // direct call the server function, the getCloudflareCtx will return you undefined in the test server function.
},
})
I don't know why it's work, but this is the solution i got. Sorry for that i can't make this as a repo.
adverse-sapphire
adverse-sapphire•5mo ago
this kind of setup was working at least some time a go https://github.com/Nipsuli/tanstack-start-cloudflare-worker
GitHub
GitHub - Nipsuli/tanstack-start-cloudflare-worker
Contribute to Nipsuli/tanstack-start-cloudflare-worker development by creating an account on GitHub.
eager-peach
eager-peach•3mo ago
I have it working in the latest version https://github.com/bonadio/tanstack-start-cloudflare-workers
GitHub
GitHub - bonadio/tanstack-start-cloudflare-workers
Contribute to bonadio/tanstack-start-cloudflare-workers development by creating an account on GitHub.

Did you find this page helpful?