Open sujith97 opened 1 year ago
Start a new pull request in StackBlitz Codeflow.
Solid start's Cloudflare adapter supports these options in vite configuration: link
+ plugins: [
+ solid({
+ adapter: cloudflare({
+ kvNamespaces: ["MY_KV"],
+ d1Databases: ["MY_DB"]
+ }),
+ }),
+ ],
We should have something similar to Qwik's Cloudflare pages adapter
Workaround
Install following dev dependencies:
npm i @miniflare/d1 @miniflare/shared
Create a separate file to fetch the DB instance:
let getDevDb: any = () => {};
if (import.meta.env.DEV) { const { D1Database, D1DatabaseAPI } = await import("@miniflare/d1"); const { createSQLiteDB } = await import("@miniflare/shared");
let devDb: any;
getDevDb = async () => { if (!devDb) { const sqlLite = await createSQLiteDB( ".wrangler/state/d1/cloudflared1db.sqlite3" ); devDb = new D1Database(new D1DatabaseAPI(sqlLite)); } return devDb; }; }
const getDb = async (context: any) => { if (context.env.get("cloudflared1db")) { return context.env.get("cloudflared1db"); }
return getDevDb(); };
export default getDb;
3. Ignore these `@miniflare` deps in build
> /adapters/cloudflare-pages/vite.config.ts
rollupOptions: { input: ['src/entry.cloudflare-pages.tsx', '@qwik-city-plan'],
That's it. Now whereever you need db instance just fetch it using await getDb()
. Local dev server will use miniflare (local sqllite db) whereas the production builds use the actual cloudflare instances.
export const useListLoader = routeLoader$(async (context: any) => {
const db = await getDb(context);
if (db) {
const ps = db.prepare("select * from users");
const firstUser = (await ps.all())?.results?.[0];
return {
list,
user: { // DB objects have some issue if we directly send them so create a new object
...firstUser,
},
};
}
});
I have the same problem with KV. here's the workaround modified from @sujith97's:
// src/lib/kv.ts
import type { KVNamespace } from '@miniflare/kv';
let getDevKVNamespace: () => KVNamespace;
if (import.meta.env.DEV) {
const { KVNamespace } = await import('@miniflare/kv');
const { MemoryStorage } = await import('@miniflare/storage-memory');
let kvNamespace: KVNamespace;
getDevKVNamespace = () => {
if (!kvNamespace) {
kvNamespace = new KVNamespace(new MemoryStorage());
}
return kvNamespace;
};
}
export const getKVNamespace = (
platform: QwikCityPlatform,
KVBindingKey: string
): KVNamespace => {
if (platform.env) {
return platform.env[KVBindingKey] as KVNamespace;
}
return getDevKVNamespace();
};
// src/routes/index.tsx
export const useData = routeLoader$(async ({ platform }) => {
const MY_KV = getKVNamespace(platform, 'MY_KV');
return await MY_KV.list();
});
Has anyone been able to get this working with R2 yet as well or only KV?
I'd like to note that from the workaround comment above for D1, https://github.com/BuilderIO/qwik/issues/3345#issuecomment-1475385715
For whatever reason, my builds began to fail recently which had something to do with the miniflare package,
Putting this in the main vite.config.ts
solved the build issue. :)
build: {
rollupOptions: {
external: ['@miniflare/d1', '@miniflare/shared'],
},
},
As far as I can tell miniflare code is not being bundled, bundle sizes do not change even if I just comment out all the code between if (import.meta.env.DEV)
stuff. So I'm not really entirely sure what is going on š¤
For instance, if I were to completely remove the build.rollupOptions.external
configuration in both places, and replace if (import.meta.env.DEV) {
with if (false) { ...
, vite seems to know not to bundle the miniflare code and there is no error. With import.meta.env.DEV
there is one.
It seems I just have a gap in my knowledge for this lmao
I also can not get this to work, the main issue I believe is the qwik-auth package, I have installed from npm run qwik add auth
this is the original way I had it set up :
export const { onRequest, useAuthSession, useAuthSignin, useAuthSignout } =
serverAuth$(({ env, platform }) => ({
secret: env.get("PRIVATE_AUTH_SECRET"),
adapter: DrizzleAdapter(platform.env?.DB),
Is there a way to set default "Env" or "PLATFORM" to be of the type needed by miniflare or any other package ? instead of needing to call
const db = await getDb(platform.env.YOUR_DB)
So that when the Vite npm run dev
starts up it check for theimport.meta.DEV
so it loads with the PLATFORM
necessary ?
Qwik-auth is very simple: https://github.com/BuilderIO/qwik/blob/main/packages/qwik-auth/src/index.ts But I am not an export on it. What needs to be changed there? Can you propose a PR?
@mhevery If I ever find the time I wouldn't mind looking into such a thing, although it does seem a little complex.
If you look at the source code for what solid-start
does for Cloudflare Pages, it seems they apply some deeper integration with Miniflare.
This is what allows for the platform
variable to have the appropriate Cloudflare bindings like D1, R2, KV etc in the dev environment i.e. npm run dev
, like what @serbyxyz is asking.
Such a fix is not necessarily specific to qwik-auth
, rather, it would likely need to be applied somehow with Qwik's Cloudflare adapter. (@builder.io/qwik-city/adapters/cloudflare-pages/vite
I believe)
Currently it seems Qwik just wants you to npm run build
then wrangler pages dev ./dist
, but this is not ideal for rapid development.
As an aside, it may be possible to run wrangler pages dev
and vite dev
at the same time and get access to platform
, but I haven't really looked to much into this. Integrating miniflare
to the Cloudflare Pages adapter seems like the most elegant solution.
yes how @austin-meadows explained it. it has nothing to do with qwik-auth just realized it when I had to modify qwik-auth to change the drizzle adapter from drizzle D1 to cloudflare D1. Which would work by just adding {env, platform} to the plugin. Instead of using the getDB() inside of the plugin
okay so if you add this to package.json or just run it in terminal :
@austin-meadows
npx wrangler pages dev --proxy 5173 --compatibility-date=2023-10-06 -- npm run dev
( if you don't add the --proxy
also just running the npm run build
and then the npm run serve
would work but you need to add the wrangler.toml,
it doesn't "dev mode" --reload though
assuming you have wrangler v3+ it will do all the miniflare "V3" / workerd ... stuff for you and allows you to do the platform.env anywhere. just need to add a wrangler.toml with your bindings etc.
The docs are very mixed between versions like the run flags --local etc but there is a list of deprecated ones https://developers.cloudflare.com/workers/wrangler/deprecations/
... now to figure out what all this proxy errors are with it (the authjs adapter d1-adapter not drizzle) also complains about some file
that isnt getting bundled in the ... -- npm run dev
.
I tried in the plugin@auth.ts doing this instead of using the getDb() in there since its much less modifications
serverAuth$(({ env, platform }) => ({
secret: env.get("PRIVATE_AUTH_SECRET"),
adapter: DrizzleAdapter(drizzle(platform.env?.DB, { schema: sqliteDB })),
it doesn't give the bundle error just the proxy error which it seems like might be something with the provider callback ( which I've tried all the ip addresses 127.0.0.1:8788 :5173... localhost: ...)
but that's as far as I got so far
I actually found something interesting when messing about with Hono and Cloudflare workers ( very similar to FastAPI which I know well) ā¦ but hono does something that is typescript specific with global variables. That maybe could be added to the cloudflare or any other adapter. The platform works with the wrangler v3 + with the adapter. @mhevery , But Iām not sure if the adapter is looking for wrangler.toml file , in the case of cloudflare , not sure what vercel or others have . But if the adapter reads the wrangler.toml file and in some way āloadsā or caches the bindings into the env, ( similar to how in tsconfig you can add the types to cloudflare and itās āgloballyā available). If the adapter reads the wrangler.toml and makes the bindings available in that āglobalā way then I think it might solve a lot of the ātypeāing problems.
for example it seems itās pretty standard to make a src/db directory similar to the qwik insights (using libSQL/Turso based on env vars). I noticed it has this getDb() function/ method ā¦
So with most using drizzle for example purpose , You can do the same IE
Export const db=drizzle(client,{schema:myschema})
But with cloudflare D1 client = platform.env.MyBinding
but platform canāt be used or ātypeād in a seperate directory it always needs to be called inside an event / fetch handler style function by using the platform thatās provided in the method / function.
So in every routeLoader or routAction the db needs to be initiated for d1 , vs importing db from src/db ā¦
I could totally be missing something here that might be common knowledge that Iām missing out on, that lead me down this path.( I have no problems with any other way of doing this except on cloudflare d1 ā¦ yes itās beta so there is not much docs examples to go off).
I did a test run of installing qwik through the cloudflare C3 method and it does add extra functions to the package.json but I think the main thing thatās missing is the loading or parsing of the wrangler.toml file which could add the bindings as global vars or something like name space? In typescript ? Iām not very good at typescript so I can be off on that.
A good investigation, but I have to be honest with you it's ower my head. Any chance someone could propose a PR?
I think if your going to investigate , from what I have found out, looking at the qwik insights repo, make a branch off that. Last time I looked at it you have the getDB for turso / libSQL already in place and you already have the Qwik-auth plug-in installed. But Iām not sure if Iām production mode you have an adapter connected to it or not?
Here is some findings I tried and maybe you can reproduce results or get better āinsightā into the subject ( for anyone that is stuck on this ).
Before even starting install qwik cloudflare plug-in via the builder.io - qwik adapters method and then install it via the cloudflare -C3 method , then compare the difference between the package.json āscriptsā, first you will notes a āpages:devā that is similar to the qwik - cloudflare adapters run dev that points to the .entry files. Second you will notice that the C3 create method adds a wrangler.toml file which is needed for the newer wrangler v3 which automatically has mini flare when in NODE_ENV= (!ānotā āproductionā) So you can do .local or .dev whatever with your env files ( it gets confusing in the wrangler.toml) when you set( see below ) [env.production] [env.dev] ā¦ or [vars] [vars.production] still confused about dev.vars file instead of .env file I think itās the same or Vite will load the env for you but not the dev.vars . But one thing you will find in cloudflare docs is that your [d1database.bindings] needs a preview_id=ācan be same as database_idā
[env.production] NODE_ENV= āproductionā [env.dev] NODE_ENV= ādevelopmentā
^^^^ But for example Vite has automatic like search or parse for .env .env.local so I tried add [env.local] NODE_ENV= ādevelopmentā [env.local.d1database] database_name= ādatabaseā database_id= āyour db idā database_binding = ā your DB binding preview_id= ā I think you can make it whatever you want but I copied the database_id hereā
but in env.local file I also added NODE_ENV=development
According to the wrangler v3 docs you donāt need to use ālocal anymore when running pages:dev , I had issue with qwik page loading using both install methods without āproxy ā(Vite port)5173ā then it would load the page And at the end of the pages:dev command adding ā npm run dev ( I think I posted link / command in previous post)
but with the wrangler.toml file and running npm run pages:dev you will notice a new .sqlite file under the .wrangler/cache( or something)/{the preview_id you gave it} in the root of your project folder
So I think the main thing is the adapter install or add command just needs to be modified by adding / creating a wrangler.toml file to it similar to how C3 does it (thatās why I mention installing both for investigation) to compare whatās different ( I think itās just whatās different in the updated wrangler v3)
But for me the database did not get migrated even by running the migrate commands for d1 with the ālocal flag in wrangler d1 migrate ālocal ( I think itās like that) it says it migrates but in sqlite viewer extension still says 0 tables but if you run migrate again it says nothing to migrate ā¦
so what I ended up doing was just using drizzle to push:sqlite -> that .wrangler/cacheā¦ .sqlite file using better-sqlite adapter and ended up just making the getDB() function when NODE_ENV ===ādevelopmentā use drizzle( better-sqlite3) and when production use drizzle( d1 )
but you canāt just add better-sqlite3 with top-level import because when you push to GitHub ( and cloudflare runs build) it will complain about better-sqlite ( even if you have node_compat= true in cloudflare settings )ā¦ the drizzle adapter is fine itās just the import of Database from /better-sqlite
so a work around was to make second function that you do like const Database = await import (ābetter-sqliteā).default
but since Database is a class and it expects it to be async it needs to be awaited thatās why it needs to be in its own function and then called from inside of getDB ( when I get to pc I will paste example) Iām not very good with typescript so maybe it can be refined , itās mainly that it will complain inside of the auth-js adapter that type of Database
** another thing to note is that the drizzle d1 adapter already has mini flare added to it ā¦
Hmm Iām not the best at this pr stuff, I donāt know where to beginā¦ I know there is some sort of format that needs to be done š¤·āāļøSorry kind of new to all this āworking in teams / GitHubā stuff :/ ( point me in the right direction and I might figure it out but will probably be scrutinized the first couple times)
** I donāt want my lack of experience on PR overshadow the actual issue so if I figure it out bear with me, I also need to sit down and actually think this out because I kind of just write things as they come into my head, but I think their is enough of that here that I can compile something more concise
On another note, based off the qwik-insights I think I tried connecting an adapter with libSQL - turso to the plug-in auth and the drizzle adapter didnāt play nice ( I took the fact it was netlify into consideration, and this is not exactly part of cloudflare topic / issue but does highlight some sort of ābugā in the db adapter in auth js and drizzle adapter)
@mhevery you wrote angular you wrote qwik , trust me we just not on the same page, if I figure out the proper way to show you the issue ( Iām not at your level by any means ) , but I know that you can fix this in 15 minutes or less, it some trivial thing that I canāt explain properly without showing you where I hit walls, and those walls are typescript , because I look at hono and other repos and itās something trivial like having a bindings.d.ts file that is somewhat equivalent to the qwik city PLATFORM = ā¦ but unless I figure out a way to show you all my brute force attempts and where they fail , itās always going to seem over your head the communication on my end is poor because im no where near your level of understanding the Framework you madeā¦ so I can sit here and try in every way I know how to explain it but we never gonna get anywhereā¦ because my typescript is mediocre at best.
But there are about 3 different issues that I have found that play into each other
Like hono and even itty-router anything that uses cloudflare does something ( similar to PLATFORM) using a .d.ts file called bindings or environment or Env, that in some shape or form needs to be adopted into the cloudflare pages plug-in ( if itās something You want working properly, with all your other āintegrationsā) But maybe it already works that way just something Iām missing or once you see what Iām talking about your going to be like oh , that just needs to be like this, but I mean only the person that wrote the thing( or has the level of understanding ) is going to find the āohā moment hehā¦ and itās some thing so simple I think but itās a huge hurdle , let me figure out a way to make a good example repo on GitHub where I can try to highlight ( or have a compare code section) that might help, because the project in working on I canāt just show ( NDA) so Iāll try to just make a blank project and use one of. My personal cloudflare accounts to get a working mock up, youāll see itās some trivial thing
** remember I have this working , and using builder.io as cms and using figma -> builder plug-in ( a lot on my plate ATM) but the issue comes when I need to come out of production mode and into dev mode to try and build the Structured data plug-in for builder.io
** the structured data plug-in I got to be able to use dev mode to with drizzle ( or any adapter, drizzle because i can just change some env vars to make it reusable )
but the auth-js unless builder.io is going to integrate a sort of plug-in ( yes I can use supabase and itās auth system off CF workers ) I can maybe use turso ( even though the auth-js plug-in complains about types with drizzle adapter as an adapter for auth js) I got it working , but Iām almost at the point of monkey patching it with python ( which Iām good at) but that breaks the whole work flow ā¦
Take this for example https://github.com/OultimoCoder/cloudflare-planetscale-hono-boilerplate/blob/main/bindings.d.ts
But look at the over all structure of the project ( you donāt need to look beyond the root directory)
but hono uses ācā as context which is equivalent to āplatformā in qwik city ā¦ but that file right there I think is what where this issue stems from , like where do we implment something like this in qwik?
** like it seems like this would need to be tied into the entry file for pages , or imported somehow I could be way of basis on that though
Thanks for the workaround, @sujith97! I tried it out and i found an alternative way to work with a local database (note: i've only tested this with Cloudflare D1):
qwikCity
plugin supports a platform
property.better-sqlite
if you're already using the wrangler v3
CLI (in conjunction with the miniflare
API).Note: This assumes that you've populated a local version of the database via the wrangler
CLI. Though, It seems that wrangler d1 migrations create
isn't useful for the moment , since it doesn't do anything(?). Instead, you could use wrangler d1 execute *database_name* --local --file path/to/file.sql
.
Thanks to this, we can add the database connection once in vite.config.ts
:
import { defineConfig, loadEnv } from "vite";
import { qwikVite } from "@builder.io/qwik/optimizer";
import { qwikCity } from "@builder.io/qwik-city/vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { Miniflare, MiniflareOptions } from 'miniflare'
export default defineConfig(async ({ mode }) => {
let platform = {}
if (mode === 'development') {// or mode: 'ssr'
// The following miniflare statements are heavily inspired by the source of the `wrangler execute` CLI command.
// @see https://github.com/cloudflare/workers-sdk/blob/24d1c5cf3b810e780df865a0f76f1c3ae8ed5fbe/packages/wrangler/src/d1/execute.tsx#L236-L251
/**
* The `d1Persist` directory is created after you've executed `wrangler execute *database_name* --local`.
*/
const d1Persist = '.wrangler/state/v3/d1'
const mf = new Miniflare({
modules: true,
script: "",
d1Persist,
d1Databases: { DB: 'database-id' }, // the value of `database_id` or `preview_database_id` in your `wrangler.toml`. Depends on what values that are / were present when you called `wrangler execute` mentioned earlier.
});
const db = await mf.getD1Database('DB') // The key of the `d1Databases` object prop.
platform = {
env: { DB: db }
}
}
return {
build: {
rollupOptions: {
external: new RegExp('stories.tsx')
},
},
devTools: {
clickToSource: false,
},
plugins: [qwikCity({
platform // Here we pass the mocked `platform` property.
}), qwikVite(), tsconfigPaths()],
preview: {
headers: {
"Cache-Control": "public, max-age=600",
},
},
}
});
Then, you should be able to use the database as follows:
export const useListLoader = routeLoader$(async (context: any) => {
const db = context.platform.env.DB
if (db) {
const ps = db.prepare("select * from users");
const firstUser = (await ps.all())?.results?.[0];
return {
list,
user: { // DB objects have some issue if we directly send them so create a new object
...firstUser,
},
};
}
});
I wonder if Hono's new vite dev server package can provide any guidance here: https://github.com/honojs/vite-plugins/blob/main/packages/dev-server
FYI, These could be used to address this issue:
getPlatformProxy()
: https://developers.cloudflare.com/workers/wrangler/api/#getplatformproxy
Which component is affected?
Qwik Rollup / Vite plugin
Describe the bug
The current Cloudflare pages setup makes it hard to develop qwik-city with Cloudflare page functions. Cloudflare has D1, KV etc objects that should be accessible during the development mode but at the moment we should first build. The build drops the static files to
dist
folder which can then be accessed using Cloudflare pages. Ideally, we should be able to have the HMR and wrangler reload during local development.Can we provide guidance on how to develop locally with Cloudflare and qwik-city vite? (https://developers.cloudflare.com/pages/platform/functions/local-development/)
Reproduction
https://stackblitz.com/edit/qwik-starter-joopk8?file=src%2Froutes%2Findex.tsx
Steps to reproduce
No response
System Info
Additional Information
No response