mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-17 12:56:05 +00:00
Merge pull request #59 from bknd-io/feat/adapter-auth-env
Adapter: verified auth & env-specific construction
This commit is contained in:
@@ -180,6 +180,14 @@ function baseConfig(adapter: string): tsup.Options {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// base adapter handles
|
||||||
|
await tsup.build({
|
||||||
|
...baseConfig(""),
|
||||||
|
entry: ["src/adapter/index.ts"],
|
||||||
|
outDir: "dist/adapter"
|
||||||
|
});
|
||||||
|
|
||||||
|
// specific adatpers
|
||||||
await tsup.build(baseConfig("remix"));
|
await tsup.build(baseConfig("remix"));
|
||||||
await tsup.build(baseConfig("bun"));
|
await tsup.build(baseConfig("bun"));
|
||||||
await tsup.build(baseConfig("astro"));
|
await tsup.build(baseConfig("astro"));
|
||||||
|
|||||||
@@ -148,6 +148,10 @@
|
|||||||
"import": "./dist/adapter/cloudflare/index.js",
|
"import": "./dist/adapter/cloudflare/index.js",
|
||||||
"require": "./dist/adapter/cloudflare/index.cjs"
|
"require": "./dist/adapter/cloudflare/index.cjs"
|
||||||
},
|
},
|
||||||
|
"./adapter": {
|
||||||
|
"types": "./dist/types/adapter/index.d.ts",
|
||||||
|
"import": "./dist/adapter/index.js"
|
||||||
|
},
|
||||||
"./adapter/vite": {
|
"./adapter/vite": {
|
||||||
"types": "./dist/types/adapter/vite/index.d.ts",
|
"types": "./dist/types/adapter/vite/index.d.ts",
|
||||||
"import": "./dist/adapter/vite/index.js",
|
"import": "./dist/adapter/vite/index.js",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { type FrameworkBkndConfig, createFrameworkApp } from "adapter";
|
import { type FrameworkBkndConfig, createFrameworkApp } from "adapter";
|
||||||
import { Api, type ApiOptions, type App } from "bknd";
|
import { Api, type ApiOptions, type App } from "bknd";
|
||||||
|
|
||||||
export type AstroBkndConfig = FrameworkBkndConfig;
|
export type AstroBkndConfig<Args = TAstro> = FrameworkBkndConfig<Args>;
|
||||||
|
|
||||||
type TAstro = {
|
type TAstro = {
|
||||||
request: Request;
|
request: Request;
|
||||||
@@ -13,18 +13,20 @@ export type Options = {
|
|||||||
host?: string;
|
host?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getApi(Astro: TAstro, options: Options = { mode: "static" }) {
|
export async function getApi(Astro: TAstro, options: Options = { mode: "static" }) {
|
||||||
return new Api({
|
const api = new Api({
|
||||||
host: new URL(Astro.request.url).origin,
|
host: new URL(Astro.request.url).origin,
|
||||||
headers: options.mode === "dynamic" ? Astro.request.headers : undefined
|
headers: options.mode === "dynamic" ? Astro.request.headers : undefined
|
||||||
});
|
});
|
||||||
|
await api.verifyAuth();
|
||||||
|
return api;
|
||||||
}
|
}
|
||||||
|
|
||||||
let app: App;
|
let app: App;
|
||||||
export function serve(config: AstroBkndConfig = {}) {
|
export function serve<Context extends TAstro = TAstro>(config: AstroBkndConfig<Context> = {}) {
|
||||||
return async (args: TAstro) => {
|
return async (args: Context) => {
|
||||||
if (!app) {
|
if (!app) {
|
||||||
app = await createFrameworkApp(config);
|
app = await createFrameworkApp(config, args);
|
||||||
}
|
}
|
||||||
return app.fetch(args.request);
|
return app.fetch(args.request);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { CreateAppConfig } from "bknd";
|
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { serveStatic } from "hono/cloudflare-workers";
|
import { serveStatic } from "hono/cloudflare-workers";
|
||||||
import type { FrameworkBkndConfig } from "../index";
|
import type { FrameworkBkndConfig } from "../index";
|
||||||
@@ -6,10 +5,9 @@ import { getCached } from "./modes/cached";
|
|||||||
import { getDurable } from "./modes/durable";
|
import { getDurable } from "./modes/durable";
|
||||||
import { getFresh, getWarm } from "./modes/fresh";
|
import { getFresh, getWarm } from "./modes/fresh";
|
||||||
|
|
||||||
export type CloudflareBkndConfig<Env = any> = Omit<FrameworkBkndConfig, "app"> & {
|
export type CloudflareBkndConfig<Env = any> = FrameworkBkndConfig<Context<Env>> & {
|
||||||
app: CreateAppConfig | ((env: Env) => CreateAppConfig);
|
|
||||||
mode?: "warm" | "fresh" | "cache" | "durable";
|
mode?: "warm" | "fresh" | "cache" | "durable";
|
||||||
bindings?: (env: Env) => {
|
bindings?: (args: Context<Env>) => {
|
||||||
kv?: KVNamespace;
|
kv?: KVNamespace;
|
||||||
dobj?: DurableObjectNamespace;
|
dobj?: DurableObjectNamespace;
|
||||||
};
|
};
|
||||||
@@ -21,15 +19,15 @@ export type CloudflareBkndConfig<Env = any> = Omit<FrameworkBkndConfig, "app"> &
|
|||||||
html?: string;
|
html?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Context = {
|
export type Context<Env = any> = {
|
||||||
request: Request;
|
request: Request;
|
||||||
env: any;
|
env: Env;
|
||||||
ctx: ExecutionContext;
|
ctx: ExecutionContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function serve(config: CloudflareBkndConfig) {
|
export function serve<Env = any>(config: CloudflareBkndConfig<Env>) {
|
||||||
return {
|
return {
|
||||||
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const manifest = config.manifest;
|
const manifest = config.manifest;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createRuntimeApp } from "adapter";
|
|||||||
import { App } from "bknd";
|
import { App } from "bknd";
|
||||||
import type { CloudflareBkndConfig, Context } from "../index";
|
import type { CloudflareBkndConfig, Context } from "../index";
|
||||||
|
|
||||||
export async function getCached(config: CloudflareBkndConfig, { env, ctx }: Context) {
|
export async function getCached(config: CloudflareBkndConfig, { env, ctx, ...args }: Context) {
|
||||||
const { kv } = config.bindings?.(env)!;
|
const { kv } = config.bindings?.(env)!;
|
||||||
if (!kv) throw new Error("kv namespace is not defined in cloudflare.bindings");
|
if (!kv) throw new Error("kv namespace is not defined in cloudflare.bindings");
|
||||||
const key = config.key ?? "app";
|
const key = config.key ?? "app";
|
||||||
@@ -37,7 +37,7 @@ export async function getCached(config: CloudflareBkndConfig, { env, ctx }: Cont
|
|||||||
},
|
},
|
||||||
adminOptions: { html: config.html }
|
adminOptions: { html: config.html }
|
||||||
},
|
},
|
||||||
env
|
{ env, ctx, ...args }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!cachedConfig) {
|
if (!cachedConfig) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DurableObject } from "cloudflare:workers";
|
import { DurableObject } from "cloudflare:workers";
|
||||||
import { createRuntimeApp } from "adapter";
|
import { createRuntimeApp, makeConfig } from "adapter";
|
||||||
import type { CloudflareBkndConfig, Context } from "adapter/cloudflare";
|
import type { CloudflareBkndConfig, Context } from "adapter/cloudflare";
|
||||||
import type { App, CreateAppConfig } from "bknd";
|
import type { App, CreateAppConfig } from "bknd";
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ export async function getDurable(config: CloudflareBkndConfig, ctx: Context) {
|
|||||||
const id = dobj.idFromName(key);
|
const id = dobj.idFromName(key);
|
||||||
const stub = dobj.get(id) as unknown as DurableBkndApp;
|
const stub = dobj.get(id) as unknown as DurableBkndApp;
|
||||||
|
|
||||||
const create_config = typeof config.app === "function" ? config.app(ctx.env) : config.app;
|
const create_config = makeConfig(config, ctx);
|
||||||
|
|
||||||
const res = await stub.fire(ctx.request, {
|
const res = await stub.fire(ctx.request, {
|
||||||
config: create_config,
|
config: create_config,
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { createRuntimeApp } from "adapter";
|
|||||||
import type { App } from "bknd";
|
import type { App } from "bknd";
|
||||||
import type { CloudflareBkndConfig, Context } from "../index";
|
import type { CloudflareBkndConfig, Context } from "../index";
|
||||||
|
|
||||||
export async function makeApp(config: CloudflareBkndConfig, { env }: Context) {
|
export async function makeApp(config: CloudflareBkndConfig, ctx: Context) {
|
||||||
return await createRuntimeApp(
|
return await createRuntimeApp(
|
||||||
{
|
{
|
||||||
...config,
|
...config,
|
||||||
adminOptions: config.html ? { html: config.html } : undefined
|
adminOptions: config.html ? { html: config.html } : undefined
|
||||||
},
|
},
|
||||||
env
|
ctx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ import type { MiddlewareHandler } from "hono";
|
|||||||
import { StorageLocalAdapter } from "media/storage/adapters/StorageLocalAdapter";
|
import { StorageLocalAdapter } from "media/storage/adapters/StorageLocalAdapter";
|
||||||
import type { AdminControllerOptions } from "modules/server/AdminController";
|
import type { AdminControllerOptions } from "modules/server/AdminController";
|
||||||
|
|
||||||
export type BkndConfig<Env = any> = CreateAppConfig & {
|
export type BkndConfig<Args = any> = CreateAppConfig & {
|
||||||
app?: CreateAppConfig | ((env: Env) => CreateAppConfig);
|
app?: CreateAppConfig | ((args: Args) => CreateAppConfig);
|
||||||
onBuilt?: (app: App) => Promise<void>;
|
onBuilt?: (app: App) => Promise<void>;
|
||||||
beforeBuild?: (app: App) => Promise<void>;
|
beforeBuild?: (app: App) => Promise<void>;
|
||||||
buildConfig?: Parameters<App["build"]>[0];
|
buildConfig?: Parameters<App["build"]>[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FrameworkBkndConfig<Env = any> = BkndConfig<Env>;
|
export type FrameworkBkndConfig<Args = any> = BkndConfig<Args>;
|
||||||
|
|
||||||
export type RuntimeBkndConfig<Env = any> = BkndConfig<Env> & {
|
export type RuntimeBkndConfig<Args = any> = BkndConfig<Args> & {
|
||||||
distPath?: string;
|
distPath?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,14 +46,14 @@ export function registerLocalMediaAdapter() {
|
|||||||
registries.media.register("local", StorageLocalAdapter);
|
registries.media.register("local", StorageLocalAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeConfig<Env = any>(config: BkndConfig<Env>, env?: Env): CreateAppConfig {
|
export function makeConfig<Args = any>(config: BkndConfig<Args>, args?: Args): CreateAppConfig {
|
||||||
let additionalConfig: CreateAppConfig = {};
|
let additionalConfig: CreateAppConfig = {};
|
||||||
if ("app" in config && config.app) {
|
if ("app" in config && config.app) {
|
||||||
if (typeof config.app === "function") {
|
if (typeof config.app === "function") {
|
||||||
if (!env) {
|
if (!args) {
|
||||||
throw new Error("env is required when config.app is a function");
|
throw new Error("args is required when config.app is a function");
|
||||||
}
|
}
|
||||||
additionalConfig = config.app(env);
|
additionalConfig = config.app(args);
|
||||||
} else {
|
} else {
|
||||||
additionalConfig = config.app;
|
additionalConfig = config.app;
|
||||||
}
|
}
|
||||||
@@ -62,11 +62,11 @@ export function makeConfig<Env = any>(config: BkndConfig<Env>, env?: Env): Creat
|
|||||||
return { ...config, ...additionalConfig };
|
return { ...config, ...additionalConfig };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createFrameworkApp<Env = any>(
|
export async function createFrameworkApp<Args = any>(
|
||||||
config: FrameworkBkndConfig,
|
config: FrameworkBkndConfig,
|
||||||
env?: Env
|
args?: Args
|
||||||
): Promise<App> {
|
): Promise<App> {
|
||||||
const app = App.create(makeConfig(config, env));
|
const app = App.create(makeConfig(config, args));
|
||||||
|
|
||||||
if (config.onBuilt) {
|
if (config.onBuilt) {
|
||||||
app.emgr.onEvent(
|
app.emgr.onEvent(
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ export function createApi({ req }: GetServerSidePropsContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function withApi<T>(handler: (ctx: GetServerSidePropsContext & { api: Api }) => T) {
|
export function withApi<T>(handler: (ctx: GetServerSidePropsContext & { api: Api }) => T) {
|
||||||
return (ctx: GetServerSidePropsContext & { api: Api }) => {
|
return async (ctx: GetServerSidePropsContext & { api: Api }) => {
|
||||||
return handler({ ...ctx, api: createApi(ctx) });
|
const api = createApi(ctx);
|
||||||
|
await api.verifyAuth();
|
||||||
|
return handler({ ...ctx, api });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
import { useAuth } from "bknd/client";
|
||||||
import type { BkndAdminProps } from "bknd/ui";
|
import type { BkndAdminProps } from "bknd/ui";
|
||||||
import { Suspense, lazy, useEffect, useState } from "react";
|
import { Suspense, lazy, useEffect, useState } from "react";
|
||||||
|
|
||||||
export function adminPage(props?: BkndAdminProps) {
|
export function adminPage(props?: BkndAdminProps) {
|
||||||
const Admin = lazy(() => import("bknd/ui").then((mod) => ({ default: mod.Admin })));
|
const Admin = lazy(() => import("bknd/ui").then((mod) => ({ default: mod.Admin })));
|
||||||
return () => {
|
return () => {
|
||||||
|
const auth = useAuth();
|
||||||
const [loaded, setLoaded] = useState(false);
|
const [loaded, setLoaded] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window === "undefined") return;
|
if (typeof window === "undefined") return;
|
||||||
@@ -13,7 +15,7 @@ export function adminPage(props?: BkndAdminProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<Admin {...props} />
|
<Admin withProvider={{ user: auth.user }} {...props} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,37 @@
|
|||||||
import { type FrameworkBkndConfig, createFrameworkApp } from "adapter";
|
import { type FrameworkBkndConfig, createFrameworkApp } from "adapter";
|
||||||
import type { App } from "bknd";
|
import type { App } from "bknd";
|
||||||
|
import { Api } from "bknd/client";
|
||||||
|
|
||||||
export type RemixBkndConfig = FrameworkBkndConfig;
|
export type RemixBkndConfig<Args = RemixContext> = FrameworkBkndConfig<Args>;
|
||||||
|
|
||||||
|
type RemixContext = {
|
||||||
|
request: Request;
|
||||||
|
};
|
||||||
|
|
||||||
let app: App;
|
let app: App;
|
||||||
export function serve(config: RemixBkndConfig = {}) {
|
export function serve<Args extends RemixContext = RemixContext>(
|
||||||
return async (args: { request: Request }) => {
|
config: RemixBkndConfig<Args> = {}
|
||||||
|
) {
|
||||||
|
return async (args: Args) => {
|
||||||
if (!app) {
|
if (!app) {
|
||||||
app = await createFrameworkApp(config);
|
app = await createFrameworkApp(config, args);
|
||||||
}
|
}
|
||||||
return app.fetch(args.request);
|
return app.fetch(args.request);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function withApi<Args extends { request: Request; context: { api: Api } }, R>(
|
||||||
|
handler: (args: Args, api: Api) => Promise<R>
|
||||||
|
) {
|
||||||
|
return async (args: Args) => {
|
||||||
|
if (!args.context.api) {
|
||||||
|
args.context.api = new Api({
|
||||||
|
host: new URL(args.request.url).origin,
|
||||||
|
headers: args.request.headers
|
||||||
|
});
|
||||||
|
await args.context.api.verifyAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler(args, args.context.api);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ import "bknd/dist/styles.css";
|
|||||||
|
|
||||||
import { getApi } from "bknd/adapter/astro";
|
import { getApi } from "bknd/adapter/astro";
|
||||||
|
|
||||||
const api = getApi(Astro, { mode: "dynamic" });
|
const api = await getApi(Astro, { mode: "dynamic" });
|
||||||
const user = api.getUser();
|
const user = api.getUser();
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
@@ -94,7 +94,7 @@ Here is an example of using the API in static context:
|
|||||||
```jsx
|
```jsx
|
||||||
---
|
---
|
||||||
import { getApi } from "bknd/adapter/astro";
|
import { getApi } from "bknd/adapter/astro";
|
||||||
const api = getApi(Astro);
|
const api = await getApi(Astro);
|
||||||
const { data } = await api.data.readMany("todos");
|
const { data } = await api.data.readMany("todos");
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ On SSR pages, you can also access the authenticated user:
|
|||||||
```jsx
|
```jsx
|
||||||
---
|
---
|
||||||
import { getApi } from "bknd/adapter/astro";
|
import { getApi } from "bknd/adapter/astro";
|
||||||
const api = getApi(Astro, { mode: "dynamic" });
|
const api = await getApi(Astro, { mode: "dynamic" });
|
||||||
const user = api.getUser();
|
const user = api.getUser();
|
||||||
const { data } = await api.data.readMany("todos");
|
const { data } = await api.data.readMany("todos");
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ and then install bknd as a dependency:
|
|||||||
If you don't choose anything specific, the following code will use the `warm` mode. See the
|
If you don't choose anything specific, the following code will use the `warm` mode. See the
|
||||||
chapter [Using a different mode](#using-a-different-mode) for available modes.
|
chapter [Using a different mode](#using-a-different-mode) for available modes.
|
||||||
|
|
||||||
``` ts
|
```ts
|
||||||
import { serve } from "bknd/adapter/cloudflare";
|
import { serve } from "bknd/adapter/cloudflare";
|
||||||
|
|
||||||
export default serve({
|
export default serve<Env>({
|
||||||
app: (env: Env) => ({
|
app: ({ env }) => ({
|
||||||
connection: {
|
connection: {
|
||||||
type: "libsql",
|
type: "libsql",
|
||||||
config: {
|
config: {
|
||||||
@@ -50,12 +50,12 @@ bucket = "node_modules/bknd/dist/static"
|
|||||||
```
|
```
|
||||||
|
|
||||||
And then modify the worker entry as follows:
|
And then modify the worker entry as follows:
|
||||||
``` ts {2, 14, 15}
|
```ts {2, 14, 15}
|
||||||
import { serve } from "bknd/adapter/cloudflare";
|
import { serve } from "bknd/adapter/cloudflare";
|
||||||
import manifest from "__STATIC_CONTENT_MANIFEST";
|
import manifest from "__STATIC_CONTENT_MANIFEST";
|
||||||
|
|
||||||
export default serve({
|
export default serve<Env>({
|
||||||
app: (env: Env) => ({
|
app: ({ env }) => ({
|
||||||
connection: {
|
connection: {
|
||||||
type: "libsql",
|
type: "libsql",
|
||||||
config: {
|
config: {
|
||||||
@@ -75,8 +75,8 @@ You can also add custom routes by defining them after the app has been built, li
|
|||||||
import { serve } from "bknd/adapter/cloudflare";
|
import { serve } from "bknd/adapter/cloudflare";
|
||||||
import manifest from "__STATIC_CONTENT_MANIFEST";
|
import manifest from "__STATIC_CONTENT_MANIFEST";
|
||||||
|
|
||||||
export default serve({
|
export default serve<Env>({
|
||||||
app: (env: Env) => ({
|
app: ({ env }) => ({
|
||||||
connection: {
|
connection: {
|
||||||
type: "libsql",
|
type: "libsql",
|
||||||
config: {
|
config: {
|
||||||
@@ -111,7 +111,7 @@ mode`, like so:
|
|||||||
import { serve } from "bknd/adapter/cloudflare";
|
import { serve } from "bknd/adapter/cloudflare";
|
||||||
|
|
||||||
export default serve({
|
export default serve({
|
||||||
/* ... */,
|
// ...
|
||||||
mode: "fresh" // mode: "fresh" | "warm" | "cache" | "durable"
|
mode: "fresh" // mode: "fresh" | "warm" | "cache" | "durable"
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -119,13 +119,14 @@ export default serve({
|
|||||||
### Mode: `cache`
|
### Mode: `cache`
|
||||||
For the cache mode to work, you also need to specify the KV to be used. For this, use the
|
For the cache mode to work, you also need to specify the KV to be used. For this, use the
|
||||||
`bindings` property:
|
`bindings` property:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { serve } from "bknd/adapter/cloudflare";
|
import { serve } from "bknd/adapter/cloudflare";
|
||||||
|
|
||||||
export default serve({
|
export default serve<Env>({
|
||||||
/* ... */,
|
// ...
|
||||||
mode: "cache",
|
mode: "cache",
|
||||||
bindings: (env: Env) => ({ kv: env.KV })
|
bindings: ({ env }) => ({ kv: env.KV })
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -136,10 +137,10 @@ environment, and additionally export the `DurableBkndApp` class:
|
|||||||
import { serve, DurableBkndApp } from "bknd/adapter/cloudflare";
|
import { serve, DurableBkndApp } from "bknd/adapter/cloudflare";
|
||||||
|
|
||||||
export { DurableBkndApp };
|
export { DurableBkndApp };
|
||||||
export default serve({
|
export default serve<Env>({
|
||||||
/* ... */,
|
// ...
|
||||||
mode: "durable",
|
mode: "durable",
|
||||||
bindings: (env: Env) => ({ dobj: env.DOBJ }),
|
bindings: ({ env }) => ({ dobj: env.DOBJ }),
|
||||||
keepAliveSeconds: 60 // optional
|
keepAliveSeconds: 60 // optional
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -164,9 +165,9 @@ import type { App } from "bknd";
|
|||||||
import { serve, DurableBkndApp } from "bknd/adapter/cloudflare";
|
import { serve, DurableBkndApp } from "bknd/adapter/cloudflare";
|
||||||
|
|
||||||
export default serve({
|
export default serve({
|
||||||
/* ... */,
|
// ...
|
||||||
mode: "durable",
|
mode: "durable",
|
||||||
bindings: (env: Env) => ({ dobj: env.DOBJ }),
|
bindings: ({ env }) => ({ dobj: env.DOBJ }),
|
||||||
keepAliveSeconds: 60 // optional
|
keepAliveSeconds: 60 // optional
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Install bknd as a dependency:
|
|||||||
|
|
||||||
## Serve the API
|
## Serve the API
|
||||||
Create a new api splat route file at `app/routes/api.$.ts`:
|
Create a new api splat route file at `app/routes/api.$.ts`:
|
||||||
``` tsx
|
```ts
|
||||||
// app/routes/api.$.ts
|
// app/routes/api.$.ts
|
||||||
import { serve } from "bknd/adapter/remix";
|
import { serve } from "bknd/adapter/remix";
|
||||||
|
|
||||||
@@ -32,6 +32,9 @@ Now make sure that you wrap your root layout with the `ClientProvider` so that a
|
|||||||
share the same context:
|
share the same context:
|
||||||
```tsx
|
```tsx
|
||||||
// app/root.tsx
|
// app/root.tsx
|
||||||
|
import { withApi } from "bknd/adapter/remix"
|
||||||
|
import { type Api, ClientProvider } from "bknd/client";
|
||||||
|
|
||||||
export function Layout(props) {
|
export function Layout(props) {
|
||||||
// nothing to change here, just for orientation
|
// nothing to change here, just for orientation
|
||||||
return (
|
return (
|
||||||
@@ -48,21 +51,12 @@ declare module "@remix-run/server-runtime" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// export a loader that initiates the API
|
// export a loader that initiates the API
|
||||||
// and pass it through the context
|
// and passes it down to args.context.api
|
||||||
export const loader = async (args: LoaderFunctionArgs) => {
|
export const loader = withApi(async (args: LoaderFunctionArgs, api: Api) => {
|
||||||
const api = new Api({
|
return {
|
||||||
host: new URL(args.request.url).origin,
|
user: api.getUser()
|
||||||
headers: args.request.headers
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// get the user from the API
|
|
||||||
const user = api.getUser();
|
|
||||||
|
|
||||||
// add api to the context
|
|
||||||
args.context.api = api;
|
|
||||||
|
|
||||||
return { user };
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const { user } = useLoaderData<typeof loader>();
|
const { user } = useLoaderData<typeof loader>();
|
||||||
@@ -93,15 +87,9 @@ Since the API has already been constructed in the root layout, you can now use i
|
|||||||
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||||
import { useLoaderData } from "@remix-run/react";
|
import { useLoaderData } from "@remix-run/react";
|
||||||
|
|
||||||
export const loader = async (args: LoaderFunctionArgs) => {
|
export const loader = async ({ context: { api } }: LoaderFunctionArgs) => {
|
||||||
const { api } = args.context;
|
|
||||||
|
|
||||||
// get the authenticated user
|
|
||||||
const user = api.getAuthState().user;
|
|
||||||
|
|
||||||
// get the data from the API
|
|
||||||
const { data } = await api.data.readMany("todos");
|
const { data } = await api.data.readMany("todos");
|
||||||
return { data, user };
|
return { data, user: api.getUser() };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import "bknd/dist/styles.css";
|
|||||||
|
|
||||||
import { getApi } from "bknd/adapter/astro";
|
import { getApi } from "bknd/adapter/astro";
|
||||||
|
|
||||||
const api = getApi(Astro, { mode: "dynamic" });
|
const api = await getApi(Astro, { mode: "dynamic" });
|
||||||
await api.verifyAuth();
|
|
||||||
const user = api.getUser();
|
const user = api.getUser();
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { APIContext } from "astro";
|
||||||
import { App } from "bknd";
|
import { App } from "bknd";
|
||||||
import { serve } from "bknd/adapter/astro";
|
import { serve } from "bknd/adapter/astro";
|
||||||
import { registerLocalMediaAdapter } from "bknd/adapter/node";
|
import { registerLocalMediaAdapter } from "bknd/adapter/node";
|
||||||
@@ -23,7 +24,7 @@ declare module "bknd/core" {
|
|||||||
interface DB extends Database {}
|
interface DB extends Database {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ALL = serve({
|
export const ALL = serve<APIContext>({
|
||||||
// we can use any libsql config, and if omitted, uses in-memory
|
// we can use any libsql config, and if omitted, uses in-memory
|
||||||
connection: {
|
connection: {
|
||||||
type: "libsql",
|
type: "libsql",
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
import { getApi } from "bknd/adapter/astro";
|
import { getApi } from "bknd/adapter/astro";
|
||||||
import Card from "../components/Card.astro";
|
import Card from "../components/Card.astro";
|
||||||
import Layout from "../layouts/Layout.astro";
|
import Layout from "../layouts/Layout.astro";
|
||||||
const api = getApi(Astro);
|
|
||||||
|
const api = await getApi(Astro);
|
||||||
const { data } = await api.data.readMany("todos");
|
const { data } = await api.data.readMany("todos");
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
import { getApi } from "bknd/adapter/astro";
|
import { getApi } from "bknd/adapter/astro";
|
||||||
import Card from "../components/Card.astro";
|
import Card from "../components/Card.astro";
|
||||||
import Layout from "../layouts/Layout.astro";
|
import Layout from "../layouts/Layout.astro";
|
||||||
const api = getApi(Astro, { mode: "dynamic" });
|
const api = await getApi(Astro, { mode: "dynamic" });
|
||||||
await api.verifyAuth();
|
|
||||||
|
|
||||||
const { data } = await api.data.readMany("todos");
|
const { data } = await api.data.readMany("todos");
|
||||||
const user = api.getUser();
|
const user = api.getUser();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { serve } from "bknd/adapter/cloudflare";
|
|||||||
import manifest from "__STATIC_CONTENT_MANIFEST";
|
import manifest from "__STATIC_CONTENT_MANIFEST";
|
||||||
|
|
||||||
export default serve({
|
export default serve({
|
||||||
app: (env: Env) => ({
|
app: (args) => ({
|
||||||
connection: {
|
connection: {
|
||||||
type: "libsql",
|
type: "libsql",
|
||||||
config: {
|
config: {
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
import type { LoaderFunctionArgs } from "@remix-run/node";
|
import type { LoaderFunctionArgs } from "@remix-run/node";
|
||||||
import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from "@remix-run/react";
|
import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from "@remix-run/react";
|
||||||
import { Api, ClientProvider } from "bknd/client";
|
import { withApi } from "bknd/adapter/remix";
|
||||||
|
import { type Api, ClientProvider } from "bknd/client";
|
||||||
declare module "@remix-run/server-runtime" {
|
|
||||||
export interface AppLoadContext {
|
|
||||||
api: Api;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Layout({ children }: { children: React.ReactNode }) {
|
export function Layout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
@@ -26,20 +21,17 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loader = async (args: LoaderFunctionArgs) => {
|
declare module "@remix-run/server-runtime" {
|
||||||
const api = new Api({
|
export interface AppLoadContext {
|
||||||
host: new URL(args.request.url).origin,
|
api: Api;
|
||||||
headers: args.request.headers
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// add api to the context
|
export const loader = withApi(async (args: LoaderFunctionArgs, api: Api) => {
|
||||||
args.context.api = api;
|
|
||||||
|
|
||||||
await api.verifyAuth();
|
|
||||||
return {
|
return {
|
||||||
user: api.getAuthState()?.user
|
user: api.getUser()
|
||||||
};
|
};
|
||||||
};
|
});
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const data = useLoaderData<typeof loader>();
|
const data = useLoaderData<typeof loader>();
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import { type MetaFunction, useLoaderData } from "@remix-run/react";
|
import { type MetaFunction, useLoaderData } from "@remix-run/react";
|
||||||
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||||
|
import { useAuth } from "bknd/client";
|
||||||
|
|
||||||
export const meta: MetaFunction = () => {
|
export const meta: MetaFunction = () => {
|
||||||
return [{ title: "Remix & bknd" }, { name: "description", content: "Welcome to Remix & bknd!" }];
|
return [{ title: "Remix & bknd" }, { name: "description", content: "Welcome to Remix & bknd!" }];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loader = async (args: LoaderFunctionArgs) => {
|
export const loader = async ({ context: { api } }: LoaderFunctionArgs) => {
|
||||||
const api = args.context.api;
|
|
||||||
await api.verifyAuth();
|
|
||||||
const { data } = await api.data.readMany("todos");
|
const { data } = await api.data.readMany("todos");
|
||||||
return { data, user: api.getUser() };
|
return { data, user: api.getUser() };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
const { data, user } = useLoaderData<typeof loader>();
|
const { data, user } = useLoaderData<typeof loader>();
|
||||||
|
const auth = useAuth();
|
||||||
|
console.log("auth", auth);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user