Merge pull request #288 from bknd-io/feat/deno-and-docs

deno: add to cli starters, improve `serveStaticViaImport` and add deno to the docs
This commit is contained in:
dswbx
2025-10-25 10:37:02 +02:00
committed by GitHub
13 changed files with 246 additions and 44 deletions

View File

@@ -154,23 +154,32 @@ export async function createRuntimeApp<Args = DefaultArgs>(
* });
* ```
*/
export function serveStaticViaImport(opts?: { manifest?: Manifest }) {
export function serveStaticViaImport(opts?: {
manifest?: Manifest;
appendRaw?: boolean;
package?: string;
}) {
let files: string[] | undefined;
const pkg = opts?.package ?? "bknd";
// @ts-ignore
return async (c: Context, next: Next) => {
if (!files) {
const manifest =
opts?.manifest ||
((await import("bknd/dist/manifest.json", { with: { type: "json" } }))
.default as Manifest);
((
await import(/* @vite-ignore */ `${pkg}/dist/manifest.json`, {
with: { type: "json" },
})
).default as Manifest);
files = Object.values(manifest).flatMap((asset) => [asset.file, ...(asset.css || [])]);
}
const path = c.req.path.substring(1);
if (files.includes(path)) {
try {
const content = await import(/* @vite-ignore */ `bknd/static/${path}?raw`, {
const url = `${pkg}/static/${path}${opts?.appendRaw ? "?raw" : ""}`;
const content = await import(/* @vite-ignore */ url, {
with: { type: "text" },
}).then((m) => m.default);
@@ -183,7 +192,7 @@ export function serveStaticViaImport(opts?: { manifest?: Manifest }) {
});
}
} catch (e) {
console.error("Error serving static file:", e);
console.error(`Error serving static file "${path}":`, String(e));
return c.text("File not found", 404);
}
}

View File

@@ -10,6 +10,7 @@ import color from "picocolors";
import { overridePackageJson, updateBkndPackages } from "./npm";
import { type Template, templates, type TemplateSetupCtx } from "./templates";
import { createScoped, flush } from "cli/utils/telemetry";
import path from "node:path";
const config = {
types: {
@@ -20,6 +21,7 @@ const config = {
node: "Node.js",
bun: "Bun",
cloudflare: "Cloudflare",
deno: "Deno",
aws: "AWS Lambda",
},
framework: {
@@ -259,7 +261,8 @@ async function action(options: {
}
}
// update package name
// update package name if there is a package.json
if (fs.existsSync(path.resolve(ctx.dir, "package.json"))) {
await overridePackageJson(
(pkg) => ({
...pkg,
@@ -268,8 +271,9 @@ async function action(options: {
{ dir: ctx.dir },
);
$p.log.success(`Updated package name to ${color.cyan(ctx.name)}`);
}
{
if (template.installDeps !== false) {
const install =
options.yes ??
(await $p.confirm({

View File

@@ -93,6 +93,7 @@ export async function replacePackageJsonVersions(
}
export async function updateBkndPackages(dir?: string, map?: Record<string, string>) {
try {
const versions = {
bknd: await sysGetVersion(),
...(map ?? {}),
@@ -106,4 +107,5 @@ export async function updateBkndPackages(dir?: string, map?: Record<string, stri
},
{ dir },
);
} catch (e) {}
}

View File

@@ -0,0 +1,21 @@
import { overrideJson } from "cli/commands/create/npm";
import type { Template } from "cli/commands/create/templates";
import { getVersion } from "cli/utils/sys";
export const deno = {
key: "deno",
title: "Deno Basic",
integration: "deno",
description: "A basic bknd Deno server with static assets",
path: "gh:bknd-io/bknd/examples/deno",
installDeps: false,
ref: true,
setup: async (ctx) => {
const version = await getVersion();
await overrideJson(
"deno.json",
(json) => ({ ...json, links: undefined, imports: { bknd: `npm:bknd@${version}` } }),
{ dir: ctx.dir },
);
},
} satisfies Template;

View File

@@ -1,3 +1,4 @@
import { deno } from "cli/commands/create/templates/deno";
import { cloudflare } from "./cloudflare";
export type TemplateSetupCtx = {
@@ -15,6 +16,7 @@ export type Integration =
| "react-router"
| "astro"
| "aws"
| "deno"
| "custom";
type TemplateScripts = "install" | "dev" | "build" | "start";
@@ -34,6 +36,11 @@ export type Template = {
* adds a ref "#{ref}" to the path. If "true", adds the current version of bknd
*/
ref?: true | string;
/**
* control whether to install dependencies automatically
* e.g. on deno, this is not needed
*/
installDeps?: boolean;
scripts?: Partial<Record<TemplateScripts, string>>;
preinstall?: (ctx: TemplateSetupCtx) => Promise<void>;
postinstall?: (ctx: TemplateSetupCtx) => Promise<void>;
@@ -90,4 +97,5 @@ export const templates: Template[] = [
path: "gh:bknd-io/bknd/examples/aws-lambda",
ref: true,
},
deno,
];

View File

@@ -0,0 +1,141 @@
---
title: "Deno"
description: "Run bknd inside Deno"
tags: ["documentation"]
---
## Installation
To get started with Deno and bknd you can either install the package manually, and follow the descriptions below, or use the CLI starter:
### CLI Starter
Create a new Deno CLI starter project by running the following command:
```sh
deno run npm:bknd create -i deno
```
### Manual
Deno is fully supported as a runtime for bknd. If you plan to solely use the API, the setup is pretty straightforward.
```ts title="main.ts"
import { createAdapterApp } from "npm:bknd/adapter";
const app = await createAdapterApp({
connection: {
url: "file:data.db",
},
});
export default {
fetch: app.fetch,
};
```
## Serve the Admin UI
In order to also serve the static assets of the admin UI, you have 3 choices:
1. Use the `serveStaticViaImport` function to serve the static assets from the `bknd` package directly. Requires unstable `raw-imports`, but it's the easiest way to serve the static assets.
2. Copy the static assets to your local project and use Hono's `serveStatic` middleware.
3. Use the `adminOptions.assetsPath` property to point to a remote address with the static assets.
### `serveStaticViaImport`
The `serveStaticViaImport` function is a middleware that serves the static assets from the `bknd` package directly using dynamic raw imports. It requires the unstable `raw-imports` feature to be enabled. You can enable it by adding the following to your `deno.json`:
```json title="deno.json"
{
"unstable": ["raw-imports"]
}
```
Or by using the `--unstable-raw-imports` flag when running your script. Now create a `main.ts` file to serve the API and static assets:
```ts title="main.ts"
import { createRuntimeApp, serveStaticViaImport } from "bknd/adapter";
const app = await createRuntimeApp({
connection: {
url: "file:data.db",
},
serveStatic: serveStaticViaImport()
});
export default {
fetch: app.fetch,
};
```
In case you don't want to point your bknd dependency to the latest version, either add an `imports` section to your `deno.json` file:
```json title="deno.json"
{
"imports": {
"bknd": "npm:bknd@<VERSION>" // [!code highlight]
}
}
```
Or specify the package with the version specified to the `serveStaticViaImport` function:
```ts
const app = await createRuntimeApp({
serveStatic: serveStaticViaImport({
package: "bknd@<VERSION>", // [!code highlight]
}),
});
```
Replace `<VERSION>` with the version you want to use.
### `serveStatic` from local files
You can also serve the static assets from your local project by using Hono's `serveStatic` middleware. You can do so by copying the static assets to your local project and using the `serveStatic` middleware. First, you have to copy the static assets, by running the following command:
```bash
deno run npm:bknd copy-assets --out public
```
This will copy the static assets to the `public` directory and then serve them from there:
```ts title="main.ts"
import { createRuntimeApp, serveStatic } from "bknd/adapter";
import { serveStatic } from "npm:hono/deno";
const app = await createRuntimeApp({
connection: {
url: "file:data.db",
},
serveStatic: serveStatic({
root: "./public",
}),
});
export default {
fetch: app.fetch,
};
```
### `adminOptions.assetsPath`
You can also use the `adminOptions.assetsPath` property to point to a remote address with the static assets. This is useful in case none of the other methods work for you.
```ts title="main.ts"
import { createRuntimeApp } from "bknd/adapter";
const app = await createRuntimeApp({
connection: {
url: "file:data.db",
},
adminOptions: {
assetsPath: "https://...",
},
});
export default {
fetch: app.fetch,
};
```

View File

@@ -1,3 +1,3 @@
{
"pages": ["node", "bun", "cloudflare", "aws", "docker"]
"pages": ["node", "bun", "cloudflare", "deno", "aws", "docker"]
}

View File

@@ -61,6 +61,12 @@ If you prefer to use a runtime instead of a framework, you can choose from the f
href="/integration/cloudflare"
/>
<Card
icon={<Icon icon="simple-icons:deno" className="text-fd-primary !size-6" />}
title="Deno"
href="/integration/deno"
/>
<Card
icon={<Icon icon="tabler:lambda" className="text-fd-primary !size-6" />}
title="AWS Lambda"

View File

@@ -97,6 +97,12 @@ Start by using the integration guide for these popular frameworks/runtimes. Ther
href="/integration/bun"
/>
<Card
icon={<Icon icon="simple-icons:deno" className="text-fd-primary !size-6" />}
title="Deno"
href="/integration/deno"
/>
<Card
icon={<Icon icon="tabler:lambda" className="text-fd-primary !size-6" />}
title="AWS Lambda"

3
examples/.gitignore vendored
View File

@@ -1,2 +1,5 @@
*/package-lock.json
*/bun.lock
*/deno.lock
*/node_modules
*/*.db

11
examples/deno/deno.json Normal file
View File

@@ -0,0 +1,11 @@
{
"nodeModulesDir": "auto",
"tasks": {
"dev": "deno serve -A --watch main.ts"
},
"imports": {
"bknd": "npm:bknd@0.19.0-rc.1"
},
"links": ["../../app/"],
"unstable": ["raw-imports"]
}

View File

@@ -1,14 +1,12 @@
import { createRuntimeApp } from "bknd/adapter";
import { createRuntimeApp, serveStaticViaImport } from "bknd/adapter";
const app = await createRuntimeApp({
connection: {
url: "file:./data.db",
},
adminOptions: {
// currently needs a hosted version of the static assets
assetsPath: "https://cdn.bknd.io/bknd/static/0.15.0-rc.9/",
},
serveStatic: serveStaticViaImport(),
});
// @ts-ignore
Deno.serve(app.fetch);
export default {
fetch: app.fetch,
};

View File

@@ -1,7 +0,0 @@
{
"name": "bknd-deno-example",
"private": true,
"dependencies": {
"bknd": "file:../../app"
}
}