Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
ffdecc3
Add SvelteKit resource and cloudflare-sveltekit example with KV/R2 bi…
acoyfellow Jun 3, 2025
1bbc076
feat(examples): add cloudflare-sveltekit example
acoyfellow Jun 3, 2025
72312e1
Merge branch 'sam-goodwin:main' into cloudflare-sveltekit-example
acoyfellow Jun 3, 2025
fed0678
docs: update README with additional Cloudflare examples
acoyfellow Jun 3, 2025
fec79c0
Merge branch 'sam-goodwin:main' into cloudflare-sveltekit-example
acoyfellow Jun 4, 2025
a40bebb
fix(cloudflare): update SvelteKit entry point and asset paths
acoyfellow Jun 4, 2025
2cbd291
fix(cloudflare): enhance SvelteKit configuration and compatibility ch…
acoyfellow Jun 4, 2025
280eb0f
refactor(cloudflare):
acoyfellow Jun 4, 2025
e431045
docs(cloudflare): enhance README about CF binding and types
acoyfellow Jun 4, 2025
7e81192
docs(cloudflare): add guide for deploying SvelteKit apps
acoyfellow Jun 4, 2025
be84faa
Merge branch 'sam-goodwin:main' into cloudflare-sveltekit-example
acoyfellow Jun 4, 2025
87c9f2f
Merge branch 'sam-goodwin:main' into cloudflare-sveltekit-example
acoyfellow Jun 5, 2025
1098e37
chore(cloudflare): update dependencies and enhance SvelteKit guide fo…
acoyfellow Jun 5, 2025
6a68fa3
fix: resolve biome formatting and linting violations
acoyfellow Jun 6, 2025
ad323cc
Merge branch 'sam-goodwin:main' into cloudflare-sveltekit-example
acoyfellow Jun 6, 2025
823cb91
Merge branch 'main' into cloudflare-sveltekit-example
sam-goodwin Jun 9, 2025
1c7ef1a
fix r2 adoption
sam-goodwin Jun 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,14 @@ await app.finalize();

# Examples

- CloudFlare Worker with Queue, R2 Bucket, Durable Objects, Workflows and RPC: [examples/cloudflare-worker/](./examples/cloudflare-worker/alchemy.run.ts)
- CloudFlare Worker Bootstrap with Queue and R2 End-to-End Testing: [examples/cloudflare-worker-bootstrap/](./examples/cloudflare-worker-bootstrap/index.ts)
- CloudFlare ViteJS Website + API Backend with Durable Objects: [examples/cloudflare-vite/](./examples/cloudflare-vite/alchemy.run.ts)
- CloudFlare TanStack Start Application Deployment: [examples/cloudflare-tanstack-start/](./examples/cloudflare-tanstack-start/alchemy.run.ts)
- CloudFlare RedwoodJS Application with D1 Database: [examples/cloudflare-redwood/](./examples/cloudflare-redwood/alchemy.run.ts)
- CloudFlare React Router Application Deployment: [examples/cloudflare-react-router/](./examples/cloudflare-react-router/alchemy.run.ts)
- CloudFlare Nuxt 3 Application with Pipeline and R2 Bucket: [examples/cloudflare-nuxt-pipeline/](./examples/cloudflare-nuxt-pipeline/alchemy.run.ts)
- CloudFlare SvelteKit Application with KV and R2 Storage: [examples/cloudflare-sveltekit/](./examples/cloudflare-sveltekit/alchemy.run.ts)
- Deploy an AWS Lambda Function with a DynamoDB Table and IAM Role: [examples/aws-app/](./examples/aws-app/alchemy.run.ts)

# Getting Started
Expand Down
325 changes: 325 additions & 0 deletions alchemy-web/docs/guides/cloudflare-sveltekit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
---
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:

::: code-group

```sh [bun]
bun create svelte@latest my-sveltekit-app
cd my-sveltekit-app
bun install
```

```sh [npm]
npm create svelte@latest my-sveltekit-app
cd my-sveltekit-app
npm install
```

```sh [pnpm]
pnpm create svelte@latest my-sveltekit-app
cd my-sveltekit-app
pnpm install
```

```sh [yarn]
yarn create svelte@latest my-sveltekit-app
cd my-sveltekit-app
yarn install
```

:::

