From 90b4de7093e2cb660be93c3bea01deb48528869a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20R=C4=85czka?= <04_barista_pads@icloud.com> Date: Fri, 26 Dec 2025 16:55:10 +0100 Subject: [PATCH 1/6] feat: add SvelteKit adapter Add framework adapter for SvelteKit with: - `getApp()` - get bknd app instance - `serve()` - request handler for hooks.server.ts Usage in hooks.server.ts: ```typescript import { serve } from "bknd/adapter/sveltekit"; import config from "../bknd.config"; const bkndHandler = serve(config); export const handle = async ({ event, resolve }) => { if (event.url.pathname.startsWith("/api/")) { return bkndHandler(event); } return resolve(event); }; ``` Includes: - Adapter implementation (app/src/adapter/sveltekit/) - Test suite - Working example (examples/sveltekit/) - Package exports and types --- app/build.ts | 5 ++ app/package.json | 8 +++ app/src/adapter/sveltekit/index.ts | 1 + .../sveltekit/sveltekit.adapter.spec.ts | 15 ++++++ .../adapter/sveltekit/sveltekit.adapter.ts | 23 ++++++++ examples/sveltekit/.gitignore | 11 ++++ examples/sveltekit/README.md | 26 ++++++++++ examples/sveltekit/bknd.config.ts | 52 +++++++++++++++++++ examples/sveltekit/package.json | 23 ++++++++ examples/sveltekit/src/app.html | 12 +++++ examples/sveltekit/src/hooks.server.ts | 14 +++++ examples/sveltekit/src/routes/+page.server.ts | 14 +++++ examples/sveltekit/src/routes/+page.svelte | 24 +++++++++ examples/sveltekit/svelte.config.js | 12 +++++ examples/sveltekit/tsconfig.json | 14 +++++ examples/sveltekit/vite.config.ts | 6 +++ 16 files changed, 260 insertions(+) create mode 100644 app/src/adapter/sveltekit/index.ts create mode 100644 app/src/adapter/sveltekit/sveltekit.adapter.spec.ts create mode 100644 app/src/adapter/sveltekit/sveltekit.adapter.ts create mode 100644 examples/sveltekit/.gitignore create mode 100644 examples/sveltekit/README.md create mode 100644 examples/sveltekit/bknd.config.ts create mode 100644 examples/sveltekit/package.json create mode 100644 examples/sveltekit/src/app.html create mode 100644 examples/sveltekit/src/hooks.server.ts create mode 100644 examples/sveltekit/src/routes/+page.server.ts create mode 100644 examples/sveltekit/src/routes/+page.svelte create mode 100644 examples/sveltekit/svelte.config.js create mode 100644 examples/sveltekit/tsconfig.json create mode 100644 examples/sveltekit/vite.config.ts diff --git a/app/build.ts b/app/build.ts index ee6e428..3fd35ef 100644 --- a/app/build.ts +++ b/app/build.ts @@ -308,6 +308,11 @@ async function buildAdapters() { platform: "node", }), + tsup.build({ + ...baseConfig("sveltekit"), + platform: "node", + }), + tsup.build({ ...baseConfig("node"), platform: "node", diff --git a/app/package.json b/app/package.json index b3fa2f2..577e19a 100644 --- a/app/package.json +++ b/app/package.json @@ -253,6 +253,11 @@ "import": "./dist/adapter/astro/index.js", "require": "./dist/adapter/astro/index.js" }, + "./adapter/sveltekit": { + "types": "./dist/types/adapter/sveltekit/index.d.ts", + "import": "./dist/adapter/sveltekit/index.js", + "require": "./dist/adapter/sveltekit/index.js" + }, "./adapter/aws": { "types": "./dist/types/adapter/aws/index.d.ts", "import": "./dist/adapter/aws/index.js", @@ -280,6 +285,7 @@ "adapter/react-router": ["./dist/types/adapter/react-router/index.d.ts"], "adapter/bun": ["./dist/types/adapter/bun/index.d.ts"], "adapter/node": ["./dist/types/adapter/node/index.d.ts"], + "adapter/sveltekit": ["./dist/types/adapter/sveltekit/index.d.ts"], "adapter/sqlite": ["./dist/types/adapter/sqlite/edge.d.ts"] } }, @@ -309,6 +315,8 @@ "remix", "react-router", "astro", + "sveltekit", + "svelte", "bun", "node" ] diff --git a/app/src/adapter/sveltekit/index.ts b/app/src/adapter/sveltekit/index.ts new file mode 100644 index 0000000..8f0f38e --- /dev/null +++ b/app/src/adapter/sveltekit/index.ts @@ -0,0 +1 @@ +export * from "./sveltekit.adapter"; diff --git a/app/src/adapter/sveltekit/sveltekit.adapter.spec.ts b/app/src/adapter/sveltekit/sveltekit.adapter.spec.ts new file mode 100644 index 0000000..002cf27 --- /dev/null +++ b/app/src/adapter/sveltekit/sveltekit.adapter.spec.ts @@ -0,0 +1,15 @@ +import { afterAll, beforeAll, describe } from "bun:test"; +import * as sveltekit from "./sveltekit.adapter"; +import { disableConsoleLog, enableConsoleLog } from "core/utils"; +import { adapterTestSuite } from "adapter/adapter-test-suite"; +import { bunTestRunner } from "adapter/bun/test"; + +beforeAll(disableConsoleLog); +afterAll(enableConsoleLog); + +describe("sveltekit adapter", () => { + adapterTestSuite(bunTestRunner, { + makeApp: sveltekit.getApp, + makeHandler: (c, a) => (request: Request) => sveltekit.serve(c, a)({ request }), + }); +}); diff --git a/app/src/adapter/sveltekit/sveltekit.adapter.ts b/app/src/adapter/sveltekit/sveltekit.adapter.ts new file mode 100644 index 0000000..3454d72 --- /dev/null +++ b/app/src/adapter/sveltekit/sveltekit.adapter.ts @@ -0,0 +1,23 @@ +import { type FrameworkBkndConfig, createFrameworkApp } from "bknd/adapter"; + +type SvelteKitEnv = NodeJS.ProcessEnv; +type TSvelteKit = { + request: Request; +}; +export type SvelteKitBkndConfig = FrameworkBkndConfig; + +export async function getApp( + config: SvelteKitBkndConfig = {}, + args: Env = process.env as Env, +) { + return await createFrameworkApp(config, args); +} + +export function serve( + config: SvelteKitBkndConfig = {}, + args: Env = process.env as Env, +) { + return async (fnArgs: TSvelteKit) => { + return (await getApp(config, args)).fetch(fnArgs.request); + }; +} diff --git a/examples/sveltekit/.gitignore b/examples/sveltekit/.gitignore new file mode 100644 index 0000000..a9edab4 --- /dev/null +++ b/examples/sveltekit/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* +*.db diff --git a/examples/sveltekit/README.md b/examples/sveltekit/README.md new file mode 100644 index 0000000..9a5d66f --- /dev/null +++ b/examples/sveltekit/README.md @@ -0,0 +1,26 @@ +# bknd + SvelteKit Example + +This example shows how to integrate bknd with SvelteKit. + +## Setup + +```bash +bun install +bun run dev +``` + +## How it works + +1. **`bknd.config.ts`** - bknd configuration with database connection, schema, and seed data +2. **`src/hooks.server.ts`** - Routes `/api/*` requests to bknd +3. **`src/routes/+page.server.ts`** - Uses `getApp()` to fetch data server-side + +## API Endpoints + +- `GET /api/data/entity/todos` - List todos (requires auth) +- `POST /api/auth/password/login` - Login + +## Test Credentials + +- Email: `admin@example.com` +- Password: `password` diff --git a/examples/sveltekit/bknd.config.ts b/examples/sveltekit/bknd.config.ts new file mode 100644 index 0000000..ad1b9d3 --- /dev/null +++ b/examples/sveltekit/bknd.config.ts @@ -0,0 +1,52 @@ +import type { SvelteKitBkndConfig } from "bknd/adapter/sveltekit"; +import { em, entity, text, libsql } from "bknd"; +import { createClient } from "@libsql/client"; + +const schema = em({ + todos: entity("todos", { + title: text().required(), + done: text(), + }), +}); + +export default { + connection: libsql( + createClient({ + url: "file:data.db", + }) + ), + config: { + data: schema.toJSON(), + auth: { + enabled: true, + allow_register: true, + jwt: { + issuer: "bknd-sveltekit-example", + secret: "dev-secret-change-in-production-1234567890abcdef", + }, + roles: { + admin: { + implicit_allow: true, + }, + default: { + permissions: ["data.entity.read", "data.entity.create"], + is_default: true, + }, + }, + }, + }, + options: { + seed: async (ctx) => { + await ctx.app.module.auth.createUser({ + email: "admin@example.com", + password: "password", + role: "admin", + }); + + await ctx.em.mutator("todos").insertMany([ + { title: "Learn bknd", done: "true" }, + { title: "Build with SvelteKit", done: "false" }, + ]); + }, + }, +} as const satisfies SvelteKitBkndConfig; diff --git a/examples/sveltekit/package.json b/examples/sveltekit/package.json new file mode 100644 index 0000000..9bab319 --- /dev/null +++ b/examples/sveltekit/package.json @@ -0,0 +1,23 @@ +{ + "name": "bknd-sveltekit-example", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^7.0.0", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^6.0.0", + "svelte": "^5.0.0", + "typescript": "^5.0.0", + "vite": "^7.0.0" + }, + "dependencies": { + "bknd": "file:../../app", + "@libsql/client": "^0.15.0" + }, + "type": "module" +} diff --git a/examples/sveltekit/src/app.html b/examples/sveltekit/src/app.html new file mode 100644 index 0000000..84ffad1 --- /dev/null +++ b/examples/sveltekit/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/examples/sveltekit/src/hooks.server.ts b/examples/sveltekit/src/hooks.server.ts new file mode 100644 index 0000000..9993b77 --- /dev/null +++ b/examples/sveltekit/src/hooks.server.ts @@ -0,0 +1,14 @@ +import type { Handle } from "@sveltejs/kit"; +import { serve } from "bknd/adapter/sveltekit"; +import config from "../bknd.config"; + +const bkndHandler = serve(config); + +export const handle: Handle = async ({ event, resolve }) => { + // Handle bknd API requests + if (event.url.pathname.startsWith("/api/")) { + return bkndHandler(event); + } + + return resolve(event); +}; diff --git a/examples/sveltekit/src/routes/+page.server.ts b/examples/sveltekit/src/routes/+page.server.ts new file mode 100644 index 0000000..6ca8ca1 --- /dev/null +++ b/examples/sveltekit/src/routes/+page.server.ts @@ -0,0 +1,14 @@ +import type { PageServerLoad } from "./$types"; +import { getApp } from "bknd/adapter/sveltekit"; +import config from "../../bknd.config"; + +export const load: PageServerLoad = async () => { + const app = await getApp(config); + const api = app.getApi(); + + const todos = await api.data.readMany("todos"); + + return { + todos: todos.data ?? [], + }; +}; diff --git a/examples/sveltekit/src/routes/+page.svelte b/examples/sveltekit/src/routes/+page.svelte new file mode 100644 index 0000000..8973d68 --- /dev/null +++ b/examples/sveltekit/src/routes/+page.svelte @@ -0,0 +1,24 @@ + + +

