-
Notifications
You must be signed in to change notification settings - Fork 86
feat(cloudflare): add SvelteKit on Cloudflare Workers guide using Alchemy #271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
ffdecc3
1bbc076
72312e1
fed0678
fec79c0
a40bebb
2cbd291
280eb0f
e431045
7e81192
be84faa
87c9f2f
1098e37
6a68fa3
ad323cc
823cb91
1c7ef1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| --- | ||
| order: 3 | ||
| title: SvelteKit | ||
| description: Step-by-step guide to deploying a SvelteKit application to Cloudflare Workers using Alchemy with KV storage and R2 buckets. | ||
| --- | ||
|
|
||
| # SvelteKit | ||
|
|
||
| This guide walks through how to deploy a SvelteKit application to Cloudflare Workers with Alchemy. | ||
|
|
||
| ## Create a new SvelteKit Project | ||
|
|
||
| Start by creating a new SvelteKit project: | ||
|
|
||
| ```sh | ||
| bun create svelte@latest my-sveltekit-app | ||
| cd my-sveltekit-app | ||
| bun install | ||
acoyfellow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ``` | ||
acoyfellow marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| > [!NOTE] | ||
| > See Svelte's [Introduction](https://svelte.dev/docs/kit/introduction) guide for more details on SvelteKit applications. | ||
| ## Install Cloudflare Adapter and Dependencies | ||
|
|
||
| Install the required dependencies: | ||
|
|
||
| ```sh | ||
| bun add @sveltejs/adapter-cloudflare alchemy cloudflare | ||
| bun add -D @cloudflare/workers-types | ||
| ``` | ||
|
|
||
| ## Configure SvelteKit for Cloudflare | ||
|
|
||
| Update your `svelte.config.js` to use the Cloudflare adapter: | ||
|
|
||
| ```js | ||
| import adapter from '@sveltejs/adapter-cloudflare'; | ||
| import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; | ||
|
|
||
| /** @type {import('@sveltejs/kit').Config} */ | ||
| const config = { | ||
| preprocess: vitePreprocess(), | ||
| kit: { | ||
| adapter: adapter() | ||
| } | ||
| }; | ||
|
|
||
| export default config; | ||
| ``` | ||
acoyfellow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ## Create `alchemy.run.ts` | ||
|
|
||
| Create an `alchemy.run.ts` file in the root of your project: | ||
|
|
||
| ```ts | ||
| import alchemy from "alchemy"; | ||
| import { KVNamespace, R2Bucket, SvelteKit } from "alchemy/cloudflare"; | ||
|
|
||
| const app = await alchemy("my-sveltekit-app", { | ||
| stage: process.env.USER ?? "dev", | ||
| phase: process.argv.includes("--destroy") ? "destroy" : "up", | ||
| }); | ||
|
|
||
| const website = await SvelteKit("sveltekit-website", { | ||
| bindings: { | ||
| AUTH_STORE: await KVNamespace("auth-store", { | ||
| title: "my-sveltekit-auth-store", | ||
| }), | ||
| STORAGE: await R2Bucket("storage", { | ||
| allowPublicAccess: false, | ||
| }), | ||
| }, | ||
| url: true, | ||
| }); | ||
|
|
||
| console.log({ | ||
| url: website.url, | ||
| }); | ||
|
|
||
| await app.finalize(); | ||
| ``` | ||
|
|
||
| ## Configure SvelteKit Types | ||
|
|
||
| Update `src/app.d.ts` for Cloudflare bindings: | ||
|
|
||
| ```ts | ||
| declare global { | ||
| namespace App { | ||
| interface Platform { | ||
| env: { | ||
| STORAGE: R2Bucket; | ||
| AUTH_STORE: KVNamespace; | ||
| }; | ||
| context: ExecutionContext; | ||
| caches: CacheStorage & { default: Cache }; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export {}; | ||
| ``` | ||
acoyfellow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ## Using Cloudflare Bindings | ||
|
|
||
| In your SvelteKit routes, access Cloudflare resources via `platform.env`: | ||
|
|
||
| ```ts | ||
| // +page.server.ts | ||
| export const load = async ({ platform }) => { | ||
| const kvData = await platform?.env?.AUTH_STORE?.get('some-key'); | ||
| const r2Object = await platform?.env?.STORAGE?.get('some-file'); | ||
| return { kvData }; | ||
| }; | ||
| ``` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can also: import { env } from "cloudflare:workers"There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. works fine at runtime, but SK's SSR build tries to import it in node which doesn't understand the I tried using Alchemy's Any ideas for making the import work during build, or should we stick with platform.env for SSR routes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh that's very interesting. Maybe cloudflare's template works around that? Otherwise I don't have a solution. It would need to run it in miniflare. |
||
|
|
||
| ## Deploy Your Application | ||
|
|
||
| Login to Cloudflare: | ||
|
|
||
| ```sh | ||
| wrangler login | ||
| ``` | ||
|
|
||
| Run your Alchemy script to deploy the application: | ||
|
|
||
| ```sh | ||
| bun ./alchemy.run | ||
| ``` | ||
|
|
||
| It should output the URL of your deployed site: | ||
|
|
||
| ```sh | ||
| { | ||
| url: "https://your-site.your-sub-domain.workers.dev", | ||
| } | ||
| ``` | ||
|
|
||
| ## Local Development | ||
|
|
||
| To run your application locally: | ||
|
|
||
| ```sh | ||
| bun run dev | ||
| ``` | ||
|
|
||
| ## Tear Down | ||
|
|
||
| When you're finished experimenting, you can tear down the application: | ||
|
|
||
| ```sh | ||
| bun ./alchemy.run --destroy | ||
| ``` | ||
|
|
||
| This will remove all Cloudflare resources created by this deployment. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import { join } from "node:path"; | ||
| import type { Assets } from "./assets.ts"; | ||
| import type { Bindings } from "./bindings.ts"; | ||
| import { Website, type WebsiteProps } from "./website.ts"; | ||
| import type { Worker } from "./worker.ts"; | ||
|
|
||
| export interface SvelteKitProps<B extends Bindings> extends WebsiteProps<B> {} | ||
|
|
||
| // don't allow the ASSETS to be overriden | ||
| export type SvelteKit<B extends Bindings> = B extends { ASSETS: any } | ||
| ? never | ||
| : Worker<B & { ASSETS: Assets }>; | ||
|
|
||
| /** | ||
| * Deploy a SvelteKit application to Cloudflare Workers with automatically configured defaults. | ||
| * | ||
| * This resource handles the deployment of SvelteKit applications with optimized settings for | ||
| * Cloudflare Workers, including proper build commands and compatibility flags. It expects | ||
| * the SvelteKit app to be configured with the @sveltejs/adapter-cloudflare adapter. | ||
| * | ||
| * For local development, SvelteKit provides excellent built-in dev server support with | ||
| * emulated platform.env bindings for Cloudflare-specific APIs. | ||
| * | ||
| * @see https://svelte.dev/docs/kit/adapter-cloudflare - Official SvelteKit Cloudflare adapter docs | ||
| * | ||
| * @example | ||
| * // Deploy a basic SvelteKit application | ||
| * const svelteApp = await SvelteKit("my-svelte-app"); | ||
| * | ||
| * @example | ||
| * // Deploy with Cloudflare bindings | ||
| * import { D1Database, KVNamespace, R2Bucket } from "alchemy/cloudflare"; | ||
| * | ||
| * const database = await D1Database("svelte-db"); | ||
| * const sessions = await KVNamespace("sessions"); | ||
| * const storage = await R2Bucket("app-storage"); | ||
| * | ||
| * const svelteApp = await SvelteKit("svelte-with-bindings", { | ||
| * bindings: { | ||
| * DB: database, | ||
| * AUTH_STORE: sessions, | ||
| * STORAGE: storage | ||
| * } | ||
| * }); | ||
| * | ||
| * @param id - Unique identifier for the SvelteKit application | ||
| * @param props - Configuration properties for the SvelteKit deployment | ||
| * @returns A Cloudflare Worker resource representing the deployed SvelteKit application | ||
| */ | ||
| export async function SvelteKit<B extends Bindings>( | ||
| id: string, | ||
| props?: Partial<SvelteKitProps<B>>, | ||
| ): Promise<SvelteKit<B>> { | ||
|
|
||
| if (props?.compatibilityDate) { | ||
| const providedDate = new Date(props.compatibilityDate); | ||
| const minDate = new Date("2024-09-23"); | ||
| if (providedDate < minDate) { | ||
| throw new Error( | ||
| `SvelteKit compatibility date must be >= 2024-09-23 for nodejs_compat support, got ${props.compatibilityDate}` | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| return Website(id, { | ||
| ...props, | ||
| command: props?.command ?? "bun run build", | ||
| main: props?.main ?? join(".svelte-kit", "cloudflare", "_worker.js"), | ||
| assets: props?.assets ?? join(".svelte-kit", "cloudflare"), | ||
| compatibilityFlags: ["nodejs_compat", ...(props?.compatibilityFlags ?? [])], | ||
| compatibilityDate: props?.compatibilityDate, | ||
| }); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.