mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
improved astro adapter (serving api) + added documentation
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
import { Api, type ApiOptions } from "bknd";
|
import { Api, type ApiOptions } from "bknd";
|
||||||
|
import { App, type CreateAppConfig } from "bknd";
|
||||||
|
|
||||||
type TAstro = {
|
type TAstro = {
|
||||||
request: {
|
request: Request;
|
||||||
url: string;
|
|
||||||
headers: Headers;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Options = {
|
export type Options = {
|
||||||
@@ -19,3 +17,15 @@ export function getApi(Astro: TAstro, options: Options = { mode: "static" }) {
|
|||||||
headers: options.mode === "dynamic" ? Astro.request.headers : undefined
|
headers: options.mode === "dynamic" ? Astro.request.headers : undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let app: App;
|
||||||
|
export function serve(config: CreateAppConfig) {
|
||||||
|
return async (args: TAstro) => {
|
||||||
|
if (!app) {
|
||||||
|
app = App.create(config);
|
||||||
|
|
||||||
|
await app.build();
|
||||||
|
}
|
||||||
|
return app.fetch(args.request);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,10 +11,21 @@ export class AuthController implements ClassController {
|
|||||||
|
|
||||||
getMiddleware: MiddlewareHandler = async (c, next) => {
|
getMiddleware: MiddlewareHandler = async (c, next) => {
|
||||||
// @todo: ONLY HOTFIX
|
// @todo: ONLY HOTFIX
|
||||||
|
// middlewares are added for all routes are registered. But we need to make sure that
|
||||||
|
// only HTML/JSON routes are adding a cookie to the response. Config updates might
|
||||||
|
// also use an extension "syntax", e.g. /api/system/patch/data/entities.posts
|
||||||
|
// This middleware should be extracted and added by each Controller individually,
|
||||||
|
// but it requires access to the auth secret.
|
||||||
|
// Note: This doesn't mean endpoints aren't protected, just the cookie is not set.
|
||||||
const url = new URL(c.req.url);
|
const url = new URL(c.req.url);
|
||||||
const last = url.pathname.split("/")?.pop();
|
const last = url.pathname.split("/")?.pop();
|
||||||
const ext = last?.includes(".") ? last.split(".")?.pop() : undefined;
|
const ext = last?.includes(".") ? last.split(".")?.pop() : undefined;
|
||||||
if (ext) {
|
if (
|
||||||
|
!this.auth.authenticator.isJsonRequest(c) &&
|
||||||
|
["GET", "HEAD", "OPTIONS"].includes(c.req.method) &&
|
||||||
|
ext &&
|
||||||
|
["js", "css", "png", "jpg", "jpeg", "svg", "ico"].includes(ext)
|
||||||
|
) {
|
||||||
isDebug() && console.log("Skipping auth", { ext }, url.pathname);
|
isDebug() && console.log("Skipping auth", { ext }, url.pathname);
|
||||||
} else {
|
} else {
|
||||||
const user = await this.auth.authenticator.resolveAuthFromRequest(c);
|
const user = await this.auth.authenticator.resolveAuthFromRequest(c);
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo: move this to a server helper
|
||||||
isJsonRequest(c: Context): boolean {
|
isJsonRequest(c: Context): boolean {
|
||||||
//return c.req.header("Content-Type") === "application/x-www-form-urlencoded";
|
//return c.req.header("Content-Type") === "application/x-www-form-urlencoded";
|
||||||
return c.req.header("Content-Type") === "application/json";
|
return c.req.header("Content-Type") === "application/json";
|
||||||
|
|||||||
120
docs/integration/astro.mdx
Normal file
120
docs/integration/astro.mdx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
---
|
||||||
|
title: 'Astro'
|
||||||
|
description: 'Run bknd inside Astro'
|
||||||
|
---
|
||||||
|
import InstallBknd from '/snippets/install-bknd.mdx';
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Install bknd as a dependency:
|
||||||
|
<InstallBknd />
|
||||||
|
|
||||||
|
For the Astro integration to work, you also need to [add the react integration](https://docs.astro.build/en/guides/integrations-guide/react/):
|
||||||
|
```bash
|
||||||
|
npx astro add react
|
||||||
|
```
|
||||||
|
|
||||||
|
You also need to make sure to set the output to `hybrid` in your Astro config:
|
||||||
|
```js {6}
|
||||||
|
// astro.config.mjs
|
||||||
|
import { defineConfig } from "astro/config";
|
||||||
|
import react from "@astrojs/react";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
output: "hybrid",
|
||||||
|
integrations: [react()]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
If you don't want to use React with Astro, there is also an option to serve the bknd Admin UI
|
||||||
|
statically using Astro's middleware. In case you're interested in this, feel free to reach
|
||||||
|
out in [Discord](https://discord.gg/952SFk8Tb8) or open an [issue on GitHub](https://github.com/bknd-io/bknd/issues/new).
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Serve the API
|
||||||
|
Create a new catch-all route at `src/pages/api/[...api].ts`:
|
||||||
|
```ts src/pages/api/[...api].ts
|
||||||
|
import { serve } from "bknd/adapter/astro";
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
export const ALL = serve({
|
||||||
|
connection: {
|
||||||
|
type: "libsql",
|
||||||
|
config: {
|
||||||
|
url: "http://127.0.0.1:8080"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
For more information about the connection object, refer to the [Setup](/setup) guide. In the
|
||||||
|
special case of astro, you may also use your Astro DB credentials since it's also using LibSQL
|
||||||
|
under the hood. Refer to the [Astro DB documentation](https://docs.astro.build/en/guides/astro-db/) for more information.
|
||||||
|
|
||||||
|
## Enabling the Admin UI
|
||||||
|
Create a new catch-all route at `src/pages/admin/[...admin].astro`:
|
||||||
|
```jsx src/pages/admin/[...admin].astro
|
||||||
|
---
|
||||||
|
import { Admin } from "bknd/ui";
|
||||||
|
import "bknd/dist/styles.css";
|
||||||
|
|
||||||
|
import { getApi } from "bknd/adapter/astro";
|
||||||
|
|
||||||
|
const api = getApi(Astro, { mode: "dynamic" });
|
||||||
|
const user = api.getUser();
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<Admin
|
||||||
|
withProvider={{ user }}
|
||||||
|
config={{ basepath: "/admin", color_scheme: "dark" }}
|
||||||
|
client:load
|
||||||
|
/>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example usage of the API
|
||||||
|
You use the API in both static and SSR pages. Just note that on static pages, authentication
|
||||||
|
might not work as expected, because Cookies are not available in the static context.
|
||||||
|
|
||||||
|
Here is an example of using the API in static context:
|
||||||
|
```jsx
|
||||||
|
---
|
||||||
|
import { getApi } from "bknd/adapter/astro";
|
||||||
|
const api = getApi(Astro);
|
||||||
|
const { data } = await api.data.readMany("todos");
|
||||||
|
---
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{data.map((todo) => (
|
||||||
|
<li>{todo.title}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
|
On SSR pages, you can also access the authenticated user:
|
||||||
|
```jsx
|
||||||
|
---
|
||||||
|
import { getApi } from "bknd/adapter/astro";
|
||||||
|
const api = getApi(Astro, { mode: "dynamic" });
|
||||||
|
const user = api.getUser();
|
||||||
|
const { data } = await api.data.readMany("todos");
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
---
|
||||||
|
|
||||||
|
{user
|
||||||
|
? <p>Logged in as <b>{user.email}</b>.</p>
|
||||||
|
: <p>Not authenticated.</p>}
|
||||||
|
<ul>
|
||||||
|
{data.map((todo) => (
|
||||||
|
<li>{todo.title}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the [astro example](https://github.com/bknd-io/bknd/tree/main/examples/astro) for more implementation details.
|
||||||
@@ -45,6 +45,15 @@ in the future, so stay tuned!
|
|||||||
17.424c.18 2.31.18 3.394.18 4.576h-5.35c0-.258.004-.493.009-.732c.014-.743.03-1.517-.09-3.081c-.16-2.29-1.147-2.799-2.961-2.799H3.305v-4.166h8.67c2.291 0 3.437-.696 3.437-2.54c0-1.623-1.146-2.605-3.437-2.605h-8.67V2h9.624c5.189 0 7.767 2.449 7.767 6.36c0 2.926-1.814 4.834-4.265 5.152c2.069.413 3.278 1.59 3.501 3.912" clip-rule="evenodd"/><path fill="currentColor" d="M3.305 22v-3.106h5.657c.945 0 1.15.7 1.15 1.118V22z"/></svg>
|
17.424c.18 2.31.18 3.394.18 4.576h-5.35c0-.258.004-.493.009-.732c.014-.743.03-1.517-.09-3.081c-.16-2.29-1.147-2.799-2.961-2.799H3.305v-4.166h8.67c2.291 0 3.437-.696 3.437-2.54c0-1.623-1.146-2.605-3.437-2.605h-8.67V2h9.624c5.189 0 7.767 2.449 7.767 6.36c0 2.926-1.814 4.834-4.265 5.152c2.069.413 3.278 1.59 3.501 3.912" clip-rule="evenodd"/><path fill="currentColor" d="M3.305 22v-3.106h5.657c.945 0 1.15.7 1.15 1.118V22z"/></svg>
|
||||||
</div>}
|
</div>}
|
||||||
href="/integration/remix"
|
href="/integration/remix"
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Astro"
|
||||||
|
icon={<div className="text-primary-light">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<rect width="24" height="24" fill="none"/><path fill="currentColor" d="M9.24 19.035c-.901-.826-1.164-2.561-.789-3.819c.65.793 1.552 1.044 2.486 1.186c1.44.218 2.856.137 4.195-.524c.153-.076.295-.177.462-.278c.126.365.159.734.115 1.11c-.107.915-.56 1.622-1.283 2.158c-.289.215-.594.406-.892.608c-.916.622-1.164 1.35-.82 2.41l.034.114a2.4 2.4 0 0 1-1.07-.918a2.6 2.6 0 0 1-.412-1.401c-.003-.248-.003-.497-.036-.74c-.081-.595-.36-.86-.883-.876a1.034 1.034 0 0 0-1.075.843q-.013.058-.033.126M4.1 15.007s2.666-1.303 5.34-1.303l2.016-6.26c.075-.304.296-.51.544-.51c.25 0 .47.206.545.51l2.016 6.26c3.167 0 5.34 1.303 5.34 1.303L15.363 2.602c-.13-.366-.35-.602-.645-.602H9.283c-.296 0-.506.236-.645.602c-.01.024-4.538 12.405-4.538 12.405"/>
|
||||||
|
</svg>
|
||||||
|
</div>}
|
||||||
|
href="/integration/astro"
|
||||||
/>
|
/>
|
||||||
<Card
|
<Card
|
||||||
title="Cloudflare"
|
title="Cloudflare"
|
||||||
|
|||||||
@@ -108,6 +108,7 @@
|
|||||||
"integration/bun",
|
"integration/bun",
|
||||||
"integration/vite",
|
"integration/vite",
|
||||||
"integration/express",
|
"integration/express",
|
||||||
|
"integration/astro",
|
||||||
"integration/nodejs",
|
"integration/nodejs",
|
||||||
"integration/deno",
|
"integration/deno",
|
||||||
"integration/browser"
|
"integration/browser"
|
||||||
|
|||||||
@@ -5,6 +5,6 @@
|
|||||||
"dev": "mintlify dev"
|
"dev": "mintlify dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mintlify": "^4.0.269"
|
"mintlify": "^4.0.285"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,12 @@
|
|||||||
import type { APIRoute } from "astro";
|
import { serve } from "bknd/adapter/astro";
|
||||||
import { App } from "bknd";
|
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
let app: App;
|
export const ALL = serve({
|
||||||
export const ALL: APIRoute = async ({ request }) => {
|
|
||||||
if (!app) {
|
|
||||||
app = App.create({
|
|
||||||
connection: {
|
connection: {
|
||||||
type: "libsql",
|
type: "libsql",
|
||||||
config: {
|
config: {
|
||||||
url: "http://127.0.0.1:8080"
|
url: "http://127.0.0.1:8080"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await app.build();
|
|
||||||
}
|
|
||||||
return app.fetch(request);
|
|
||||||
};
|
|
||||||
|
|||||||
Reference in New Issue
Block a user