How to run SvelteKit & Cloudflare Page locally?
Asked Answered
D

4

7

I'm trying to create a new app using SvelteKit & Cloudflare Pages and I'm struggling with running the application locally in a proper manner. Currently, in order to get the app running locally I run 2 scripts in 2 different terminals:

  1. vite build --watch
    • this creates the cloudflare directories in the .svelte-kit directory
    • vite dev doesn't seem to create the cloudflare directories so I've used vite build
  2. wrangler pages dev .svelte-kit/cloudflare --live-reload
    • this starts the app

The above approach works, namely, it starts the app as expected. The problem is that any changes I'm making to the code are visible in the browser tab after 3-4 seconds which from a DX point of view is far from enjoying. Ideally, when running locally, any code changes should be visible in the browser tab in 1-sec-ish.

This is the first time I'm tinkering with SvelteKit & Cloudflare Pages so I'm sure I'm missing something. Therefore, the question is: how should one run a SvelteKit & Cloudflare Pages app locally "the right way"? "The right way" means that I can see the effect of my code changes in 1-sec-ish and I can run the app locally using something like wrangler or miniflare in order for the local environment to resemble as much as possible the production one.

P.s.: I've checked docs like https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site/ and https://www.npmjs.com/package/@sveltejs/adapter-cloudflare readme.md but they focus mainly on how to run the app in production
P.p.s: I believe the 2 scripts can be merged into a single one using something like concurrently but at the moment my focus is on having quick feedback when it comes to code changes
P.p.p.s.: bundling and tinkering with the javascript modules themselves is not one of my strong points yet so if you have any helpful articles, they're more than welcome

Davies answered 23/12, 2022 at 22:43 Comment(1)
The vite dev command doesn't do any bundling, but vite build bundles all the code with Rollup, which makes it comparably slow. I sympathize with your wish to make the local environment resemble the production one, but if I understand the Cloudflare pages adapter correctly it "just" compiles the SvelteKit server endpoints into a single _worker.js file in production, so if running vite dev locally differs from the production environment in functionality it will be a bug in the framework.Judejudea
A
4

I'd suggest using server hooks to setup the platform, and then use the Miniflare v3 APIs to simulate Cloudflare bindings locally. Then you can just use the standard Sveltekit/vite commands to run locally e.g. npm run dev.

Here's a template project you can use as a starter that supports D1 and KV bindings, plus the cf properties, that the env would normally get. It also uses the same file location as wrangler, so you can use regular --local commands from wrangler to handle migrations etc.

See https://github.com/sdarnell/cf-svelte/

It uses hooks.server.ts to dynamically load miniflare in dev only:

// /src/hooks.server.ts
import { dev } from '$app/environment';
import type { Handle } from '@sveltejs/kit';

export const handle = (async ({ event, resolve }) => {
    if (dev && !event.platform) {
        const mf = await import('./lib/server/miniflare');
        event.platform = await mf.setupPlatform();
    }
    return resolve(event);
}) satisfies Handle;

Then in the miniflare.ts helper file it parses a wrangler.toml file, then creates a Miniflare instance with the corresponding bindings:

...
    const kvs = Object.fromEntries((toml.kv_namespaces || []).map(d => [d.binding, d.id]));
    const dbs = Object.fromEntries((toml.d1_databases || []).map(d => [d.binding, d.database_id]));

    const root = '.wrangler/state/v3';
    const mf = new Miniflare({
        log: new Log(LogLevel.WARN),
        modules: true,
        script: '',
        d1Databases: dbs,
        kvNamespaces: kvs,
        kvPersist: `${root}/kv`,
        d1Persist: `${root}/d1`,
        compatibilityDate: toml.compatibility_date,
    });

    platform = {
        env: await mf.getBindings(),
        context: {},
        caches: {},
        cf: cachedCfDotJson,
    } as App.Platform;
...

Update: the cf-svelte project has been updated to import wrangler which does effectively the same as above, but it is now supported by Cloudflare. This eliminates the miniflare.ts file.

Update: see the Cloudflare blog post explains a bit more about the new approach: https://blog.cloudflare.com/blazing-fast-development-with-full-stack-frameworks-and-cloudflare

