From c3ee31a5652de51294e3825d82cbc4b08ead3741 Mon Sep 17 00:00:00 2001 From: Shishant Biswas Date: Sat, 14 Mar 2026 15:59:40 +0530 Subject: [PATCH] init: solid start adapter --- app/build.ts | 5 + app/e2e/inc/adapters.ts | 3 + app/package.json | 9 +- app/src/adapter/solid-start/index.ts | 1 + .../solid-start/solid-start.adapter.spec.ts | 16 + .../solid-start/solid-start.adapter.ts | 22 ++ .../integration/(frameworks)/meta.json | 3 +- .../integration/(frameworks)/solid-start.mdx | 335 ++++++++++++++++++ .../integration/introduction.mdx | 6 + docs/content/docs/(documentation)/start.mdx | 6 + examples/solid/.gitignore | 31 ++ examples/solid/README.md | 32 ++ examples/solid/bknd.config.ts | 51 +++ examples/solid/package.json | 28 ++ examples/solid/public/bknd.ico | Bin 0 -> 15086 bytes examples/solid/public/bknd.svg | 14 + examples/solid/public/file.svg | 1 + examples/solid/public/globe.svg | 1 + examples/solid/public/solid.svg | 1 + examples/solid/public/window.svg | 1 + examples/solid/src/app.css | 33 ++ examples/solid/src/app.tsx | 20 ++ examples/solid/src/components/Footer.tsx | 53 +++ examples/solid/src/components/List.tsx | 11 + examples/solid/src/entry-client.tsx | 4 + examples/solid/src/entry-server.tsx | 21 ++ examples/solid/src/global.d.ts | 1 + examples/solid/src/lib/bknd.ts | 30 ++ examples/solid/src/middleware/index.ts | 20 ++ examples/solid/src/routes/[...404].tsx | 19 + examples/solid/src/routes/index.tsx | 150 ++++++++ examples/solid/src/routes/user.tsx | 139 ++++++++ examples/solid/tsconfig.json | 19 + examples/solid/vite.config.ts | 15 + 34 files changed, 1099 insertions(+), 2 deletions(-) create mode 100644 app/src/adapter/solid-start/index.ts create mode 100644 app/src/adapter/solid-start/solid-start.adapter.spec.ts create mode 100644 app/src/adapter/solid-start/solid-start.adapter.ts create mode 100644 docs/content/docs/(documentation)/integration/(frameworks)/solid-start.mdx create mode 100644 examples/solid/.gitignore create mode 100644 examples/solid/README.md create mode 100644 examples/solid/bknd.config.ts create mode 100644 examples/solid/package.json create mode 100644 examples/solid/public/bknd.ico create mode 100644 examples/solid/public/bknd.svg create mode 100644 examples/solid/public/file.svg create mode 100644 examples/solid/public/globe.svg create mode 100644 examples/solid/public/solid.svg create mode 100644 examples/solid/public/window.svg create mode 100644 examples/solid/src/app.css create mode 100644 examples/solid/src/app.tsx create mode 100644 examples/solid/src/components/Footer.tsx create mode 100644 examples/solid/src/components/List.tsx create mode 100644 examples/solid/src/entry-client.tsx create mode 100644 examples/solid/src/entry-server.tsx create mode 100644 examples/solid/src/global.d.ts create mode 100644 examples/solid/src/lib/bknd.ts create mode 100644 examples/solid/src/middleware/index.ts create mode 100644 examples/solid/src/routes/[...404].tsx create mode 100644 examples/solid/src/routes/index.tsx create mode 100644 examples/solid/src/routes/user.tsx create mode 100644 examples/solid/tsconfig.json create mode 100644 examples/solid/vite.config.ts diff --git a/app/build.ts b/app/build.ts index 1ab90ea..4794661 100644 --- a/app/build.ts +++ b/app/build.ts @@ -340,6 +340,11 @@ async function buildAdapters() { platform: "node", }), + tsup.build({ + ...baseConfig("solid-start"), + platform: "node", + }), + tsup.build({ ...baseConfig("sqlite/edge"), entry: ["src/adapter/sqlite/edge.ts"], diff --git a/app/e2e/inc/adapters.ts b/app/e2e/inc/adapters.ts index 30b647b..9858349 100644 --- a/app/e2e/inc/adapters.ts +++ b/app/e2e/inc/adapters.ts @@ -15,6 +15,9 @@ const configs = { nextjs: { base_path: "/admin", }, + "solid-start": { + base_path: "/admin", + }, astro: { base_path: "/admin", }, diff --git a/app/package.json b/app/package.json index ceb3771..9e0a8f2 100644 --- a/app/package.json +++ b/app/package.json @@ -258,6 +258,11 @@ "import": "./dist/adapter/sveltekit/index.js", "require": "./dist/adapter/sveltekit/index.js" }, + "./adapter/solid-start": { + "types": "./dist/types/adapter/solid-start/index.d.ts", + "import": "./dist/adapter/solid-start/index.js", + "require": "./dist/adapter/solid-start/index.js" + }, "./adapter/aws": { "types": "./dist/types/adapter/aws/index.d.ts", "import": "./dist/adapter/aws/index.js", @@ -292,6 +297,7 @@ "adapter/node": ["./dist/types/adapter/node/index.d.ts"], "adapter/sveltekit": ["./dist/types/adapter/sveltekit/index.d.ts"], "adapter/tanstack-start": ["./dist/types/adapter/tanstack-start/index.d.ts"], + "adapter/solid-start": ["./dist/types/adapter/solid-start/index.d.ts"], "adapter/sqlite": ["./dist/types/adapter/sqlite/edge.d.ts"] } }, @@ -324,6 +330,7 @@ "sveltekit", "svelte", "bun", - "node" + "node", + "solid-start" ] } diff --git a/app/src/adapter/solid-start/index.ts b/app/src/adapter/solid-start/index.ts new file mode 100644 index 0000000..62fe56f --- /dev/null +++ b/app/src/adapter/solid-start/index.ts @@ -0,0 +1 @@ +export * from "./solid-start.adapter"; diff --git a/app/src/adapter/solid-start/solid-start.adapter.spec.ts b/app/src/adapter/solid-start/solid-start.adapter.spec.ts new file mode 100644 index 0000000..0149a25 --- /dev/null +++ b/app/src/adapter/solid-start/solid-start.adapter.spec.ts @@ -0,0 +1,16 @@ +import { afterAll, beforeAll, describe } from "bun:test"; +import * as solidStart from "./solid-start.adapter"; +import { disableConsoleLog, enableConsoleLog } from "core/utils"; +import { adapterTestSuite } from "adapter/adapter-test-suite"; +import { bunTestRunner } from "adapter/bun/test"; +import type { SolidStartBkndConfig } from "./solid-start.adapter"; + +beforeAll(disableConsoleLog); +afterAll(enableConsoleLog); + +describe("solid-start adapter", () => { + adapterTestSuite(bunTestRunner, { + makeApp: solidStart.getApp, + makeHandler: solidStart.serve, + }); +}); diff --git a/app/src/adapter/solid-start/solid-start.adapter.ts b/app/src/adapter/solid-start/solid-start.adapter.ts new file mode 100644 index 0000000..37c6fe1 --- /dev/null +++ b/app/src/adapter/solid-start/solid-start.adapter.ts @@ -0,0 +1,22 @@ +import { createRuntimeApp, type RuntimeBkndConfig } from "bknd/adapter"; + +export type SolidStartEnv = NodeJS.ProcessEnv; +export type SolidStartBkndConfig = RuntimeBkndConfig; + +export async function getApp( + config: SolidStartBkndConfig, + args: Env = process.env as Env, +) { + return await createRuntimeApp(config, args); +} + + +export function serve( + config: SolidStartBkndConfig = {}, + args: Env = process.env as Env, +) { + return async (req: Request) => { + const app = await getApp(config, args); + return app.fetch(req); + }; +} diff --git a/docs/content/docs/(documentation)/integration/(frameworks)/meta.json b/docs/content/docs/(documentation)/integration/(frameworks)/meta.json index 5cc7900..6a6ade3 100644 --- a/docs/content/docs/(documentation)/integration/(frameworks)/meta.json +++ b/docs/content/docs/(documentation)/integration/(frameworks)/meta.json @@ -5,6 +5,7 @@ "astro", "sveltekit", "tanstack-start", - "vite" + "vite", + "solid-start" ] } diff --git a/docs/content/docs/(documentation)/integration/(frameworks)/solid-start.mdx b/docs/content/docs/(documentation)/integration/(frameworks)/solid-start.mdx new file mode 100644 index 0000000..ab88c01 --- /dev/null +++ b/docs/content/docs/(documentation)/integration/(frameworks)/solid-start.mdx @@ -0,0 +1,335 @@ +--- +title: "Solid Start" +description: "Run bknd inside Solid Start" +tags: ["documentation"] +--- + +## Installation + +To get started with Solid Start and bknd you can either install the package manually, and follow the descriptions below, or use the CLI starter. + +Create a new Solid Start project by following the [official guide](https://docs.solidjs.com/solid-start/getting-started), and then install bknd as a dependency: + + + +```bash tab="npm" +npm install bknd +``` + +```bash tab="pnpm" +pnpm install bknd +``` + +```bash tab="yarn" +yarn add bknd +``` + +```bash tab="bun" +bun add bknd +``` + + + +## Configuration + + + When run with Node.js, a version of 22 (LTS) or higher is required. Please + verify your version by running `node -v`, and + [upgrade](https://nodejs.org/en/download/) if necessary. + + +Now create a `bknd.config.ts` file in the root of your project. If you created the project using the CLI starter, this file is already created for you. + +```typescript title="bknd.config.ts" +import type { SolidStartBkndConfig } from "bknd/adapter/solid-start"; +import { em, entity, text, boolean } from "bknd"; +import { secureRandomString } from "bknd/utils"; + +const schema = em({ + todos: entity("todos", { + title: text(), + done: boolean(), + }), +}); + +// register your schema to get automatic type completion +type Database = (typeof schema)["DB"]; +declare module "bknd" { + interface DB extends Database {} +} + +export default { + connection: { + url: "file:data.db", + }, + options: { + // the seed option is only executed if the database was empty + seed: async (ctx) => { + // create some entries + await ctx.em.mutator("todos").insertMany([ + { title: "Learn bknd", done: true }, + { title: "Build something cool", done: false }, + ]); + + // and create a user + await ctx.app.module.auth.createUser({ + email: "test@bknd.io", + password: "12345678", + }); + }, + }, + config: { + data: schema.toJSON(), + auth: { + enabled: true, + jwt: { + secret: secureRandomString(32), + }, + }, + }, + // please run `bun bknd copy-assets --out public/admin` to copy the admin assets into the public directory + // alternatively add this as a postinstall script in your package.json like this: + // "postinstall": "bknd copy-assets --out public/admin" + adminOptions: { + adminBasepath: "/admin", + assetsPath: "/admin/", // trailing slash is important + }, +} satisfies SolidStartBkndConfig; +``` + +See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `SolidStartBkndConfig` type extends the `BkndConfig` type with the following additional properties: + +```typescript +export type SolidStartBkndConfig = RuntimeBkndConfig; +``` + +## Serve the API and Admin UI + +The Solid Start adapter uses middleware to handle API requests and serve the Admin UI. Create a `src/middleware/index.ts` file: + +```ts title="src/middleware/index.ts" +import { createMiddleware } from "@solidjs/start/middleware"; +import config from "../../bknd.config"; +import { serve } from "bknd/adapter/solid-start"; + +const handler = serve(config); + +export default createMiddleware({ + onRequest: async (event) => { + const url = new URL(event.request.url); + const pathname = url.pathname; + + if (pathname.startsWith("/api") || pathname !== "/") { + const res = await handler(event.request); + + if (res && res.status !== 404) { + return res; + } + } + }, +}); +``` + +### Update the app config (Solid Start v1 based on vinxi) + +Add the following to your `app.config.ts` file: + +```ts title="app.config.ts" +import { defineConfig } from "@solidjs/start/config"; + +const app = defineConfig({ + // ... your existing config + middleware: "src/middleware/index.ts", + // ... your existing config +}); + +export default app; +``` + +### Update the vite config (Solid Start v2) + +Add the following to your `vite.config.ts` file: + +```ts title="vite.config.ts" +import { defineConfig } from "vite"; +import solid from "vite-plugin-solid"; +import { solidStart } from "@solidjs/start/vite"; + +export default defineConfig({ + plugins: [ + solid(), + solidStart({ + middleware: "./src/middleware/index.ts", + }), + ], +}); +``` + + + + You can visit https://localhost:3000/admin to see the admin UI. Additionally you can create more todos as you explore the admin UI. + + +Create a helper file to instantiate the bknd instance and retrieve the API, importing the configuration from the `bknd.config.ts` file: + + +```ts title="src/lib/bknd.ts" +import { getApp as getBkndApp } from "bknd/adapter/solid-start"; +import bkndConfig from "../../bknd.config"; +import type { App } from "bknd"; + +export const getApp = async () => { + return await getBkndApp(bkndConfig); +}; + +export async function getApi({ + headers, + verify, +}: { + verify?: boolean; + headers?: Headers; +}) { + const app = await getApp(); + + if (verify) { + const api = app.getApi({ headers }); + await api.verifyAuth(); + return api; + } + + return app.getApi(); +}; +``` +## Example usage of the API + +You can use the `getApi` helper function we've already set up to fetch and mutate in static pages and server components: + +```tsx title="src/routes/index.tsx" +import { getApi } from "~/lib/bknd"; +import { query, createAsync } from "@solidjs/router"; + +export const getTodo = async () => { + "use server" + const api = await getApi({}); + const limit = 5; + const todos = await api.data.readMany("todos"); + const total = todos.body.meta.total as number; + return { total, todos: todos as unknown as Todo[], limit }; +}; + +const getTodosFromServer = query(async () => await getTodo(), "getTodosFromServer"); + +export default async function Home() { + const api = await getApi(); + const data = createAsync(() => getTodosFromServer()); + + return ( +
    + + {(todo) =>
  • {todo.title}
  • } +
    +
