From b0c5f6307aabc58d5b2db94fb630df5cff859514 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 25 Jan 2025 09:09:09 +0100 Subject: [PATCH] updated adapters to automatically verify auth --- app/src/adapter/astro/astro.adapter.ts | 6 ++-- app/src/adapter/nextjs/nextjs.adapter.ts | 6 ++-- app/src/adapter/remix/AdminPage.tsx | 4 ++- app/src/adapter/remix/remix.adapter.ts | 17 ++++++++++ docs/integration/astro.mdx | 6 ++-- docs/integration/remix.mdx | 34 ++++++------------- .../astro/src/pages/admin/[...admin].astro | 3 +- examples/astro/src/pages/index.astro | 3 +- examples/astro/src/pages/ssr.astro | 3 +- examples/remix/app/root.tsx | 28 ++++++--------- examples/remix/app/routes/_index.tsx | 7 ++-- 11 files changed, 60 insertions(+), 57 deletions(-) diff --git a/app/src/adapter/astro/astro.adapter.ts b/app/src/adapter/astro/astro.adapter.ts index 479b873..fccfd7d 100644 --- a/app/src/adapter/astro/astro.adapter.ts +++ b/app/src/adapter/astro/astro.adapter.ts @@ -13,11 +13,13 @@ export type Options = { host?: string; }; -export function getApi(Astro: TAstro, options: Options = { mode: "static" }) { - return new Api({ +export async function getApi(Astro: TAstro, options: Options = { mode: "static" }) { + const api = new Api({ host: new URL(Astro.request.url).origin, headers: options.mode === "dynamic" ? Astro.request.headers : undefined }); + await api.verifyAuth(); + return api; } let app: App; diff --git a/app/src/adapter/nextjs/nextjs.adapter.ts b/app/src/adapter/nextjs/nextjs.adapter.ts index 65d5ffa..7e30d26 100644 --- a/app/src/adapter/nextjs/nextjs.adapter.ts +++ b/app/src/adapter/nextjs/nextjs.adapter.ts @@ -29,8 +29,10 @@ export function createApi({ req }: GetServerSidePropsContext) { } export function withApi(handler: (ctx: GetServerSidePropsContext & { api: Api }) => T) { - return (ctx: GetServerSidePropsContext & { api: Api }) => { - return handler({ ...ctx, api: createApi(ctx) }); + return async (ctx: GetServerSidePropsContext & { api: Api }) => { + const api = createApi(ctx); + await api.verifyAuth(); + return handler({ ...ctx, api }); }; } diff --git a/app/src/adapter/remix/AdminPage.tsx b/app/src/adapter/remix/AdminPage.tsx index 9361cc2..5c9e90b 100644 --- a/app/src/adapter/remix/AdminPage.tsx +++ b/app/src/adapter/remix/AdminPage.tsx @@ -1,9 +1,11 @@ +import { useAuth } from "bknd/client"; import type { BkndAdminProps } from "bknd/ui"; import { Suspense, lazy, useEffect, useState } from "react"; export function adminPage(props?: BkndAdminProps) { const Admin = lazy(() => import("bknd/ui").then((mod) => ({ default: mod.Admin }))); return () => { + const auth = useAuth(); const [loaded, setLoaded] = useState(false); useEffect(() => { if (typeof window === "undefined") return; @@ -13,7 +15,7 @@ export function adminPage(props?: BkndAdminProps) { return ( - + ); }; diff --git a/app/src/adapter/remix/remix.adapter.ts b/app/src/adapter/remix/remix.adapter.ts index c3d0c78..ed2818b 100644 --- a/app/src/adapter/remix/remix.adapter.ts +++ b/app/src/adapter/remix/remix.adapter.ts @@ -1,5 +1,6 @@ import { type FrameworkBkndConfig, createFrameworkApp } from "adapter"; import type { App } from "bknd"; +import { Api } from "bknd/client"; export type RemixBkndConfig = FrameworkBkndConfig; @@ -12,3 +13,19 @@ export function serve(config: RemixBkndConfig = {}) { return app.fetch(args.request); }; } + +export function withApi( + handler: (args: Args, api: Api) => Promise +) { + 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); + }; +} diff --git a/docs/integration/astro.mdx b/docs/integration/astro.mdx index 63c2f31..057e0b1 100644 --- a/docs/integration/astro.mdx +++ b/docs/integration/astro.mdx @@ -65,7 +65,7 @@ import "bknd/dist/styles.css"; import { getApi } from "bknd/adapter/astro"; -const api = getApi(Astro, { mode: "dynamic" }); +const api = await getApi(Astro, { mode: "dynamic" }); const user = api.getUser(); export const prerender = false; @@ -94,7 +94,7 @@ Here is an example of using the API in static context: ```jsx --- import { getApi } from "bknd/adapter/astro"; -const api = getApi(Astro); +const api = await getApi(Astro); const { data } = await api.data.readMany("todos"); --- @@ -109,7 +109,7 @@ On SSR pages, you can also access the authenticated user: ```jsx --- import { getApi } from "bknd/adapter/astro"; -const api = getApi(Astro, { mode: "dynamic" }); +const api = await getApi(Astro, { mode: "dynamic" }); const user = api.getUser(); const { data } = await api.data.readMany("todos"); diff --git a/docs/integration/remix.mdx b/docs/integration/remix.mdx index 3aaadc6..d89c888 100644 --- a/docs/integration/remix.mdx +++ b/docs/integration/remix.mdx @@ -32,6 +32,9 @@ Now make sure that you wrap your root layout with the `ClientProvider` so that a share the same context: ```tsx // app/root.tsx +import { withApi } from "bknd/adapter/remix" +import { type Api, ClientProvider } from "bknd/client"; + export function Layout(props) { // nothing to change here, just for orientation return ( @@ -48,21 +51,12 @@ declare module "@remix-run/server-runtime" { } // export a loader that initiates the API -// and pass it through the context -export const loader = async (args: LoaderFunctionArgs) => { - const api = new Api({ - host: new URL(args.request.url).origin, - 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 }; -}; +// and passes it down to args.context.api +export const loader = withApi(async (args: LoaderFunctionArgs, api: Api) => { + return { + user: api.getUser() + }; +}); export default function App() { const { user } = useLoaderData(); @@ -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 { useLoaderData } from "@remix-run/react"; -export const loader = async (args: LoaderFunctionArgs) => { - const { api } = args.context; - - // get the authenticated user - const user = api.getAuthState().user; - - // get the data from the API +export const loader = async ({ context: { api } }: LoaderFunctionArgs) => { const { data } = await api.data.readMany("todos"); - return { data, user }; + return { data, user: api.getUser() }; }; export default function Index() { diff --git a/examples/astro/src/pages/admin/[...admin].astro b/examples/astro/src/pages/admin/[...admin].astro index 612b801..8d34e21 100644 --- a/examples/astro/src/pages/admin/[...admin].astro +++ b/examples/astro/src/pages/admin/[...admin].astro @@ -4,8 +4,7 @@ import "bknd/dist/styles.css"; import { getApi } from "bknd/adapter/astro"; -const api = getApi(Astro, { mode: "dynamic" }); -await api.verifyAuth(); +const api = await getApi(Astro, { mode: "dynamic" }); const user = api.getUser(); export const prerender = false; diff --git a/examples/astro/src/pages/index.astro b/examples/astro/src/pages/index.astro index 4c2f31b..854df7f 100644 --- a/examples/astro/src/pages/index.astro +++ b/examples/astro/src/pages/index.astro @@ -2,7 +2,8 @@ import { getApi } from "bknd/adapter/astro"; import Card from "../components/Card.astro"; import Layout from "../layouts/Layout.astro"; -const api = getApi(Astro); + +const api = await getApi(Astro); const { data } = await api.data.readMany("todos"); --- diff --git a/examples/astro/src/pages/ssr.astro b/examples/astro/src/pages/ssr.astro index 726076d..ca8f380 100644 --- a/examples/astro/src/pages/ssr.astro +++ b/examples/astro/src/pages/ssr.astro @@ -2,8 +2,7 @@ import { getApi } from "bknd/adapter/astro"; import Card from "../components/Card.astro"; import Layout from "../layouts/Layout.astro"; -const api = getApi(Astro, { mode: "dynamic" }); -await api.verifyAuth(); +const api = await getApi(Astro, { mode: "dynamic" }); const { data } = await api.data.readMany("todos"); const user = api.getUser(); diff --git a/examples/remix/app/root.tsx b/examples/remix/app/root.tsx index 8930b4f..004ec11 100644 --- a/examples/remix/app/root.tsx +++ b/examples/remix/app/root.tsx @@ -1,12 +1,7 @@ import type { LoaderFunctionArgs } from "@remix-run/node"; import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from "@remix-run/react"; -import { Api, ClientProvider } from "bknd/client"; - -declare module "@remix-run/server-runtime" { - export interface AppLoadContext { - api: Api; - } -} +import { withApi } from "bknd/adapter/remix"; +import { type Api, ClientProvider } from "bknd/client"; export function Layout({ children }: { children: React.ReactNode }) { return ( @@ -26,20 +21,17 @@ export function Layout({ children }: { children: React.ReactNode }) { ); } -export const loader = async (args: LoaderFunctionArgs) => { - const api = new Api({ - host: new URL(args.request.url).origin, - headers: args.request.headers - }); +declare module "@remix-run/server-runtime" { + export interface AppLoadContext { + api: Api; + } +} - // add api to the context - args.context.api = api; - - await api.verifyAuth(); +export const loader = withApi(async (args: LoaderFunctionArgs, api: Api) => { return { - user: api.getAuthState()?.user + user: api.getUser() }; -}; +}); export default function App() { const data = useLoaderData(); diff --git a/examples/remix/app/routes/_index.tsx b/examples/remix/app/routes/_index.tsx index eef795d..1cbbe19 100644 --- a/examples/remix/app/routes/_index.tsx +++ b/examples/remix/app/routes/_index.tsx @@ -1,19 +1,20 @@ import { type MetaFunction, useLoaderData } from "@remix-run/react"; import type { LoaderFunctionArgs } from "@remix-run/server-runtime"; +import { useAuth } from "bknd/client"; export const meta: MetaFunction = () => { return [{ title: "Remix & bknd" }, { name: "description", content: "Welcome to Remix & bknd!" }]; }; -export const loader = async (args: LoaderFunctionArgs) => { - const api = args.context.api; - await api.verifyAuth(); +export const loader = async ({ context: { api } }: LoaderFunctionArgs) => { const { data } = await api.data.readMany("todos"); return { data, user: api.getUser() }; }; export default function Index() { const { data, user } = useLoaderData(); + const auth = useAuth(); + console.log("auth", auth); return (