> [!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:

::: code-group

```sh [bun]
bun add @sveltejs/adapter-cloudflare alchemy cloudflare
bun add -D @cloudflare/workers-types
```

```sh [npm]
npm install @sveltejs/adapter-cloudflare alchemy cloudflare
npm install --save-dev @cloudflare/workers-types
```

```sh [pnpm]
pnpm add @sveltejs/adapter-cloudflare alchemy cloudflare
pnpm add -D @cloudflare/workers-types
```

```sh [yarn]
yarn add @sveltejs/adapter-cloudflare alchemy cloudflare
yarn 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;
```

Create or update your `vite.config.ts` to configure the `cloudflare:workers` module:

```ts
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { cloudflareWorkersDevEnvironmentShim, external } from 'alchemy/cloudflare';

export default defineConfig({
plugins: [
sveltekit(),
cloudflareWorkersDevEnvironmentShim()
],
define: {
global: 'globalThis',
},
build: {
rollupOptions: {
external
}
}
});
```

## 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

Create `src/env.ts` to define your Cloudflare bindings with type safety:

```ts
import type { website } from "../alchemy.run.js";

export interface CloudflarePlatform {
env: typeof website.Env;
context: ExecutionContext;
caches: CacheStorage & { default: Cache };
}

declare global {
export type CloudflareEnv = typeof website.Env;
}

declare module "cloudflare:workers" {
namespace Cloudflare {
export interface Env extends CloudflareEnv {}
}
}
```

Then update `src/app.d.ts` to use these types:

```ts
import type { CloudflarePlatform } from './env';

declare global {
namespace App {
interface Platform extends CloudflarePlatform {}
}
}

export {};
```

Alternatively, you can define types directly in `src/app.d.ts`:

```ts
declare global {
namespace App {
interface Platform {
env: {
STORAGE: R2Bucket;
AUTH_STORE: KVNamespace;
};
context: ExecutionContext;
caches: CacheStorage & { default: Cache };
}
}
}

export {};
```

> [!NOTE]
> The `src/env.ts` approach provides better type safety since `.ts` files are type-checked, while `.d.ts` files are not. It also automatically derives types from your Alchemy configuration. The traditional `app.d.ts` approach is simpler but requires manual type definitions. Both approaches work with SvelteKit's adapter system by extending the `App.Platform` interface.
## Using Cloudflare Bindings

Both type configurations support two ways to access Cloudflare resources:

**Option 1: Direct runtime import (recommended)**
```ts
// +page.server.ts
import { env } from "cloudflare:workers";

export const load = async () => {
const kvData = await env.AUTH_STORE?.get('some-key');
const r2Object = await env.STORAGE?.get('some-file');
return { kvData };
};
```

**Option 2: Via platform parameter**
```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 };
};
```

## Log in to Cloudflare

Before you can deploy, you need to authenticate by running `wrangler login`.

::: code-group

```sh [bun]
bun wrangler login
```

```sh [npm]
npx wrangler login
```

```sh [pnpm]
pnpm wrangler login
```

```sh [yarn]
yarn wrangler login
```

:::

> [!TIP]
> Alchemy will by default try and use your wrangler OAuth token and Refresh Token to connect but see the [Cloudflare Auth](../guides/cloudflare-auth.md) for other methods.
## Deploy

Now we can run and deploy our Alchemy stack:

::: code-group

```sh [bun]
bun ./alchemy.run
```

```sh [npm]
npx tsx ./alchemy.run
```

```sh [pnpm]
pnpm tsx ./alchemy.run
```

```sh [yarn]
yarn tsx ./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:

::: code-group

```sh [bun]
bun run dev
```

```sh [npm]
npm run dev
```

```sh [pnpm]
pnpm run dev
```

```sh [yarn]
yarn run dev
```

:::

## Destroy

For illustrative purposes, let's destroy the Alchemy stack:

```sh
bun ./alchemy.run --destroy
```

You're done! Happy SvelteKit'ing 🚀
1 change: 1 addition & 0 deletions alchemy/src/cloudflare/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export * from "./r2-rest-state-store.ts";
export * from "./react-router.ts";
export * from "./redwood.ts";
export * from "./route.ts";
export * from "./sveltekit.ts";
export * from "./tanstack-start.ts";
export * from "./vectorize-index.ts";
export * from "./vectorize-metadata-index.ts";
Expand Down
28 changes: 17 additions & 11 deletions alchemy/src/cloudflare/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,27 +368,33 @@ const PipelineResource = Resource("cloudflare::Pipeline", async function <
let pipelineData: CloudflarePipelineResponse;

if (this.phase === "create") {
console.log(props);
// Check if we should adopt an existing pipeline
if (props.adopt) {
try {
// Try to create pipeline first
console.log("Creating new Cloudflare Pipeline:", pipelineName);
pipelineData = await createPipeline(api, pipelineName, props);
} catch (error) {
// If creation fails with 409 (conflict), adopt existing pipeline
if (error instanceof CloudflareApiError && error.status === 409) {
try {
// Try to create pipeline first
console.log("Creating new Cloudflare Pipeline:", pipelineName);
pipelineData = await createPipeline(api, pipelineName, props);
} catch (error) {
// If creation fails with 409 (conflict), adopt existing pipeline
if (
error instanceof CloudflareApiError &&
(error.status === 409 ||
(error.status === 400 &&
error.message.includes("Pipeline with this name already exists")))
) {
if (props.adopt) {
console.log(
"Pipeline already exists, adopting existing Cloudflare Pipeline:",
pipelineName,
);
pipelineData = await getPipeline(api, pipelineName);
} else {
// For any other error, rethrow
throw error;
}
} else {
// For any other error, rethrow
throw error;
}
} else {
pipelineData = await createPipeline(api, pipelineName, props);
}
} else {
// Update operation
Expand Down
Loading
Loading