+ ); +} +``` + +## Using authentication + +When using authentication, you'll need to pass the headers to the `getApi` function to verify the user is authenticated. + +```tsx title="src/routes/auth.tsx" +import { createResource, Suspense } from "solid-js"; +import { A } from "@solidjs/router"; +import { getRequestEvent } from "solid-js/web"; +import { getApi } from "~/lib/bknd"; + +export const getUser = async () => { + "use server" + const request = getRequestEvent()?.request; + const api = await getApi({ verify: true, headers: request?.headers }); + return api.getUser(); +} + +export default function Home() { + const [data] = createResource(async () => { + const user = await getUser() + return { user }; + }, { + initialValue: { + user: null + } + }); + + return ( +
+
+
+ Solid logo +
&
+ bknd logo +
+ + Loading...

}> +
+ {data()?.user ? ( + <> + Logged in as {data()?.user?.email}. + + Logout + + + ) : ( +
+

+ Not logged in. + + Login + +

+

+ Sign in with: + + test@bknd.io + + / + + 12345678 + +

+
+ )} +
+
+
+
+ ); +} +``` + + +## Tips + +Use `target="_self"` on links, anytime your traversing to an external route (like `/api/auth/logout` which are handled by bknd's middleware) to prevent solid router from intercepting the link. +```jsx + + Logout + +``` \ No newline at end of file diff --git a/docs/content/docs/(documentation)/integration/introduction.mdx b/docs/content/docs/(documentation)/integration/introduction.mdx index 6b94374..7415205 100644 --- a/docs/content/docs/(documentation)/integration/introduction.mdx +++ b/docs/content/docs/(documentation)/integration/introduction.mdx @@ -39,6 +39,12 @@ bknd seamlessly integrates with popular frameworks, allowing you to use what you href="/integration/tanstack-start" /> +} + title="Solid Start" + href="/integration/solid-start" +/> + Create a new issue to request a guide for your framework. diff --git a/docs/content/docs/(documentation)/start.mdx b/docs/content/docs/(documentation)/start.mdx index 744588d..08c3784 100644 --- a/docs/content/docs/(documentation)/start.mdx +++ b/docs/content/docs/(documentation)/start.mdx @@ -156,6 +156,12 @@ Pick your framework or runtime to get started. href="/integration/tanstack-start" /> +} + title="Solid Start" + href="/integration/solid-start" +/> + } title="AWS Lambda" diff --git a/examples/solid/.gitignore b/examples/solid/.gitignore new file mode 100644 index 0000000..4dcc620 --- /dev/null +++ b/examples/solid/.gitignore @@ -0,0 +1,31 @@ +dist +.wrangler +.output +.vercel +.netlify +.vinxi +app.config.timestamp_*.js + +# Environment +.env +.env*.local + +# dependencies +/node_modules + +# IDEs and editors +/.idea +.project +.classpath +*.launch +.settings/ + +# Temp +gitignore + +# System Files +.DS_Store +Thumbs.db + +public/admin +data.db diff --git a/examples/solid/README.md b/examples/solid/README.md new file mode 100644 index 0000000..9337430 --- /dev/null +++ b/examples/solid/README.md @@ -0,0 +1,32 @@ +# SolidStart + +Everything you need to build a Solid project, powered by [`solid-start`](https://start.solidjs.com); + +## Creating a project + +```bash +# create a new project in the current directory +npm init solid@latest + +# create a new project in my-app +npm init solid@latest my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +Solid apps are built with _presets_, which optimise your project for deployment to different environments. + +By default, `npm run build` will generate a Node app that you can run with `npm start`. To use a different preset, add it to the `devDependencies` in `package.json` and specify in your `app.config.js`. + +## This project was created with the [Solid CLI](https://github.com/solidjs-community/solid-cli) diff --git a/examples/solid/bknd.config.ts b/examples/solid/bknd.config.ts new file mode 100644 index 0000000..d4c3fda --- /dev/null +++ b/examples/solid/bknd.config.ts @@ -0,0 +1,51 @@ +import { em, entity, text, boolean } from "bknd"; +import { secureRandomString } from "bknd/utils"; +import type { SolidStartBkndConfig } from "bknd/adapter/solid-start"; + +const schema = em({ + todos: entity("todos", { + title: text(), + done: boolean(), + }), +}); + +// register your schema to get automatic type completion +type Database = (typeof schema)["DB"]; +declare module "bknd" { + interface DB extends Database { } +} + +export default { + connection: { url: "file:data.db" }, + options: { + // the seed option is only executed if the database was empty + seed: async (ctx) => { + // create some entries + await ctx.em.mutator("todos").insertMany([ + { title: "Learn bknd", done: true }, + { title: "Build something cool", done: false }, + ]); + + // and create a user + await ctx.app.module.auth.createUser({ + email: "test@bknd.io", + password: "12345678", + }); + }, + }, + config: { + data: schema.toJSON(), + auth: { + enabled: true, + jwt: { + secret: secureRandomString(32), + }, + }, + }, + adminOptions: { + adminBasepath: "/admin", + assetsPath: "/admin/", + logoReturnPath: "../..", + }, +} satisfies SolidStartBkndConfig; + diff --git a/examples/solid/package.json b/examples/solid/package.json new file mode 100644 index 0000000..3e77765 --- /dev/null +++ b/examples/solid/package.json @@ -0,0 +1,28 @@ +{ + "name": "example-basic", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "start": "vite start", + "preview": "vite preview", + "typegen": "bunx tsx node_modules/.bin/bknd types --outfile bknd-types.d.ts", + "postinstall": "bun run bknd copy-assets --out public/admin" + }, + "dependencies": { + "@solidjs/meta": "^0.29.4", + "@solidjs/router": "^0.15.0", + "@solidjs/start": "2.0.0-alpha.2", + "bknd": "file:../../app", + "nitro": "3.0.1-alpha.2", + "solid-js": "^1.9.5", + "vite": "^7.0.0" + }, + "engines": { + "node": ">=22" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.1.18", + "tailwindcss": "^4.1.18" + } +} \ No newline at end of file diff --git a/examples/solid/public/bknd.ico b/examples/solid/public/bknd.ico new file mode 100644 index 0000000000000000000000000000000000000000..c1a946d533a71c15e51fc4cade87289ca467b57d GIT binary patch literal 15086 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 + + + \ No newline at end of file diff --git a/examples/solid/public/file.svg b/examples/solid/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/examples/solid/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/solid/public/globe.svg b/examples/solid/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/examples/solid/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/solid/public/solid.svg b/examples/solid/public/solid.svg new file mode 100644 index 0000000..25df77f --- /dev/null +++ b/examples/solid/public/solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/solid/public/window.svg b/examples/solid/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/examples/solid/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/solid/src/app.css b/examples/solid/src/app.css new file mode 100644 index 0000000..4a737b8 --- /dev/null +++ b/examples/solid/src/app.css @@ -0,0 +1,33 @@ +@import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&display=swap'); +@import "tailwindcss"; + +.geist-mono-100 { + font-optical-sizing: auto; + font-weight: 100; + font-style: normal; +} + +:root { + --background: #ffffff; + --foreground: #171717; + font-family: "Geist Mono", monospace; +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +@theme { + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + --color-background: var(--background); + --color-foreground: var(--foreground); +} + +body { + @apply bg-background text-foreground; + font-family: var(--font-geist-mono-100) +} \ No newline at end of file diff --git a/examples/solid/src/app.tsx b/examples/solid/src/app.tsx new file mode 100644 index 0000000..ce077ac --- /dev/null +++ b/examples/solid/src/app.tsx @@ -0,0 +1,20 @@ +import { MetaProvider, Title } from "@solidjs/meta"; +import { Router } from "@solidjs/router"; +import { FileRoutes } from "@solidjs/start/router"; +import { Suspense } from "solid-js"; +import "./app.css"; + +export default function App() { + return ( + ( + + Solid Start 🤝 Bknd.io + {props.children} + + )} + > + + + ); +} diff --git a/examples/solid/src/components/Footer.tsx b/examples/solid/src/components/Footer.tsx new file mode 100644 index 0000000..a6cdd70 --- /dev/null +++ b/examples/solid/src/components/Footer.tsx @@ -0,0 +1,53 @@ +import { A, useLocation } from "@solidjs/router"; + +export function Footer() { + const pathname = useLocation().pathname; + + return ( + + ); +} diff --git a/examples/solid/src/components/List.tsx b/examples/solid/src/components/List.tsx new file mode 100644 index 0000000..3ec1bb7 --- /dev/null +++ b/examples/solid/src/components/List.tsx @@ -0,0 +1,11 @@ +import { JSX } from "solid-js"; + +export const List = ({ items = [] }: { items: JSX.Element[] }) => ( +
    + {items.map((item, i) => ( +
  1. + {item} +
  2. + ))} +
