mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-15 20:17:22 +00:00
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
This commit is contained in:
@@ -308,6 +308,11 @@ async function buildAdapters() {
|
||||
platform: "node",
|
||||
}),
|
||||
|
||||
tsup.build({
|
||||
...baseConfig("sveltekit"),
|
||||
platform: "node",
|
||||
}),
|
||||
|
||||
tsup.build({
|
||||
...baseConfig("node"),
|
||||
platform: "node",
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
|
||||
1
app/src/adapter/sveltekit/index.ts
Normal file
1
app/src/adapter/sveltekit/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./sveltekit.adapter";
|
||||
15
app/src/adapter/sveltekit/sveltekit.adapter.spec.ts
Normal file
15
app/src/adapter/sveltekit/sveltekit.adapter.spec.ts
Normal file
@@ -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 }),
|
||||
});
|
||||
});
|
||||
23
app/src/adapter/sveltekit/sveltekit.adapter.ts
Normal file
23
app/src/adapter/sveltekit/sveltekit.adapter.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { type FrameworkBkndConfig, createFrameworkApp } from "bknd/adapter";
|
||||
|
||||
type SvelteKitEnv = NodeJS.ProcessEnv;
|
||||
type TSvelteKit = {
|
||||
request: Request;
|
||||
};
|
||||
export type SvelteKitBkndConfig<Env = SvelteKitEnv> = FrameworkBkndConfig<Env>;
|
||||
|
||||
export async function getApp<Env = SvelteKitEnv>(
|
||||
config: SvelteKitBkndConfig<Env> = {},
|
||||
args: Env = process.env as Env,
|
||||
) {
|
||||
return await createFrameworkApp(config, args);
|
||||
}
|
||||
|
||||
export function serve<Env = SvelteKitEnv>(
|
||||
config: SvelteKitBkndConfig<Env> = {},
|
||||
args: Env = process.env as Env,
|
||||
) {
|
||||
return async (fnArgs: TSvelteKit) => {
|
||||
return (await getApp(config, args)).fetch(fnArgs.request);
|
||||
};
|
||||
}
|
||||
11
examples/sveltekit/.gitignore
vendored
Normal file
11
examples/sveltekit/.gitignore
vendored
Normal file
@@ -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
|
||||
26
examples/sveltekit/README.md
Normal file
26
examples/sveltekit/README.md
Normal file
@@ -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`
|
||||
52
examples/sveltekit/bknd.config.ts
Normal file
52
examples/sveltekit/bknd.config.ts
Normal file
@@ -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;
|
||||
23
examples/sveltekit/package.json
Normal file
23
examples/sveltekit/package.json
Normal file
@@ -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"
|
||||
}
|
||||
12
examples/sveltekit/src/app.html
Normal file
12
examples/sveltekit/src/app.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
14
examples/sveltekit/src/hooks.server.ts
Normal file
14
examples/sveltekit/src/hooks.server.ts
Normal file
@@ -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);
|
||||
};
|
||||
14
examples/sveltekit/src/routes/+page.server.ts
Normal file
14
examples/sveltekit/src/routes/+page.server.ts
Normal file
@@ -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 ?? [],
|
||||
};
|
||||
};
|
||||
24
examples/sveltekit/src/routes/+page.svelte
Normal file
24
examples/sveltekit/src/routes/+page.svelte
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
</script>
|
||||
|
||||
<h1>bknd + SvelteKit Example</h1>
|
||||
|
||||
<h2>Todos</h2>
|
||||
<ul>
|
||||
{#each data.todos as todo (todo.id)}
|
||||
<li>{todo.title} - {todo.done === "true" ? "Done" : "Pending"}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<h2>API Endpoints</h2>
|
||||
<ul>
|
||||
<li><a href="/api/data/entity/todos">/api/data/entity/todos</a></li>
|
||||
<li><a href="/api/auth/password/login">/api/auth/password/login</a> (POST)</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Login credentials: <code>admin@example.com</code> / <code>password</code>
|
||||
</p>
|
||||
12
examples/sveltekit/svelte.config.js
Normal file
12
examples/sveltekit/svelte.config.js
Normal file
@@ -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;
|
||||
14
examples/sveltekit/tsconfig.json
Normal file
14
examples/sveltekit/tsconfig.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
6
examples/sveltekit/vite.config.ts
Normal file
6
examples/sveltekit/vite.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { sveltekit } from "@sveltejs/kit/vite";
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
});
|
||||
Reference in New Issue
Block a user