bknd logo

Plugins

bknd allows you to extend its functionality by creating plugins. These allows to hook into the app lifecycle and to provide a data structure that is guaranteed to be merged. A plugin is a function that takes in an instance of App and returns the following structure:

PropTypeDefault
name
string
-
schema?
(() => MaybePromise<any>)
-
beforeBuild?
(() => MaybePromise<void>)
-
onBuilt?
(() => MaybePromise<void>)
-
onServerInit?
((server: Hono<ServerEnv>) => MaybePromise<void>)
-
onBoot?
(() => MaybePromise<void>)
-
onFirstBoot?
(() => MaybePromise<void>)
-

Creating a simple plugin

To create a simple plugin which guarantees an entity pages to be available and an additioanl endpoint to render a html list of pages, you can create it as follows:

myPagesPlugin.tsx
/** @jsxImportSource hono/jsx */
import { type App, type AppPlugin, em, entity, text } from "bknd";

export const myPagesPlugin: AppPlugin = (app) => ({
   name: "my-pages-plugin",
   // define the schema of the plugin
   // this will always be merged into the app's schema
   schema: () => em({
      pages: entity("pages", {
         title: text(),
         content: text(),
      }),
   }),
   // execute code after the app is built
   onBuilt: () => {
      // register a new endpoint, make sure that you choose an endpoint that is reachable for bknd
      app.server.get("/my-pages", async (c) => {
         const { data: pages } = await app.em.repo("pages").findMany({});
         return c.html(
            <body>
               <h1>Pages: {pages.length}</h1>
               <ul>
                  {pages.map((page: any) => (
                     <li key={page.id}>{page.title}</li>
                  ))}
               </ul>
            </body>,
         );
      });
   },
});

And then register it in your bknd.config.ts file:

import type { BkndConfig } from "bknd/adapter";
import { myPagesPlugin } from "./myPagesPlugin";

export default {
   options: {
      plugins: [myPagesPlugin],
   }
} satisfies BkndConfig;

The schema returned from the plugin will be merged into the schema of the app.

Built-in plugins

bknd comes with a few built-in plugins that you can use.

syncTypes

A simple plugin that writes down the TypeScript types of the data schema on boot and each build. The output is equivalent to running npx bknd types.

bknd.config.ts
import { syncTypes } from "bknd/plugins";
import { writeFile } from "node:fs/promises";

export default {
   options: {
      plugins: [
         syncTypes({ 
            // whether to enable the plugin, make sure to disable in production
            enabled: true,
            // your writing function (required)
            write: async (et) => {
               await writeFile("bknd-types.d.ts", et.toString(), "utf-8");
            }
         }),
      ]
   },
} satisfies BkndConfig;

syncConfig

A simple plugin that writes down the app configuration on boot and each build.

bknd.config.ts
import { syncConfig } from "bknd/plugins";
import { writeFile } from "node:fs/promises";

export default {
   options: {
      plugins: [
         syncConfig({ 
            // whether to enable the plugin, make sure to disable in production
            enabled: true,
            // your writing function (required)
            write: async (config) => {
               await writeFile("config.json", JSON.stringify(config, null, 2), "utf-8");
            },
         }),
      ]
   },
} satisfies BkndConfig;

showRoutes

A simple plugin that logs the routes of your app in the console.

bknd.config.ts
import { showRoutes } from "bknd/plugins";

export default {
   options: {
      plugins: [
         showRoutes({ 
            // whether to show the routes only once (on first build)
            once: true 
         })
      ],
   },
} satisfies BkndConfig;

cloudflareImageOptimization

A plugin that add Cloudflare Image Optimization to your app's media storage.

bknd.config.ts
import { cloudflareImageOptimization } from "bknd/plugins";

export default {
   options: {
      plugins: [
         cloudflareImageOptimization({
            // the url to access the image optimization plugin
            accessUrl: "/api/plugin/image/optimize",
            // the path to resolve the image from, defaults to `/api/media/file`
            resolvePath: "/api/media/file",
            // for example, you may want to have default option to limit to a width of 1000px
            defaultOptions: {
               width: 1000,
            }
         })
      ],
   },
} satisfies BkndConfig;

Here is a break down of all configuration options:

PropTypeDefault
accessUrl?
string
/api/plugin/image/optimize
resolvePath?
string
/api/media/file
explain?
boolean
false
defaultOptions?
any
{}
fixedOptions?
any
{}
cacheControl?
string
public, max-age=31536000, immutable

When enabled, you can now access your images at your configured accessUrl. For example, if you have a media file at /api/media/file/image.jpg, you can access the optimized image at /api/plugin/image/optimize/image.jpg for optimization.

Now you can add query parameters for the transformations, e.g. ?width=1000&height=1000.

PropTypeDefault
dpr?
number
1
fit?
"scale-down" | "contain" | "cover" | "crop" | "pad"
-
format?
"auto" | "avif" | "webp" | "jpeg" | "baseline-jpeg" | "json"
-
height?
number
-
width?
number
-
metadata?
"copyright" | "keep" | "none"
copyright
quality?
number
85