+); diff --git a/examples/solid/src/entry-client.tsx b/examples/solid/src/entry-client.tsx new file mode 100644 index 0000000..0ca4e3c --- /dev/null +++ b/examples/solid/src/entry-client.tsx @@ -0,0 +1,4 @@ +// @refresh reload +import { mount, StartClient } from "@solidjs/start/client"; + +mount(() => , document.getElementById("app")!); diff --git a/examples/solid/src/entry-server.tsx b/examples/solid/src/entry-server.tsx new file mode 100644 index 0000000..38126d1 --- /dev/null +++ b/examples/solid/src/entry-server.tsx @@ -0,0 +1,21 @@ +// @refresh reload +import { createHandler, StartServer } from "@solidjs/start/server"; + +export default createHandler(() => ( + ( + + + + + + {assets} + + +
{children}
+ {scripts} + + + )} + /> +)); diff --git a/examples/solid/src/global.d.ts b/examples/solid/src/global.d.ts new file mode 100644 index 0000000..dc6f10c --- /dev/null +++ b/examples/solid/src/global.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/src/lib/bknd.ts b/examples/solid/src/lib/bknd.ts new file mode 100644 index 0000000..170eadb --- /dev/null +++ b/examples/solid/src/lib/bknd.ts @@ -0,0 +1,30 @@ +import { getApp as getBkndApp } from "bknd/adapter/solid-start"; +import bkndConfig from "../../bknd.config"; +import type { App } from "bknd"; + +let client: App | null = null; + +export const getApp = async () => { + if (!client) { + client = await getBkndApp(bkndConfig); + } + return client; +}; + +export async function getApi({ + headers, + verify, +}: { + verify?: boolean; + headers?: Headers; +}) { + const app = await getApp(); + + if (verify) { + const api = app.getApi({ headers }); + await api.verifyAuth(); + return api; + } + + return app.getApi(); +} diff --git a/examples/solid/src/middleware/index.ts b/examples/solid/src/middleware/index.ts new file mode 100644 index 0000000..ebebed8 --- /dev/null +++ b/examples/solid/src/middleware/index.ts @@ -0,0 +1,20 @@ +import { createMiddleware } from "@solidjs/start/middleware"; +import config from "../../bknd.config"; +import { serve } from "bknd/adapter/solid-start"; + +const handler = serve(config); + +export default createMiddleware({ + onRequest: async (event) => { + const url = new URL(event.request.url); + const pathname = url.pathname; + + if (pathname.startsWith("/api") || pathname !== "/") { + const res = await handler(event.request); + + if (res && res.status !== 404) { + return res; + } + } + }, +}); diff --git a/examples/solid/src/routes/[...404].tsx b/examples/solid/src/routes/[...404].tsx new file mode 100644 index 0000000..4ea71ec --- /dev/null +++ b/examples/solid/src/routes/[...404].tsx @@ -0,0 +1,19 @@ +import { Title } from "@solidjs/meta"; +import { HttpStatusCode } from "@solidjs/start"; + +export default function NotFound() { + return ( +
+ Not Found + +

Page Not Found

+

+ Visit{" "} + + start.solidjs.com + {" "} + to learn how to build SolidStart apps. +

+
+ ); +} diff --git a/examples/solid/src/routes/index.tsx b/examples/solid/src/routes/index.tsx new file mode 100644 index 0000000..3423bf0 --- /dev/null +++ b/examples/solid/src/routes/index.tsx @@ -0,0 +1,150 @@ +import type { DB } from "bknd"; +import { Suspense } from "solid-js"; +import { Footer } from "~/components/Footer"; +import { List } from "~/components/List"; +import { getApi } from "~/lib/bknd"; +import { action, redirect, useAction, useSubmission } from "@solidjs/router"; +import { query, createAsync } from "@solidjs/router"; + +type Todo = DB['todos']; + +export const getTodo = async () => { + "use server" + const api = await getApi({}); + const limit = 5; + const todos = await api.data.readMany("todos"); + const total = todos.body.meta.total as number; + return { total, todos: todos as unknown as Todo[], limit }; +}; + +const getTodosFromServer = query(async () => await getTodo(), "getTodosFromServer"); + + +const createTodo = action(async (formData: FormData) => { + "use server" + const title = formData.get("title") as string; + const api = await getApi({}); + await api.data.createOne("todos", { title }); + throw redirect("/", { revalidate: getTodosFromServer.keyFor() }); +}, "createTodo"); + + + +const completeTodo = action(async (todo: Todo) => { + "use server" + const api = await getApi({}); + await api.data.updateOne("todos", todo.id, { + done: !todo.done, + }); + throw redirect("/", { revalidate: getTodosFromServer.keyFor() }); +}, "completeTodo"); + + +const deleteTodo = action(async (todo: Todo) => { + "use server" + const api = await getApi({}); + await api.data.deleteOne("todos", todo.id); + throw redirect("/", { revalidate: getTodosFromServer.keyFor() }); +}, "deleteTodo"); + +export default function Home() { + const data = createAsync(() => getTodosFromServer()); + + const submission = useSubmission(createTodo); + + const updateTodo = useAction(completeTodo); + const removeTodo = useAction(deleteTodo); + + return ( +
+
+
+ Solid Start logo +
&
+ bknd logo +
+ + +
+

+ What's next? +

+
+ {(data()?.total ?? 0) > (data()?.limit ?? 0) && ( +
+ {(data()?.total ?? 0) - (data()?.limit ?? 0)} more todo(s) hidden +
+ )} +
+ {data()?.todos?. + splice(0, data()?.limit ?? 0) + .map((todo) => ( +
+
+ { + await updateTodo(todo); + }} + /> +
+ {todo.title} +
+
+ +
+ ))} +
+
+ + +
+
+
+
+
+
+
); +} + + + +const Description = () => ( + +); \ No newline at end of file diff --git a/examples/solid/src/routes/user.tsx b/examples/solid/src/routes/user.tsx new file mode 100644 index 0000000..b5be690 --- /dev/null +++ b/examples/solid/src/routes/user.tsx @@ -0,0 +1,139 @@ +import { A } from "@solidjs/router"; +import type { DB } from "bknd"; +import { createResource, Suspense } from "solid-js"; +import { getRequestEvent } from "solid-js/web"; +import { Footer } from "~/components/Footer"; +import { List } from "~/components/List"; +import { getApi } from "~/lib/bknd"; + + +const getUser = async () => { + "use server" + const request = getRequestEvent()?.request; + const api = await getApi({ verify: true, headers: request?.headers }); + return api.getUser(); +} + +type Todo = DB['todos']; +export const getTodo = async () => { + "use server" + const api = await getApi({}); + const limit = 5; + const todos = await api.data.readMany("todos"); + const total = todos.body.meta.total as number; + return { total, todos: todos as unknown as Todo[], limit }; +}; + +export default function Home() { + const [data] = createResource(async () => { + const todo = await getTodo(); + const user = await getUser() + return { todo, user }; + }, { + initialValue: { + todo: { + todos: [], + limit: 0, + total: 0 + }, + user: null + } + }); + + return ( +
+
+
+ Solid logo +
&
+ bknd logo +
+ todo.title)} /> + + + Loading...

}> +
+ {data()?.user ? ( + <> + Logged in as {data()?.user?.email}. + + Logout + + + ) : ( +
+

+ Not logged in. + + Login + +

+

+ Sign in with: + + test@bknd.io + + / + + 12345678 + +

+
+ )} +
+
+
+
+
+ ); +} + +function Buttons() { + return ( + + ); +} diff --git a/examples/solid/tsconfig.json b/examples/solid/tsconfig.json new file mode 100644 index 0000000..7692957 --- /dev/null +++ b/examples/solid/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "allowJs": true, + "strict": true, + "noEmit": true, + "types": ["vite/client"], + "isolatedModules": true, + "paths": { + "~/*": ["./src/*"] + } + } +} diff --git a/examples/solid/vite.config.ts b/examples/solid/vite.config.ts new file mode 100644 index 0000000..ff31018 --- /dev/null +++ b/examples/solid/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from "vite"; +import { nitro } from "nitro/vite"; + +import { solidStart } from "@solidjs/start/config"; +import tailwindcss from "@tailwindcss/vite"; + +export default defineConfig({ + plugins: [ + tailwindcss(), + solidStart({ + middleware: "./src/middleware/index.ts", + }), + nitro(), + ], +});