bknd + SvelteKit Example

+ +

Todos

+
    + {#each data.todos as todo (todo.id)} +
  • {todo.title} - {todo.done === "true" ? "Done" : "Pending"}
  • + {/each} +
+ +

API Endpoints

+ + +

+ Login credentials: admin@example.com / password +

diff --git a/examples/sveltekit/svelte.config.js b/examples/sveltekit/svelte.config.js new file mode 100644 index 0000000..3fc56b9 --- /dev/null +++ b/examples/sveltekit/svelte.config.js @@ -0,0 +1,12 @@ +import adapter from "@sveltejs/adapter-auto"; +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + kit: { + adapter: adapter(), + }, +}; + +export default config; diff --git a/examples/sveltekit/tsconfig.json b/examples/sveltekit/tsconfig.json new file mode 100644 index 0000000..4344710 --- /dev/null +++ b/examples/sveltekit/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } +} diff --git a/examples/sveltekit/vite.config.ts b/examples/sveltekit/vite.config.ts new file mode 100644 index 0000000..80864b9 --- /dev/null +++ b/examples/sveltekit/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from "@sveltejs/kit/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [sveltekit()], +}); From a97a79f11ed68e037c8943cbe1e68f024cc998bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20R=C4=85czka?= <04_barista_pads@icloud.com> Date: Sun, 28 Dec 2025 12:47:09 +0100 Subject: [PATCH 2/6] fix(sveltekit): make adapter runtime-agnostic Remove process.env default to support all SvelteKit runtimes (Node, Cloudflare Workers, Vercel Edge, Deno). Users now pass env explicitly via $env/dynamic/private. --- .../sveltekit/sveltekit.adapter.spec.ts | 4 +-- .../adapter/sveltekit/sveltekit.adapter.ts | 26 +++++++++++++------ examples/sveltekit/src/hooks.server.ts | 3 ++- examples/sveltekit/src/routes/+page.server.ts | 3 ++- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/app/src/adapter/sveltekit/sveltekit.adapter.spec.ts b/app/src/adapter/sveltekit/sveltekit.adapter.spec.ts index 002cf27..17da756 100644 --- a/app/src/adapter/sveltekit/sveltekit.adapter.spec.ts +++ b/app/src/adapter/sveltekit/sveltekit.adapter.spec.ts @@ -9,7 +9,7 @@ afterAll(enableConsoleLog); describe("sveltekit adapter", () => { adapterTestSuite(bunTestRunner, { - makeApp: sveltekit.getApp, - makeHandler: (c, a) => (request: Request) => sveltekit.serve(c, a)({ request }), + makeApp: (c, a) => sveltekit.getApp(c, a ?? ({} as any)), + makeHandler: (c, a) => (request: Request) => sveltekit.serve(c, a ?? ({} as any))({ request }), }); }); diff --git a/app/src/adapter/sveltekit/sveltekit.adapter.ts b/app/src/adapter/sveltekit/sveltekit.adapter.ts index 3454d72..7d7ea8d 100644 --- a/app/src/adapter/sveltekit/sveltekit.adapter.ts +++ b/app/src/adapter/sveltekit/sveltekit.adapter.ts @@ -1,21 +1,31 @@ import { type FrameworkBkndConfig, createFrameworkApp } from "bknd/adapter"; -type SvelteKitEnv = NodeJS.ProcessEnv; type TSvelteKit = { request: Request; }; -export type SvelteKitBkndConfig = FrameworkBkndConfig; -export async function getApp( - config: SvelteKitBkndConfig = {}, - args: Env = process.env as Env, +export type SvelteKitBkndConfig = FrameworkBkndConfig; + +/** + * Get bknd app instance + * @param config - bknd configuration + * @param args - environment variables (use $env/dynamic/private for universal runtime support) + */ +export async function getApp( + config: SvelteKitBkndConfig = {} as SvelteKitBkndConfig, + args: Env, ) { return await createFrameworkApp(config, args); } -export function serve( - config: SvelteKitBkndConfig = {}, - args: Env = process.env as Env, +/** + * Create request handler for hooks.server.ts + * @param config - bknd configuration + * @param args - environment variables (use $env/dynamic/private for universal runtime support) + */ +export function serve( + config: SvelteKitBkndConfig = {} as SvelteKitBkndConfig, + args: Env, ) { return async (fnArgs: TSvelteKit) => { return (await getApp(config, args)).fetch(fnArgs.request); diff --git a/examples/sveltekit/src/hooks.server.ts b/examples/sveltekit/src/hooks.server.ts index 9993b77..1f4b055 100644 --- a/examples/sveltekit/src/hooks.server.ts +++ b/examples/sveltekit/src/hooks.server.ts @@ -1,8 +1,9 @@ import type { Handle } from "@sveltejs/kit"; import { serve } from "bknd/adapter/sveltekit"; +import { env } from "$env/dynamic/private"; import config from "../bknd.config"; -const bkndHandler = serve(config); +const bkndHandler = serve(config, env); export const handle: Handle = async ({ event, resolve }) => { // Handle bknd API requests diff --git a/examples/sveltekit/src/routes/+page.server.ts b/examples/sveltekit/src/routes/+page.server.ts index 6ca8ca1..8efbda8 100644 --- a/examples/sveltekit/src/routes/+page.server.ts +++ b/examples/sveltekit/src/routes/+page.server.ts @@ -1,9 +1,10 @@ import type { PageServerLoad } from "./$types"; import { getApp } from "bknd/adapter/sveltekit"; +import { env } from "$env/dynamic/private"; import config from "../../bknd.config"; export const load: PageServerLoad = async () => { - const app = await getApp(config); + const app = await getApp(config, env); const api = app.getApi(); const todos = await api.data.readMany("todos"); From 03d3b8e40754ee93b0b8f45de1c9af0e3a284bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20R=C4=85czka?= <04_barista_pads@icloud.com> Date: Sun, 28 Dec 2025 20:13:50 +0100 Subject: [PATCH 3/6] feat(sveltekit): use createRuntimeApp for built-in Admin UI --- app/src/adapter/sveltekit/sveltekit.adapter.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/adapter/sveltekit/sveltekit.adapter.ts b/app/src/adapter/sveltekit/sveltekit.adapter.ts index 7d7ea8d..14f5c9d 100644 --- a/app/src/adapter/sveltekit/sveltekit.adapter.ts +++ b/app/src/adapter/sveltekit/sveltekit.adapter.ts @@ -1,10 +1,15 @@ -import { type FrameworkBkndConfig, createFrameworkApp } from "bknd/adapter"; +import { type FrameworkBkndConfig, createRuntimeApp } from "bknd/adapter"; type TSvelteKit = { request: Request; }; -export type SvelteKitBkndConfig = FrameworkBkndConfig; +export type SvelteKitBkndConfig = FrameworkBkndConfig & { + adminOptions?: { + adminBasepath?: string; + assetsPath?: string; + }; +}; /** * Get bknd app instance @@ -15,7 +20,7 @@ export async function getApp( config: SvelteKitBkndConfig = {} as SvelteKitBkndConfig, args: Env, ) { - return await createFrameworkApp(config, args); + return await createRuntimeApp(config, args); } /** From 521d8bc6ff4a8914e7b161600ade2c207d051f0b Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 9 Jan 2026 12:59:03 +0100 Subject: [PATCH 4/6] refactor(adapter): replaced `FrameworkBkndConfig` with `RuntimeBkndConfig` --- app/src/adapter/sveltekit/sveltekit.adapter.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/src/adapter/sveltekit/sveltekit.adapter.ts b/app/src/adapter/sveltekit/sveltekit.adapter.ts index 14f5c9d..415fc33 100644 --- a/app/src/adapter/sveltekit/sveltekit.adapter.ts +++ b/app/src/adapter/sveltekit/sveltekit.adapter.ts @@ -1,15 +1,10 @@ -import { type FrameworkBkndConfig, createRuntimeApp } from "bknd/adapter"; +import { createRuntimeApp, type RuntimeBkndConfig } from "bknd/adapter"; type TSvelteKit = { request: Request; }; -export type SvelteKitBkndConfig = FrameworkBkndConfig & { - adminOptions?: { - adminBasepath?: string; - assetsPath?: string; - }; -}; +export type SvelteKitBkndConfig = Pick, "adminOptions">; /** * Get bknd app instance From 7e8b357bbb5325518b442dbe5b2cafec1a82f5a7 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 9 Jan 2026 13:11:01 +0100 Subject: [PATCH 5/6] add support for `/admin` route in backend handler and enable asset copying updated `hooks.server.ts` to handle `/admin` routes and prevent fallback for 404 responses. added `adminBasepath` config to match backend paths. introduced assets copying step via `postinstall` script and included `robots.txt` and `favicon.ico` under `static`. --- examples/sveltekit/.gitignore | 2 ++ examples/sveltekit/bknd.config.ts | 4 ++++ examples/sveltekit/package.json | 3 ++- examples/sveltekit/src/hooks.server.ts | 8 ++++++-- examples/sveltekit/static/favicon.ico | Bin 0 -> 15086 bytes examples/sveltekit/static/robots.txt | 3 +++ 6 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 examples/sveltekit/static/favicon.ico create mode 100644 examples/sveltekit/static/robots.txt diff --git a/examples/sveltekit/.gitignore b/examples/sveltekit/.gitignore index a9edab4..1380082 100644 --- a/examples/sveltekit/.gitignore +++ b/examples/sveltekit/.gitignore @@ -9,3 +9,5 @@ node_modules vite.config.js.timestamp-* vite.config.ts.timestamp-* *.db +static/manifest.json +static/assets \ No newline at end of file diff --git a/examples/sveltekit/bknd.config.ts b/examples/sveltekit/bknd.config.ts index ad1b9d3..d36c862 100644 --- a/examples/sveltekit/bknd.config.ts +++ b/examples/sveltekit/bknd.config.ts @@ -35,6 +35,10 @@ export default { }, }, }, + adminOptions: { + // this path must be the same as in `hooks.server.ts` + adminBasepath: "/admin" + }, options: { seed: async (ctx) => { await ctx.app.module.auth.createUser({ diff --git a/examples/sveltekit/package.json b/examples/sveltekit/package.json index 9bab319..53fbf94 100644 --- a/examples/sveltekit/package.json +++ b/examples/sveltekit/package.json @@ -5,7 +5,8 @@ "scripts": { "dev": "vite dev", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "postinstall": "node node_modules/.bin/bknd copy-assets --out static" }, "devDependencies": { "@sveltejs/adapter-auto": "^7.0.0", diff --git a/examples/sveltekit/src/hooks.server.ts b/examples/sveltekit/src/hooks.server.ts index 1f4b055..66f3ef4 100644 --- a/examples/sveltekit/src/hooks.server.ts +++ b/examples/sveltekit/src/hooks.server.ts @@ -7,8 +7,12 @@ const bkndHandler = serve(config, env); export const handle: Handle = async ({ event, resolve }) => { // Handle bknd API requests - if (event.url.pathname.startsWith("/api/")) { - return bkndHandler(event); + const pathname = event.url.pathname; + if (pathname.startsWith("/api/") || pathname.startsWith("/admin")) { + const res = await bkndHandler(event); + if (res.status !== 404) { + return res; + } } return resolve(event); diff --git a/examples/sveltekit/static/favicon.ico b/examples/sveltekit/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c1a946d533a71c15e51fc4cade87289ca467b57d GIT binary patch literal 15086 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 Date: Fri, 9 Jan 2026 13:26:28 +0100 Subject: [PATCH 6/6] docs: add sveltekit integration guide --- .../integration/(frameworks)/meta.json | 2 +- .../integration/(frameworks)/sveltekit.mdx | 204 ++++++++++++++++++ .../integration/introduction.mdx | 6 + docs/content/docs/(documentation)/start.mdx | 6 + 4 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 docs/content/docs/(documentation)/integration/(frameworks)/sveltekit.mdx diff --git a/docs/content/docs/(documentation)/integration/(frameworks)/meta.json b/docs/content/docs/(documentation)/integration/(frameworks)/meta.json index 82151fc..6fb7bfc 100644 --- a/docs/content/docs/(documentation)/integration/(frameworks)/meta.json +++ b/docs/content/docs/(documentation)/integration/(frameworks)/meta.json @@ -1,3 +1,3 @@ { - "pages": ["nextjs", "react-router", "astro", "vite"] + "pages": ["nextjs", "react-router", "astro", "sveltekit", "vite"] } diff --git a/docs/content/docs/(documentation)/integration/(frameworks)/sveltekit.mdx b/docs/content/docs/(documentation)/integration/(frameworks)/sveltekit.mdx new file mode 100644 index 0000000..5ef07bf --- /dev/null +++ b/docs/content/docs/(documentation)/integration/(frameworks)/sveltekit.mdx @@ -0,0 +1,204 @@ +--- +title: "SvelteKit" +description: "Run bknd inside SvelteKit" +tags: ["documentation"] +--- + +## Installation + +To get started with SvelteKit and bknd, create a new SvelteKit project by following the [official guide](https://svelte.dev/docs/kit/creating-a-project), and then install bknd as a dependency: + + + +```bash tab="npm" +npm install bknd +``` + +```bash tab="pnpm" +pnpm install bknd +``` + +```bash tab="yarn" +yarn add bknd +``` + +```bash tab="bun" +bun add bknd +``` + + + +## Configuration + + + When run with Node.js, a version of 22 (LTS) or higher is required. Please + verify your version by running `node -v`, and + [upgrade](https://nodejs.org/en/download/) if necessary. + + +Now create a `bknd.config.ts` file in the root of your project: + +```typescript title="bknd.config.ts" +import type { SvelteKitBkndConfig } from "bknd/adapter/sveltekit"; + +export default { + connection: { + url: "file:data.db", + }, +} satisfies SvelteKitBkndConfig; +``` + +See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `SvelteKitBkndConfig` type extends the base config type with the following properties: + +```typescript +export type SvelteKitBkndConfig = Pick, "adminOptions">; +``` + +## Serve the API + +The SvelteKit adapter uses SvelteKit's hooks mechanism to handle API requests. Create a `src/hooks.server.ts` file: + +```typescript title="src/hooks.server.ts" +import type { Handle } from "@sveltejs/kit"; +import { serve } from "bknd/adapter/sveltekit"; +import { env } from "$env/dynamic/private"; +import config from "../bknd.config"; + +const bkndHandler = serve(config, env); + +export const handle: Handle = async ({ event, resolve }) => { + // handle bknd API requests + const pathname = event.url.pathname; + if (pathname.startsWith("/api/")) { + const res = await bkndHandler(event); + if (res.status !== 404) { + return res; + } + } + + return resolve(event); +}; +``` + +For more information about the connection object, refer to the [Database](/usage/database) guide. + + + The adapter uses `$env/dynamic/private` to access environment variables, making it runtime-agnostic + and compatible with any deployment target (Node.js, Bun, Cloudflare, etc.). + + +## Enabling the Admin UI + +The SvelteKit adapter supports serving the Admin UI statically. First, copy the required assets to your `static` folder by adding a postinstall script to your `package.json`: + +```json title="package.json" +{ + "scripts": { + "postinstall": "bknd copy-assets --out static" // [!code highlight] + } +} +``` + +Then update your `bknd.config.ts` to configure the admin base path: + +```typescript title="bknd.config.ts" +import type { SvelteKitBkndConfig } from "bknd/adapter/sveltekit"; + +export default { + connection: { + url: "file:data.db", + }, + adminOptions: { // [!code highlight] + adminBasepath: "/admin" // [!code highlight] + }, // [!code highlight] +} satisfies SvelteKitBkndConfig; +``` + +Finally, update your `hooks.server.ts` to also handle admin routes: + +```typescript title="src/hooks.server.ts" +import type { Handle } from "@sveltejs/kit"; +import { serve } from "bknd/adapter/sveltekit"; +import { env } from "$env/dynamic/private"; +import config from "../bknd.config"; + +const bkndHandler = serve(config, env); + +export const handle: Handle = async ({ event, resolve }) => { + // handle bknd API and admin requests + const pathname = event.url.pathname; + if (pathname.startsWith("/api/") || pathname.startsWith("/admin")) { // [!code highlight] + const res = await bkndHandler(event); + if (res.status !== 404) { + return res; + } + } + + return resolve(event); +}; +``` + +## Example usage of the API + +You can use the `getApp` function to access the bknd API in your server-side load functions: + +```typescript title="src/routes/+page.server.ts" +import type { PageServerLoad } from "./$types"; +import { getApp } from "bknd/adapter/sveltekit"; +import { env } from "$env/dynamic/private"; +import config from "../../bknd.config"; + +export const load: PageServerLoad = async () => { + const app = await getApp(config, env); + const api = app.getApi(); + + const todos = await api.data.readMany("todos"); + + return { + todos: todos.data ?? [], + }; +}; +``` + +Then display the data in your Svelte component: + +```svelte title="src/routes/+page.svelte" + + +