Abjuration answered 28/9, 2023 at 11:5 Comment(2)
Thank you!!! This is the only thing I've found that worked. Unfortunately using remote db is not yet possible.Creation
@DanielVilela That's right, accessing remote D1 is not possible without a worker proxy, or using the REST api (which is currently very slow). Here's an example of use in case you're interested in going that route: github.com/nora-soderlund/cloudflare-dynamic-bindings/blob/main/…Abjuration
R
3

Had the same requirement, and a quick Google which brought me here, didn't help; however, it turns out the solution can be found in the current Cloudflare docs:

https://developers.cloudflare.com/pages/platform/functions/local-development/

In summary, running the following should do the trick:

npx wrangler pages dev -- npm run dev
Rosser answered 24/3, 2023 at 11:44 Comment(4)
Did this actually make any cloudflare pages functionality work for you locally? Running like this doesn't seem to make platform.env available in +page.server.ts files as far as I can tell...Clarke
@Clarke is right . I'm also having problems that platform.env is not available.Canvass
@eirik This is issue I opened github.com/sveltejs/kit/issues/10389 . In short, platform.env is available on Cloudflare, but for local, you can mock it. There is an example in issue linked. In hooks, you just modify the env.Canvass
The subcommand method was deprecated and has since been removed from wrangler.Hon
N
0

It seems April 2024 has been a watershed in this matter. I'm posting this answer to help clarify the playing field to newcomers, since many, many documentation still refer to the old ways, partly even within Cloudflare's own documentation.

"Blazing fast development with full-stack frameworks and Cloudflare" (Cloudflare blogs, April 2024) has all the details. Thanks to @StephenD for mentioning the blog post in your update!

TL;DR

(it really isn't too long - you should read it! ;)

  • one can now use the "most natural way" that any framework provides (npm run dev for SvelteKit). This is done by Cloudflare wrapping their "C3" around the framework specific creator scripts. Nice approach, works!!!

  • everything seems Cloudflare Pages specific. I don't think I need to look into Workers, any more, to have a full stack project. This is important to note, since things used to be different. If you read docs prior to 2024, they might explain Pages/Worker hybrids. I think you can now safely ignore them.

  • Wrangler 3 has Miniflare baked-in. No need for developers to study about Miniflare (unless we are just curious!) All simulation tools come under wrangler CLI and are steered by wrangler.toml

  • Server-side unit testing is transitioning from Jest to Vitest. See Migrate from Miniflare 2’s test environments for details.

btw. I appreciate how Cloudflare blogs not only describe the changes, but also the technical reasons, means and aims behind them! Even future roadmaps are uncovered in just the right amount for us developers to get a good idea on what's happening. Great blogs.

I'm crafting a repo that showcases Cloudflare Pages + SvelteKit development. You are welcome to contribute & discuss there.

Necessarian answered 22/6 at 11:40 Comment(0)
T
-1

It has been fixed since Cloudflare Pages supports wrangler.toml

Just add a wrangler.toml file with your D1 bindings in it and platform should be available.

pages --remote is not yet supported as of May 2024, meaning the D1 requests won't tap in your remote database. You will need to replicate your tables locally, from the docs:

$ wrangler d1 execute YOUR_DATABASE_NAME \ --local --command "CREATE TABLE IF NOT EXISTS users ( user_id INTEGER PRIMARY KEY, email_address TEXT, created_at INTEGER, deleted INTEGER, settings TEXT);"
Teeter answered 19/5 at 7:20 Comment(3)
Hmm.. The OP didn't mention D1 at all, but was writing about seeing changes in the static (web) content. Did you read the question?Necessarian
Agreed that the user did not ask about d1 specifically but he did ask about how to reproduce his prod environment, with bindings, locally, with sveltekit and CF Pages. D1 being an example of such bindings here, adding the recently supported wrangler.toml should fix the issue and provide the expected outcome for this user.Teeter
You are right, and the -1 I placed might have been in haste. To improve your answer, I would: - define what you mean by "it" (what has been fixed?) - a more favourable link would be blog.cloudflare.com/… (the blog post introducing the change)Necessarian

© 2022 - 2024 — McMap. All rights reserved.