diff --git a/app/build.ts b/app/build.ts index 1ab90ea..92f6335 100644 --- a/app/build.ts +++ b/app/build.ts @@ -335,6 +335,11 @@ async function buildAdapters() { platform: "node", }), + tsup.build({ + ...baseConfig("nuxt"), + platform: "node", + }), + tsup.build({ ...baseConfig("node"), platform: "node", diff --git a/app/e2e/inc/adapters.ts b/app/e2e/inc/adapters.ts index 30b647b..66d691b 100644 --- a/app/e2e/inc/adapters.ts +++ b/app/e2e/inc/adapters.ts @@ -15,6 +15,9 @@ const configs = { nextjs: { base_path: "/admin", }, + nuxt: { + base_path: "/admin", + }, astro: { base_path: "/admin", }, diff --git a/app/package.json b/app/package.json index ceb3771..dd8d457 100644 --- a/app/package.json +++ b/app/package.json @@ -233,6 +233,11 @@ "import": "./dist/adapter/nextjs/index.js", "require": "./dist/adapter/nextjs/index.js" }, + "./adapter/nuxt": { + "types": "./dist/types/adapter/nuxt/index.d.ts", + "import": "./dist/adapter/nuxt/index.js", + "require": "./dist/adapter/nuxt/index.js" + }, "./adapter/react-router": { "types": "./dist/types/adapter/react-router/index.d.ts", "import": "./dist/adapter/react-router/index.js", @@ -287,6 +292,7 @@ "adapter/cloudflare": ["./dist/types/adapter/cloudflare/index.d.ts"], "adapter/vite": ["./dist/types/adapter/vite/index.d.ts"], "adapter/nextjs": ["./dist/types/adapter/nextjs/index.d.ts"], + "adapter/nuxt": ["./dist/types/adapter/nuxt/index.d.ts"], "adapter/react-router": ["./dist/types/adapter/react-router/index.d.ts"], "adapter/bun": ["./dist/types/adapter/bun/index.d.ts"], "adapter/node": ["./dist/types/adapter/node/index.d.ts"], @@ -318,6 +324,7 @@ "serverless", "cloudflare", "nextjs", + "nuxt", "remix", "react-router", "astro", diff --git a/app/src/adapter/nuxt/index.ts b/app/src/adapter/nuxt/index.ts new file mode 100644 index 0000000..c6920bb --- /dev/null +++ b/app/src/adapter/nuxt/index.ts @@ -0,0 +1 @@ +export * from "./nuxt.adapter"; diff --git a/app/src/adapter/nuxt/nuxt.adapter.spec.ts b/app/src/adapter/nuxt/nuxt.adapter.spec.ts new file mode 100644 index 0000000..409771d --- /dev/null +++ b/app/src/adapter/nuxt/nuxt.adapter.spec.ts @@ -0,0 +1,15 @@ +import { afterAll, beforeAll, describe } from "bun:test"; +import * as nuxt from "./nuxt.adapter"; +import { disableConsoleLog, enableConsoleLog } from "core/utils"; +import { adapterTestSuite } from "adapter/adapter-test-suite"; +import { bunTestRunner } from "adapter/bun/test"; + +beforeAll(disableConsoleLog); +afterAll(enableConsoleLog); + +describe("nuxt adapter", () => { + adapterTestSuite(bunTestRunner, { + makeApp: nuxt.getApp, + makeHandler: nuxt.serve, + }); +}); diff --git a/app/src/adapter/nuxt/nuxt.adapter.ts b/app/src/adapter/nuxt/nuxt.adapter.ts new file mode 100644 index 0000000..426c735 --- /dev/null +++ b/app/src/adapter/nuxt/nuxt.adapter.ts @@ -0,0 +1,30 @@ +import { createRuntimeApp, type RuntimeBkndConfig } from "bknd/adapter"; + +export type NuxtEnv = NodeJS.ProcessEnv; +export type NuxtBkndConfig = RuntimeBkndConfig; + +/** + * Get bknd app instance + * @param config - bknd configuration + * @param args - environment variables + */ +export async function getApp( + config: NuxtBkndConfig = {} as NuxtBkndConfig, + args: Env, +) { + return await createRuntimeApp(config, args); +} + +/** + * Create middleware handler for Nuxt + * @param config - bknd configuration + * @param args - environment variables + */ +export function serve( + config: NuxtBkndConfig = {} as NuxtBkndConfig, + args: Env, +) { + return async (request: Request) => { + return (await getApp(config, args)).fetch(request); + }; +} diff --git a/docs/content/docs/(documentation)/integration/(frameworks)/meta.json b/docs/content/docs/(documentation)/integration/(frameworks)/meta.json index 5cc7900..39b253c 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", + "nuxt" ] } diff --git a/docs/content/docs/(documentation)/integration/(frameworks)/nuxt.mdx b/docs/content/docs/(documentation)/integration/(frameworks)/nuxt.mdx new file mode 100644 index 0000000..3cc715d --- /dev/null +++ b/docs/content/docs/(documentation)/integration/(frameworks)/nuxt.mdx @@ -0,0 +1,388 @@ +--- +title: "Nuxt" +description: "Run bknd inside Nuxt" +tags: ["documentation"] +--- + +## Installation + +To get started with Nuxt and bknd, create a new Nuxt project by following the [official guide](https://nuxt.com/docs/4.x/getting-started/installation), 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: + +```typescript title="bknd.config.ts" +import type NuxtBkndConfig from "bknd/adapter/nuxt"; +import { em, entity, text, boolean } from "bknd"; +import { secureRandomString } from "bknd/utils"; + +const schema = em({ + todos: entity("todos", { + title: text(), + done: boolean(), + }), +}); + +export default { + connection: { + url: "file:data.db", + }, + config: { + data: schema.toJSON(), + auth: { + enabled: true, + jwt: { + secret: secureRandomString(32), + }, + }, + }, + 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", + }); + }, + }, +} satisfies NuxtBkndConfig; +``` + +For more information about the connection object, refer to the [Database](/usage/database) guide. + + +See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `NuxtBkndConfig` type extends the base config type with the following properties: + +```typescript +export type NuxtBkndConfig = FrameworkBkndConfig; +``` + +## Serve the API and Admin UI + +The Nuxt adapter uses Nuxt middleware to handle API requests and serve the Admin UI. Create a `/server/middleware/bknd.ts` file: + +```typescript title="/server/middleware/bknd.ts" +import { serve } from "bknd/adapter/nuxt"; +import config from "../../bknd.config"; + +const handler = serve(config, process.env); + +export default defineEventHandler(async (event) => { + const pathname = event.path; + const request = toWebRequest(event); + + if (pathname.startsWith("/api") || pathname !== "/") { + const res = await handle(request); + + if (res && res.status !== 404) { + return res; + } + } +}); +``` + + + 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="server/utils/bknd.ts" +import { type NuxtBkndConfig, getApp as getNuxtApp } from "bknd/adapter/nuxt"; +import bkndConfig from "../../bknd.config"; + +export async function getApp( + config: NuxtBkndConfig, + args: Env = process.env as Env, +) { + return await getNuxtApp(config, args); +} + +export async function getApi({ headers, verify }: { verify?: boolean; headers?: Headers }) { + const app = await getApp(bkndConfig, process.env); + + if (verify) { + const api = app.getApi({ headers }); + await api.verifyAuth(); + return api; + } + + return app.getApi(); +}; +``` + + + The adapter uses `process.env` to access environment variables, this works because Nuxt uses Nitro underneath and it will use polyfills for `process.env` making it platform/runtime agnostic. + + + +## Example usage of the API + +You can use the `getApp` function to access the bknd API in your app to expose endpoints, +Here are some examples: + +```typescript title="server/routes/todos.post.ts" +export default defineEventHandler(async (event) => { + const body = await readBody(event); + const { data, action } = body; + + const api = await getApi({}); + + switch (action) { + case 'get': { + const limit = 5; + const todos = await api.data.readMany("todos", { limit, sort: "-id" }); + return { total: todos.body.meta.total, todos, limit }; + } + + case 'create': { + return await api.data.createOne("todos", { title: data.title }); + } + + case 'delete': { + return await api.data.deleteOne("todos", data.id); + } + + case 'toggle': { + return await api.data.updateOne("todos", data.id, { done: !data.done }); + } + + default: { + return { path: action }; + } + } +}); +``` + + + This can't be done in the `/server/api` directory as it will collide with the API endpoints created by the middleware. We will use [defineEventHandler](https://nuxt.com/docs/4.x/directory-structure/server) in the [/server/routes](https://nuxt.com/docs/4.x/directory-structure/server) directory to create endpoints and use them in conjunction with composables to access the API safely. + + + +### Using the API through composables + +To use the API in your frontend components/pages, you can create a composable that uses the enpoints created in previous steps: + +```typescript title="app/composables/useTodoActions.ts" +import type { DB } from "bknd"; + +type Todo = DB["todos"]; + +export const useTodoActions = () => { + const fetchTodos = () => + $fetch<{ limit: number; todos: Array; total: number }>("/todos", { + method: "POST", + body: { action: "get" }, + }); + + const createTodo = (title: string) => + $fetch("/todos", { + method: "POST", + body: { action: "create", data: { title } }, + }); + + const deleteTodo = (todo: Todo) => + $fetch("/todos", { + method: "POST", + body: { action: "delete", data: { id: todo.id } }, + }); + + const toggleTodo = (todo: Todo) => + $fetch("/todos", { + method: "POST", + body: { action: "toggle", data: todo }, + }); + + return { fetchTodos, createTodo, deleteTodo, toggleTodo }; +}; +``` + +### Usage in a page/component + +Make a composable to fetch the todos: + +```ts title="app/composables/useTodoActions.ts" +import type { DB } from "bknd"; + +type Todo = DB["todos"]; + +export const useTodoActions = () => { + const fetchTodos = () => + $fetch<{ limit: number; todos: Array; total: number }>("/todos", { + method: "POST", + body: { action: "get" }, + }); + + const createTodo = (title: string) => + $fetch("/todos", { + method: "POST", + body: { action: "create", data: { title } }, + }); + + const deleteTodo = (todo: Todo) => + $fetch("/todos", { + method: "POST", + body: { action: "delete", data: { id: todo.id } }, + }); + + const toggleTodo = (todo: Todo) => + $fetch("/todos", { + method: "POST", + body: { action: "toggle", data: todo }, + }); + + return { fetchTodos, createTodo, deleteTodo, toggleTodo }; +}; +``` +Then use the `useTodoActions` composable in a page: + +```vue title="app/pages/todos.vue" + + + +``` + + + You can visit https://localhost:3000/todos to see all the todos. + + +### Using authentication + +Make a composable to fetch the user: + +```ts title="app/composables/useUser.ts" +import type { User } from "bknd"; + +export const useUser = () => { + const getUser = () => $fetch("/api/auth/me") as Promise<{ user: User }>; + return { getUser }; +}; +``` + +Then use the `useUser` composable in a page: + +```vue title="app/pages/user.vue" + + + +``` + +Check the [Nuxt repository example](https://github.com/bknd-io/bknd/tree/main/examples/nuxt) for more implementation details. \ 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..a04cb12 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="Nuxt" + href="/integration/nuxt" +/> + 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..d0644ae 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="Nuxt" + href="/integration/nuxt" +/> + } title="AWS Lambda" diff --git a/examples/nuxt/.gitignore b/examples/nuxt/.gitignore new file mode 100644 index 0000000..8cd13a3 --- /dev/null +++ b/examples/nuxt/.gitignore @@ -0,0 +1,27 @@ +# Nuxt dev/build outputs +.output +.data +.nuxt +.nitro +.cache +dist + +# Node dependencies +node_modules + +# Logs +logs +*.log + +# Misc +.DS_Store +.fleet +.idea + +# Local env files +.env +.env.* +!.env.example + +public/admin +data.db \ No newline at end of file diff --git a/examples/nuxt/README.md b/examples/nuxt/README.md new file mode 100644 index 0000000..25990b9 --- /dev/null +++ b/examples/nuxt/README.md @@ -0,0 +1,64 @@ +# bknd starter: Nuxt +A minimal example of a Nuxt project with bknd integration. + +## Project Structure + +Inside of your Nuxt project, you'll see the following folders and files: + +```text +. +├── app +│ ├── assets +│ │ └── css +│ ├── components +│ │ ├── Buttons.vue +│ │ ├── Footer.vue +│ │ └── List.vue +│ ├── composables +│ │ ├── useTodoActions.ts +│ │ └── useUser.ts +│ └── pages +│ ├── index.vue +│ └── user.vue +├── bknd.config.ts +├── bun.lock +├── nuxt.config.ts +├── package.json +├── public +│ ├── admin # generated by bknd and contains the admin UI +│ ├── bknd.ico +│ ├── bknd.svg +│ ├── favicon.ico +│ ├── file.svg +│ ├── globe.svg +│ ├── nuxt.svg +│ ├── robots.txt +│ └── window.svg +├── README.md +├── server +│ ├── middleware +│ │ └── bknd.ts # intercepts api and admin ui requests +│ ├── routes +│ │ └── todos.post.ts +│ └── utils +│ └── bknd.ts # initializes bknd instance +└── tsconfig.json +``` + +Here is a quick overview about how to adjust the behavior of `bknd`: +* Initialization of the `bknd` config with helper functions are located at `src/server/utils/bknd.ts` +* Admin UI is rendered at `src/server/middleware/bknd.ts` + +## Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +|:--------------------------|:-------------------------------------------------| +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:3000` | +| `npm run build` | Build your production site | + +## Want to learn more? + +Feel free to check [our documentation](https://docs.bknd.io/integration/nuxt) or jump into our [Discord server](https://discord.gg/952SFk8Tb8). \ No newline at end of file diff --git a/examples/nuxt/app/assets/css/main.css b/examples/nuxt/app/assets/css/main.css new file mode 100644 index 0000000..a88e69a --- /dev/null +++ b/examples/nuxt/app/assets/css/main.css @@ -0,0 +1,24 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; + font-weight: 400; +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +@theme { + --color-background: var(--background); + --color-foreground: var(--foreground); +} + +body { + @apply bg-background text-foreground; + font-family: Arial, Helvetica, sans-serif; +} diff --git a/examples/nuxt/app/components/Buttons.vue b/examples/nuxt/app/components/Buttons.vue new file mode 100644 index 0000000..864dc3a --- /dev/null +++ b/examples/nuxt/app/components/Buttons.vue @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/examples/nuxt/app/components/Footer.vue b/examples/nuxt/app/components/Footer.vue new file mode 100644 index 0000000..be698bf --- /dev/null +++ b/examples/nuxt/app/components/Footer.vue @@ -0,0 +1,29 @@ + + + diff --git a/examples/nuxt/app/components/List.vue b/examples/nuxt/app/components/List.vue new file mode 100644 index 0000000..a3fddc2 --- /dev/null +++ b/examples/nuxt/app/components/List.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/examples/nuxt/app/composables/useTodoActions.ts b/examples/nuxt/app/composables/useTodoActions.ts new file mode 100644 index 0000000..138a9d2 --- /dev/null +++ b/examples/nuxt/app/composables/useTodoActions.ts @@ -0,0 +1,31 @@ +import type { DB } from "bknd"; + +type Todo = DB["todos"]; + +export const useTodoActions = () => { + const fetchTodos = () => + $fetch<{ limit: number; todos: Array; total: number }>("/todos", { + method: "POST", + body: { action: "get" }, + }); + + const createTodo = (title: string) => + $fetch("/todos", { + method: "POST", + body: { action: "create", data: { title } }, + }); + + const deleteTodo = (todo: Todo) => + $fetch("/todos", { + method: "POST", + body: { action: "delete", data: { id: todo.id } }, + }); + + const toggleTodo = (todo: Todo) => + $fetch("/todos", { + method: "POST", + body: { action: "toggle", data: todo }, + }); + + return { fetchTodos, createTodo, deleteTodo, toggleTodo }; +}; diff --git a/examples/nuxt/app/composables/useUser.ts b/examples/nuxt/app/composables/useUser.ts new file mode 100644 index 0000000..1dc371a --- /dev/null +++ b/examples/nuxt/app/composables/useUser.ts @@ -0,0 +1,6 @@ +import type { User } from "bknd"; + +export const useUser = () => { + const getUser = () => $fetch("/api/auth/me") as Promise<{ user: User }>; + return { getUser }; +}; diff --git a/examples/nuxt/app/pages/index.vue b/examples/nuxt/app/pages/index.vue new file mode 100644 index 0000000..9795a16 --- /dev/null +++ b/examples/nuxt/app/pages/index.vue @@ -0,0 +1,64 @@ + + + diff --git a/examples/nuxt/app/pages/user.vue b/examples/nuxt/app/pages/user.vue new file mode 100644 index 0000000..5ac2e27 --- /dev/null +++ b/examples/nuxt/app/pages/user.vue @@ -0,0 +1,49 @@ + + + \ No newline at end of file diff --git a/examples/nuxt/bknd.config.ts b/examples/nuxt/bknd.config.ts new file mode 100644 index 0000000..7a0ba13 --- /dev/null +++ b/examples/nuxt/bknd.config.ts @@ -0,0 +1,58 @@ +import { em, entity, text, boolean, } from "bknd"; +import { secureRandomString } from "bknd/utils"; +import type { NuxtBkndConfig } from "bknd/adapter/nuxt"; +import { registerLocalMediaAdapter } from "bknd/adapter/node"; + +const local = registerLocalMediaAdapter(); + +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), + }, + }, + media: { + enabled: true, + adapter: local({ + path: "./public/uploads", + }), + }, + }, + adminOptions: { + adminBasepath: "/admin", + assetsPath: "/admin/", + }, +} satisfies NuxtBkndConfig; diff --git a/examples/nuxt/nuxt.config.ts b/examples/nuxt/nuxt.config.ts new file mode 100644 index 0000000..a179d24 --- /dev/null +++ b/examples/nuxt/nuxt.config.ts @@ -0,0 +1,11 @@ +// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + compatibilityDate: "2025-07-15", + devtools: { enabled: false }, + modules: ["@nuxtjs/tailwindcss"], + app: { + head: { + title: "Nuxt 🤝 Bknd.io", + }, + }, +}); diff --git a/examples/nuxt/package.json b/examples/nuxt/package.json new file mode 100644 index 0000000..139736d --- /dev/null +++ b/examples/nuxt/package.json @@ -0,0 +1,21 @@ +{ + "name": "nuxbknd", + "type": "module", + "private": true, + "scripts": { + "build": "nuxt build", + "dev": "nuxt dev", + "generate": "nuxt generate", + "preview": "nuxt preview", + "typegen": "bunx tsx node_modules/.bin/bknd types --outfile bknd-types.d.ts", + "postinstall": "nuxt prepare && bun run bknd copy-assets --out public/admin" + }, + "dependencies": { + "@nuxtjs/tailwindcss": "6.14.0", + "@types/node": "^25.2.3", + "bknd": "file:../../app", + "nuxt": "^4.3.1", + "vue": "^3.5.28", + "vue-router": "^4.6.4" + } +} diff --git a/examples/nuxt/public/bknd.ico b/examples/nuxt/public/bknd.ico new file mode 100644 index 0000000..c1a946d Binary files /dev/null and b/examples/nuxt/public/bknd.ico differ diff --git a/examples/nuxt/public/bknd.svg b/examples/nuxt/public/bknd.svg new file mode 100644 index 0000000..182ef92 --- /dev/null +++ b/examples/nuxt/public/bknd.svg @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/examples/nuxt/public/favicon.ico b/examples/nuxt/public/favicon.ico new file mode 100644 index 0000000..18993ad Binary files /dev/null and b/examples/nuxt/public/favicon.ico differ diff --git a/examples/nuxt/public/file.svg b/examples/nuxt/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/examples/nuxt/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/nuxt/public/globe.svg b/examples/nuxt/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/examples/nuxt/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/nuxt/public/nuxt.svg b/examples/nuxt/public/nuxt.svg new file mode 100644 index 0000000..5ad1e9d --- /dev/null +++ b/examples/nuxt/public/nuxt.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/nuxt/public/robots.txt b/examples/nuxt/public/robots.txt new file mode 100644 index 0000000..0ad279c --- /dev/null +++ b/examples/nuxt/public/robots.txt @@ -0,0 +1,2 @@ +User-Agent: * +Disallow: diff --git a/examples/nuxt/public/window.svg b/examples/nuxt/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/examples/nuxt/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/nuxt/server/middleware/bknd.ts b/examples/nuxt/server/middleware/bknd.ts new file mode 100644 index 0000000..d298768 --- /dev/null +++ b/examples/nuxt/server/middleware/bknd.ts @@ -0,0 +1,15 @@ +import { serve } from "bknd/adapter/nuxt"; +import config from "../../bknd.config"; + +export default defineEventHandler(async (event) => { + const pathname = event.path + const request = toWebRequest(event); + + if (pathname.startsWith("/api") || pathname !== "/") { + const res = await serve(config, process.env)(request); + + if (res && res.status !== 404) { + return res; + } + } +}); diff --git a/examples/nuxt/server/routes/todos.post.ts b/examples/nuxt/server/routes/todos.post.ts new file mode 100644 index 0000000..314e0ff --- /dev/null +++ b/examples/nuxt/server/routes/todos.post.ts @@ -0,0 +1,31 @@ +export default defineEventHandler(async (event) => { + const body = await readBody(event); + const { data, action } = body; + + const api = await getApi({}); + + switch (action) { + case 'get': { + const limit = 5; + const todos = await api.data.readMany("todos", { limit, sort: "-id" }); + return { total: todos.body.meta.total, todos, limit }; + } + + case 'create': { + return await api.data.createOne("todos", { title: data.title }); + } + + case 'delete': { + return await api.data.deleteOne("todos", data.id); + } + + case 'toggle': { + return await api.data.updateOne("todos", data.id, { done: !data.done }); + } + + default: { + return { path: action }; + } + } + +}); diff --git a/examples/nuxt/server/utils/bknd.ts b/examples/nuxt/server/utils/bknd.ts new file mode 100644 index 0000000..6b8d02a --- /dev/null +++ b/examples/nuxt/server/utils/bknd.ts @@ -0,0 +1,21 @@ +import { type NuxtBkndConfig, getApp as getNuxtApp } from "bknd/adapter/nuxt"; +import bkndConfig from "../../bknd.config"; + +export async function getApp( + config: NuxtBkndConfig, + args: Env = process.env as Env, +) { + return await getNuxtApp(config, args); +} + +export async function getApi({ headers, verify }: { verify?: boolean; headers?: Headers }) { + const app = await getApp(bkndConfig, process.env); + + if (verify) { + const api = app.getApi({ headers }); + await api.verifyAuth(); + return api; + } + + return app.getApi(); +} diff --git a/examples/nuxt/tsconfig.json b/examples/nuxt/tsconfig.json new file mode 100644 index 0000000..307b213 --- /dev/null +++ b/examples/nuxt/tsconfig.json @@ -0,0 +1,18 @@ +{ + // https://nuxt.com/docs/guide/concepts/typescript + "files": [], + "references": [ + { + "path": "./.nuxt/tsconfig.app.json" + }, + { + "path": "./.nuxt/tsconfig.server.json" + }, + { + "path": "./.nuxt/tsconfig.shared.json" + }, + { + "path": "./.nuxt/tsconfig.node.json" + } + ] +}