Todos

+
    + {#each data.todos as todo (todo.id)} +
  • {todo.title}
  • + {/each} +
+``` + +### Using authentication + +To use authentication in your load functions, pass the request headers to the API: + +```typescript title="src/routes/+page.server.ts" +import type { PageServerLoad } from "./$types"; +import { getApp } from "bknd/adapter/sveltekit"; +import { env } from "$env/dynamic/private"; +import config from "../../bknd.config"; + +export const load: PageServerLoad = async ({ request }) => { + const app = await getApp(config, env); + const api = app.getApi({ headers: request.headers }); + await api.verifyAuth(); + + const todos = await api.data.readMany("todos"); + + return { + todos: todos.data ?? [], + user: api.getUser(), + }; +}; +``` + +Check the [SvelteKit repository example](https://github.com/bknd-io/bknd/tree/main/examples/sveltekit) for more implementation details. diff --git a/docs/content/docs/(documentation)/integration/introduction.mdx b/docs/content/docs/(documentation)/integration/introduction.mdx index 6330208..7119efd 100644 --- a/docs/content/docs/(documentation)/integration/introduction.mdx +++ b/docs/content/docs/(documentation)/integration/introduction.mdx @@ -27,6 +27,12 @@ bknd seamlessly integrates with popular frameworks, allowing you to use what you href="/integration/astro" /> +} + title="SvelteKit" + href="/integration/sveltekit" +/> + Create a new issue to request a guide for your framework. diff --git a/docs/content/docs/(documentation)/start.mdx b/docs/content/docs/(documentation)/start.mdx index 91bfdd7..86ca8ce 100644 --- a/docs/content/docs/(documentation)/start.mdx +++ b/docs/content/docs/(documentation)/start.mdx @@ -103,6 +103,12 @@ Start by using the integration guide for these popular frameworks/runtimes. Ther href="/integration/deno" /> +} + title="SvelteKit" + href="/integration/sveltekit" +/> + } title="AWS Lambda"