mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-15 20:17:22 +00:00
init: solid start adapter
This commit is contained in:
@@ -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"],
|
||||
|
||||
@@ -15,6 +15,9 @@ const configs = {
|
||||
nextjs: {
|
||||
base_path: "/admin",
|
||||
},
|
||||
"solid-start": {
|
||||
base_path: "/admin",
|
||||
},
|
||||
astro: {
|
||||
base_path: "/admin",
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
1
app/src/adapter/solid-start/index.ts
Normal file
1
app/src/adapter/solid-start/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./solid-start.adapter";
|
||||
16
app/src/adapter/solid-start/solid-start.adapter.spec.ts
Normal file
16
app/src/adapter/solid-start/solid-start.adapter.spec.ts
Normal file
@@ -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<SolidStartBkndConfig>(bunTestRunner, {
|
||||
makeApp: solidStart.getApp,
|
||||
makeHandler: solidStart.serve,
|
||||
});
|
||||
});
|
||||
22
app/src/adapter/solid-start/solid-start.adapter.ts
Normal file
22
app/src/adapter/solid-start/solid-start.adapter.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { createRuntimeApp, type RuntimeBkndConfig } from "bknd/adapter";
|
||||
|
||||
export type SolidStartEnv = NodeJS.ProcessEnv;
|
||||
export type SolidStartBkndConfig<Env = SolidStartEnv> = RuntimeBkndConfig<Env>;
|
||||
|
||||
export async function getApp<Env = SolidStartEnv>(
|
||||
config: SolidStartBkndConfig<Env>,
|
||||
args: Env = process.env as Env,
|
||||
) {
|
||||
return await createRuntimeApp(config, args);
|
||||
}
|
||||
|
||||
|
||||
export function serve<Env = SolidStartEnv>(
|
||||
config: SolidStartBkndConfig<Env> = {},
|
||||
args: Env = process.env as Env,
|
||||
) {
|
||||
return async (req: Request) => {
|
||||
const app = await getApp(config, args);
|
||||
return app.fetch(req);
|
||||
};
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
"astro",
|
||||
"sveltekit",
|
||||
"tanstack-start",
|
||||
"vite"
|
||||
"vite",
|
||||
"solid-start"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
<Tabs groupId='package-manager' persist items={[ 'npm', 'pnpm', 'yarn', 'bun']}>
|
||||
|
||||
```bash tab="npm"
|
||||
npm install bknd
|
||||
```
|
||||
|
||||
```bash tab="pnpm"
|
||||
pnpm install bknd
|
||||
```
|
||||
|
||||
```bash tab="yarn"
|
||||
yarn add bknd
|
||||
```
|
||||
|
||||
```bash tab="bun"
|
||||
bun add bknd
|
||||
```
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Configuration
|
||||
|
||||
<Callout type="warning">
|
||||
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.
|
||||
</Callout>
|
||||
|
||||
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<Env = SolidStartEnv> = RuntimeBkndConfig<Env>;
|
||||
```
|
||||
|
||||
## 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",
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
<Callout type="success">
|
||||
You can visit https://localhost:3000/admin to see the admin UI. Additionally you can create more todos as you explore the admin UI.
|
||||
</Callout>
|
||||
|
||||
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 (
|
||||
<ul>
|
||||
<For each={data()}>
|
||||
{(todo) => <li>{todo.title}</li>}
|
||||
</For>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 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 (
|
||||
<div class="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
|
||||
<main class="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
<div class="flex flex-row items-center ">
|
||||
<img
|
||||
class="dark:invert size-18"
|
||||
src="/solid.svg"
|
||||
alt="Solid logo"
|
||||
/>
|
||||
<div class="ml-3.5 mr-2 opacity-70">&</div>
|
||||
<img
|
||||
class="dark:invert"
|
||||
src="/bknd.svg"
|
||||
alt="bknd logo"
|
||||
width={183}
|
||||
height={59}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<div>
|
||||
{data()?.user ? (
|
||||
<>
|
||||
Logged in as {data()?.user?.email}.
|
||||
<A
|
||||
class="underline"
|
||||
target="_self"
|
||||
href={"/api/auth/logout"}
|
||||
>
|
||||
Logout
|
||||
</A>
|
||||
</>
|
||||
) : (
|
||||
<div class="flex flex-col gap-1">
|
||||
<p>
|
||||
Not logged in.
|
||||
<A
|
||||
class="underline"
|
||||
target="_self"
|
||||
href={"/admin/auth/login"}
|
||||
>
|
||||
Login
|
||||
</A>
|
||||
</p>
|
||||
<p class="text-xs opacity-50">
|
||||
Sign in with:
|
||||
<b>
|
||||
<code>test@bknd.io</code>
|
||||
</b>
|
||||
/
|
||||
<b>
|
||||
<code>12345678</code>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Suspense>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 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
|
||||
<A href="/api/auth/logout" target="_self">
|
||||
Logout
|
||||
</A>
|
||||
```
|
||||
@@ -39,6 +39,12 @@ bknd seamlessly integrates with popular frameworks, allowing you to use what you
|
||||
href="/integration/tanstack-start"
|
||||
/>
|
||||
|
||||
<Card
|
||||
icon={<Icon icon="simple-icons:solid" className="text-fd-primary !size-6" />}
|
||||
title="Solid Start"
|
||||
href="/integration/solid-start"
|
||||
/>
|
||||
|
||||
<Card title="Yours missing?" href="https://github.com/bknd-io/bknd/issues/new">
|
||||
Create a new issue to request a guide for your framework.
|
||||
</Card>
|
||||
|
||||
@@ -156,6 +156,12 @@ Pick your framework or runtime to get started.
|
||||
href="/integration/tanstack-start"
|
||||
/>
|
||||
|
||||
<Card
|
||||
icon={<Icon icon="simple-icons:solid" className="text-fd-primary !size-6" />}
|
||||
title="Solid Start"
|
||||
href="/integration/solid-start"
|
||||
/>
|
||||
|
||||
<Card
|
||||
icon={<Icon icon="tabler:lambda" className="text-fd-primary !size-6" />}
|
||||
title="AWS Lambda"
|
||||
|
||||
31
examples/solid/.gitignore
vendored
Normal file
31
examples/solid/.gitignore
vendored
Normal file
@@ -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
|
||||
32
examples/solid/README.md
Normal file
32
examples/solid/README.md
Normal file
@@ -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)
|
||||
51
examples/solid/bknd.config.ts
Normal file
51
examples/solid/bknd.config.ts
Normal file
@@ -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;
|
||||
|
||||
28
examples/solid/package.json
Normal file
28
examples/solid/package.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
BIN
examples/solid/public/bknd.ico
Normal file
BIN
examples/solid/public/bknd.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
14
examples/solid/public/bknd.svg
Normal file
14
examples/solid/public/bknd.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<svg
|
||||
width="578"
|
||||
height="188"
|
||||
viewBox="0 0 578 188"
|
||||
fill="black"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M41.5 34C37.0817 34 33.5 37.5817 33.5 42V146C33.5 150.418 37.0817 154 41.5 154H158.5C162.918 154 166.5 150.418 166.5 146V42C166.5 37.5817 162.918 34 158.5 34H41.5ZM123.434 113.942C124.126 111.752 124.5 109.42 124.5 107C124.5 94.2975 114.203 84 101.5 84C99.1907 84 96.9608 84.3403 94.8579 84.9736L87.2208 65.1172C90.9181 63.4922 93.5 59.7976 93.5 55.5C93.5 49.701 88.799 45 83 45C77.201 45 72.5 49.701 72.5 55.5C72.5 61.299 77.201 66 83 66C83.4453 66 83.8841 65.9723 84.3148 65.9185L92.0483 86.0256C87.1368 88.2423 83.1434 92.1335 80.7957 96.9714L65.4253 91.1648C65.4746 90.7835 65.5 90.3947 65.5 90C65.5 85.0294 61.4706 81 56.5 81C51.5294 81 47.5 85.0294 47.5 90C47.5 94.9706 51.5294 99 56.5 99C60.0181 99 63.0648 96.9814 64.5449 94.0392L79.6655 99.7514C78.9094 102.03 78.5 104.467 78.5 107C78.5 110.387 79.2321 113.603 80.5466 116.498L69.0273 123.731C67.1012 121.449 64.2199 120 61 120C55.201 120 50.5 124.701 50.5 130.5C50.5 136.299 55.201 141 61 141C66.799 141 71.5 136.299 71.5 130.5C71.5 128.997 71.1844 127.569 70.6158 126.276L81.9667 119.149C86.0275 125.664 93.2574 130 101.5 130C110.722 130 118.677 124.572 122.343 116.737L132.747 120.899C132.585 121.573 132.5 122.276 132.5 123C132.5 127.971 136.529 132 141.5 132C146.471 132 150.5 127.971 150.5 123C150.5 118.029 146.471 114 141.5 114C138.32 114 135.525 115.649 133.925 118.139L123.434 113.942Z"
|
||||
/>
|
||||
<path d="M243.9 151.5C240.4 151.5 237 151 233.7 150C230.4 149 227.4 147.65 224.7 145.95C222 144.15 219.75 142.15 217.95 139.95C216.15 137.65 215 135.3 214.5 132.9L219.3 131.1L218.25 149.7H198.15V39H219.45V89.25L215.4 87.6C216 85.2 217.15 82.9 218.85 80.7C220.55 78.4 222.7 76.4 225.3 74.7C227.9 72.9 230.75 71.5 233.85 70.5C236.95 69.5 240.15 69 243.45 69C250.35 69 256.5 70.8 261.9 74.4C267.3 77.9 271.55 82.75 274.65 88.95C277.85 95.15 279.45 102.25 279.45 110.25C279.45 118.25 277.9 125.35 274.8 131.55C271.7 137.75 267.45 142.65 262.05 146.25C256.75 149.75 250.7 151.5 243.9 151.5ZM238.8 133.35C242.8 133.35 246.25 132.4 249.15 130.5C252.15 128.5 254.5 125.8 256.2 122.4C257.9 118.9 258.75 114.85 258.75 110.25C258.75 105.75 257.9 101.75 256.2 98.25C254.6 94.75 252.3 92.05 249.3 90.15C246.3 88.25 242.8 87.3 238.8 87.3C234.8 87.3 231.3 88.25 228.3 90.15C225.3 92.05 222.95 94.75 221.25 98.25C219.55 101.75 218.7 105.75 218.7 110.25C218.7 114.85 219.55 118.9 221.25 122.4C222.95 125.8 225.3 128.5 228.3 130.5C231.3 132.4 234.8 133.35 238.8 133.35ZM308.312 126.15L302.012 108.6L339.512 70.65H367.562L308.312 126.15ZM288.062 150V39H309.362V150H288.062ZM341.762 150L313.262 114.15L328.262 102.15L367.412 150H341.762ZM371.675 150V70.65H392.075L392.675 86.85L388.475 88.65C389.575 85.05 391.525 81.8 394.325 78.9C397.225 75.9 400.675 73.5 404.675 71.7C408.675 69.9 412.875 69 417.275 69C423.275 69 428.275 70.2 432.275 72.6C436.375 75 439.425 78.65 441.425 83.55C443.525 88.35 444.575 94.3 444.575 101.4V150H423.275V103.05C423.275 99.45 422.775 96.45 421.775 94.05C420.775 91.65 419.225 89.9 417.125 88.8C415.125 87.6 412.625 87.1 409.625 87.3C407.225 87.3 404.975 87.7 402.875 88.5C400.875 89.2 399.125 90.25 397.625 91.65C396.225 93.05 395.075 94.65 394.175 96.45C393.375 98.25 392.975 100.2 392.975 102.3V150H382.475C380.175 150 378.125 150 376.325 150C374.525 150 372.975 150 371.675 150ZM488.536 151.5C481.636 151.5 475.436 149.75 469.936 146.25C464.436 142.65 460.086 137.8 456.886 131.7C453.786 125.5 452.236 118.35 452.236 110.25C452.236 102.35 453.786 95.3 456.886 89.1C460.086 82.9 464.386 78 469.786 74.4C475.286 70.8 481.536 69 488.536 69C492.236 69 495.786 69.6 499.186 70.8C502.686 71.9 505.786 73.45 508.486 75.45C511.286 77.45 513.536 79.7 515.236 82.2C516.936 84.6 517.886 87.15 518.086 89.85L512.686 90.75V39H533.986V150H513.886L512.986 131.7L517.186 132.15C516.986 134.65 516.086 137.05 514.486 139.35C512.886 141.65 510.736 143.75 508.036 145.65C505.436 147.45 502.436 148.9 499.036 150C495.736 151 492.236 151.5 488.536 151.5ZM493.336 133.8C497.336 133.8 500.836 132.8 503.836 130.8C506.836 128.8 509.186 126.05 510.886 122.55C512.586 119.05 513.436 114.95 513.436 110.25C513.436 105.65 512.586 101.6 510.886 98.1C509.186 94.5 506.836 91.75 503.836 89.85C500.836 87.85 497.336 86.85 493.336 86.85C489.336 86.85 485.836 87.85 482.836 89.85C479.936 91.75 477.636 94.5 475.936 98.1C474.336 101.6 473.536 105.65 473.536 110.25C473.536 114.95 474.336 119.05 475.936 122.55C477.636 126.05 479.936 128.8 482.836 130.8C485.836 132.8 489.336 133.8 493.336 133.8Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
1
examples/solid/public/file.svg
Normal file
1
examples/solid/public/file.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
1
examples/solid/public/globe.svg
Normal file
1
examples/solid/public/globe.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
examples/solid/public/solid.svg
Normal file
1
examples/solid/public/solid.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg data-hk="000000220" width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg" role="presentation" class="drop-shadow-[10px_20px_35px_rgb(125,211,252,0.3)] size-52 md:size-[400px] mx-auto"><path d="M233.205 430.856L304.742 425.279C304.742 425.279 329.208 421.295 343.569 397.659L293.041 385.443L233.205 430.856Z" fill="url(#paint0_linear_1_2)"></path><path d="M134.278 263.278C113.003 264.341 73.6443 268.059 73.6443 268.059L245.173 392.614L284.265 402.44L343.569 397.925L170.977 273.105C170.977 273.105 157.148 263.278 137.203 263.278C136.139 263.278 135.342 263.278 134.278 263.278Z" fill="url(#paint1_linear_1_2)"></path><path d="M355.536 238.58L429.2 234.065C429.2 234.065 454.464 230.348 468.825 206.977L416.435 193.964L355.536 238.58Z" fill="url(#paint2_linear_1_2)"></path><path d="M251.289 68.6128C229.217 69.4095 188.795 72.5964 188.795 72.5964L367.503 200.072L407.926 210.429L469.09 206.712L289.318 78.9702C289.318 78.9702 274.426 68.6128 253.417 68.6128C252.885 68.6128 252.087 68.6128 251.289 68.6128Z" fill="url(#paint3_linear_1_2)"></path><path d="M31.0946 295.679C30.8287 295.945 30.8287 296.21 30.8287 296.475L77.8993 330.469L202.623 420.764C228.95 439.62 264.586 431.653 282.67 402.44L187.465 333.921L110.077 277.62C100.504 270.715 89.8663 267.528 79.2289 267.528C60.6134 267.528 42.2639 277.354 31.0946 295.679Z" fill="url(#paint4_linear_1_2)"></path><path d="M147.043 99.9505C147.043 100.216 146.776 100.482 146.511 100.747L195.442 135.538L244.374 170.062L325.751 227.957C353.142 247.345 389.841 239.642 407.925 210.695L358.461 175.374L308.997 140.318L228.153 82.6881C218.047 75.5177 206.611 72.0652 195.442 72.0652C176.561 72.3308 158.212 81.8915 147.043 99.9505Z" fill="url(#paint5_linear_1_2)"></path><path d="M112.471 139.255L175.497 208.305C178.423 212.289 181.614 216.006 185.337 219.193L308.199 354.105L369.364 350.387C387.448 321.439 380.002 282.135 352.611 262.748L271.234 204.852L222.568 170.328L173.636 135.538L112.471 139.255Z" fill="url(#paint6_linear_1_2)"></path><path d="M111.939 140.052C94.1213 168.734 101.567 207.509 128.427 226.629L209.005 283.994L258.735 319.049L308.199 354.105C326.283 325.158 318.836 285.852 291.445 266.465L112.471 139.255C112.471 139.521 112.204 139.787 111.939 140.052Z" fill="url(#paint7_linear_1_2)"></path><defs><linearGradient id="paint0_linear_1_2" x1="359.728" y1="56.8062" x2="265.623" y2="521.28" gradientUnits="userSpaceOnUse"><stop stop-color="#1593F5"></stop><stop offset="1" stop-color="#0084CE"></stop></linearGradient><linearGradient id="paint1_linear_1_2" x1="350.496" y1="559.872" x2="-44.0802" y2="-73.2062" gradientUnits="userSpaceOnUse"><stop stop-color="#1593F5"></stop><stop offset="1" stop-color="#0084CE"></stop></linearGradient><linearGradient id="paint2_linear_1_2" x1="610.25" y1="570.526" x2="372.635" y2="144.034" gradientUnits="userSpaceOnUse"><stop stop-color="white"></stop><stop offset="1" stop-color="#15ABFF"></stop></linearGradient><linearGradient id="paint3_linear_1_2" x1="188.808" y1="-180.608" x2="390.515" y2="281.703" gradientUnits="userSpaceOnUse"><stop stop-color="white"></stop><stop offset="1" stop-color="#79CFFF"></stop></linearGradient><linearGradient id="paint4_linear_1_2" x1="415.84" y1="-4.74684" x2="95.1922" y2="439.83" gradientUnits="userSpaceOnUse"><stop stop-color="#0057E5"></stop><stop offset="1" stop-color="#0084CE"></stop></linearGradient><linearGradient id="paint5_linear_1_2" x1="343.141" y1="-21.5427" x2="242.301" y2="256.708" gradientUnits="userSpaceOnUse"><stop stop-color="white"></stop><stop offset="1" stop-color="#15ABFF"></stop></linearGradient><linearGradient id="paint6_linear_1_2" x1="469.095" y1="533.421" x2="-37.6939" y2="-135.731" gradientUnits="userSpaceOnUse"><stop stop-color="white"></stop><stop offset="1" stop-color="#79CFFF"></stop></linearGradient><linearGradient id="paint7_linear_1_2" x1="380.676" y1="-89.0869" x2="120.669" y2="424.902" gradientUnits="userSpaceOnUse"><stop stop-color="white"></stop><stop offset="1" stop-color="#79CFFF"></stop></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
1
examples/solid/public/window.svg
Normal file
1
examples/solid/public/window.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||
|
After Width: | Height: | Size: 385 B |
33
examples/solid/src/app.css
Normal file
33
examples/solid/src/app.css
Normal file
@@ -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)
|
||||
}
|
||||
20
examples/solid/src/app.tsx
Normal file
20
examples/solid/src/app.tsx
Normal file
@@ -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 (
|
||||
<Router
|
||||
root={props => (
|
||||
<MetaProvider>
|
||||
<Title>Solid Start 🤝 Bknd.io</Title>
|
||||
<Suspense>{props.children}</Suspense>
|
||||
</MetaProvider>
|
||||
)}
|
||||
>
|
||||
<FileRoutes />
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
53
examples/solid/src/components/Footer.tsx
Normal file
53
examples/solid/src/components/Footer.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { A, useLocation } from "@solidjs/router";
|
||||
|
||||
export function Footer() {
|
||||
const pathname = useLocation().pathname;
|
||||
|
||||
return (
|
||||
<footer class="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
||||
<A
|
||||
class="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href={pathname === "/" ? "/user" : "/"}
|
||||
>
|
||||
<img
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
{pathname === "/" ? "User" : "Home"}
|
||||
</A>
|
||||
<A
|
||||
target="_self"
|
||||
class="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="/admin"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Admin
|
||||
</A>
|
||||
<a
|
||||
class="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href={"https://bknd.io"}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to bknd.io →
|
||||
</a>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
11
examples/solid/src/components/List.tsx
Normal file
11
examples/solid/src/components/List.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { JSX } from "solid-js";
|
||||
|
||||
export const List = ({ items = [] }: { items: JSX.Element[] }) => (
|
||||
<ol class="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
{items.map((item, i) => (
|
||||
<li class={i < items.length - 1 ? "mb-2" : ""}>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
);
|
||||
4
examples/solid/src/entry-client.tsx
Normal file
4
examples/solid/src/entry-client.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
// @refresh reload
|
||||
import { mount, StartClient } from "@solidjs/start/client";
|
||||
|
||||
mount(() => <StartClient />, document.getElementById("app")!);
|
||||
21
examples/solid/src/entry-server.tsx
Normal file
21
examples/solid/src/entry-server.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
// @refresh reload
|
||||
import { createHandler, StartServer } from "@solidjs/start/server";
|
||||
|
||||
export default createHandler(() => (
|
||||
<StartServer
|
||||
document={({ assets, children, scripts }) => (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/solid.svg" />
|
||||
{assets}
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">{children}</div>
|
||||
{scripts}
|
||||
</body>
|
||||
</html>
|
||||
)}
|
||||
/>
|
||||
));
|
||||
1
examples/solid/src/global.d.ts
vendored
Normal file
1
examples/solid/src/global.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="@solidjs/start/env" />
|
||||
30
examples/solid/src/lib/bknd.ts
Normal file
30
examples/solid/src/lib/bknd.ts
Normal file
@@ -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();
|
||||
}
|
||||
20
examples/solid/src/middleware/index.ts
Normal file
20
examples/solid/src/middleware/index.ts
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
19
examples/solid/src/routes/[...404].tsx
Normal file
19
examples/solid/src/routes/[...404].tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { HttpStatusCode } from "@solidjs/start";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<main>
|
||||
<Title>Not Found</Title>
|
||||
<HttpStatusCode code={404} />
|
||||
<h1>Page Not Found</h1>
|
||||
<p>
|
||||
Visit{" "}
|
||||
<a href="https://start.solidjs.com" target="_blank">
|
||||
start.solidjs.com
|
||||
</a>{" "}
|
||||
to learn how to build SolidStart apps.
|
||||
</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
150
examples/solid/src/routes/index.tsx
Normal file
150
examples/solid/src/routes/index.tsx
Normal file
@@ -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 (
|
||||
<div class="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 ">
|
||||
<main class="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
<div class="flex flex-row items-center ">
|
||||
<img
|
||||
class="dark:invert size-18"
|
||||
src="/solid.svg"
|
||||
alt="Solid Start logo"
|
||||
/>
|
||||
<div class="ml-3.5 mr-2 opacity-70">&</div>
|
||||
<img
|
||||
class="dark:invert"
|
||||
src="/bknd.svg"
|
||||
alt="bknd logo"
|
||||
width={183}
|
||||
height={59}
|
||||
/>
|
||||
</div>
|
||||
<Description />
|
||||
<Suspense>
|
||||
<div class="flex flex-col border border-foreground/15 w-full py-4 px-5 gap-2">
|
||||
<h2 class=" mb-1 opacity-70">
|
||||
<code>What's next?</code>
|
||||
</h2>
|
||||
<div class="flex flex-col w-full gap-2">
|
||||
{(data()?.total ?? 0) > (data()?.limit ?? 0) && (
|
||||
<div class="bg-foreground/10 flex justify-center p-1 text-xs rounded text-foreground/40">
|
||||
{(data()?.total ?? 0) - (data()?.limit ?? 0)} more todo(s) hidden
|
||||
</div>
|
||||
)}
|
||||
<div class="flex flex-col gap-3">
|
||||
{data()?.todos?.
|
||||
splice(0, data()?.limit ?? 0)
|
||||
.map((todo) => (
|
||||
<div class="flex flex-row">
|
||||
<div class="flex flex-row flex-grow items-center gap-3 ml-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="flex-shrink-0 cursor-pointer"
|
||||
checked={!!todo.done}
|
||||
onChange={async () => {
|
||||
await updateTodo(todo);
|
||||
}}
|
||||
/>
|
||||
<div class="text-foreground/90 leading-none">
|
||||
{todo.title}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="cursor-pointer grayscale transition-all hover:grayscale-0 text-xs "
|
||||
onClick={async () => {
|
||||
await removeTodo(todo);
|
||||
}}
|
||||
>
|
||||
❌
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<form
|
||||
class="flex flex-row w-full gap-3 mt-2"
|
||||
action={createTodo}
|
||||
method="post"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
name="title"
|
||||
placeholder="New todo"
|
||||
class="py-2 px-4 flex flex-grow rounded-sm bg-foreground/10 focus:bg-foreground/20 transition-colors outline-none"
|
||||
/>
|
||||
<button type="submit" class="cursor-pointer" disabled={submission.pending}>
|
||||
{submission.pending ? "Adding..." : "Add"}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Suspense>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Description = () => (
|
||||
<List
|
||||
items={[
|
||||
"Get started with a full backend.",
|
||||
"Focus on what matters instead of repetition.",
|
||||
]}
|
||||
/>
|
||||
);
|
||||
139
examples/solid/src/routes/user.tsx
Normal file
139
examples/solid/src/routes/user.tsx
Normal file
@@ -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 (
|
||||
<div class="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
|
||||
<main class="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
<div class="flex flex-row items-center ">
|
||||
<img
|
||||
class="dark:invert size-18"
|
||||
src="/solid.svg"
|
||||
alt="Solid logo"
|
||||
/>
|
||||
<div class="ml-3.5 mr-2 opacity-70">&</div>
|
||||
<img
|
||||
class="dark:invert"
|
||||
src="/bknd.svg"
|
||||
alt="bknd logo"
|
||||
width={183}
|
||||
height={59}
|
||||
/>
|
||||
</div>
|
||||
<List items={(data()?.todo.todos ?? []).map((todo) => todo.title)} />
|
||||
<Buttons />
|
||||
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<div>
|
||||
{data()?.user ? (
|
||||
<>
|
||||
Logged in as {data()?.user?.email}.
|
||||
<A
|
||||
class="underline"
|
||||
// target="_self" is required to prevent solid router from intercepting the link
|
||||
target="_self"
|
||||
href={"/api/auth/logout"}
|
||||
>
|
||||
Logout
|
||||
</A>
|
||||
</>
|
||||
) : (
|
||||
<div class="flex flex-col gap-1">
|
||||
<p>
|
||||
Not logged in.
|
||||
<A
|
||||
class="underline"
|
||||
// target="_self" is required to prevent solid router from intercepting the link
|
||||
target="_self"
|
||||
href={"/admin/auth/login"}
|
||||
>
|
||||
Login
|
||||
</A>
|
||||
</p>
|
||||
<p class="text-xs opacity-50">
|
||||
Sign in with:
|
||||
<b>
|
||||
<code>test@bknd.io</code>
|
||||
</b>
|
||||
/
|
||||
<b>
|
||||
<code>12345678</code>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Suspense>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Buttons() {
|
||||
return (
|
||||
<div class="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
class="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground gap-2 text-white hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
||||
href="https://bknd.io/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img
|
||||
class="grayscale"
|
||||
src="/bknd.ico"
|
||||
alt="bknd logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Go To Bknd.io
|
||||
</a>
|
||||
<a
|
||||
class="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
||||
href="https://docs.bknd.io/start"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
19
examples/solid/tsconfig.json
Normal file
19
examples/solid/tsconfig.json
Normal file
@@ -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/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
15
examples/solid/vite.config.ts
Normal file
15
examples/solid/vite.config.ts
Normal file
@@ -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(),
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user