# Examples

> Runnable apps for every integration, built against a local stack.

Source: https://bettersupabase.com/docs/examples

Each example in [`apps/examples`](https://github.com/ScaleDockHQ/better-supabase/tree/main/apps/examples)
was set up with `better-supabase init`, generates its types from the same
schema, and runs against the local stack from `supabase/config.toml`.

| Example          | Shows                                                                                                                                                                                           |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `nextjs`         | A multi-tenant SaaS app on Cache Components, with every page proved instant by `instant()` tests                                                                                                |
| `hono-api`       | `createHono`, `bs.resource` with a list query, a custom route, and an admin route behind `bs.require`                                                                                           |
| `express`        | Express 5 on `better-supabase/node`: `toExpress`, a role `guard`, `problemErrorHandler`, and a smoke test against the local stack                                                               |
| `orpc-api`       | An oRPC router with valibot inputs, `bs.authed` with roles, and a contract-first OpenAPI server on Hono                                                                                         |
| `expo-powersync` | An Expo Router app: web reads in server loaders; iOS and Android read PowerSync with `useWatch`, upload with `createUploadConnector`, sign in with a magic link or OAuth, and register for push |
| `edge`           | An Edge Function with `bs.routes`, typed route params and role guards                                                                                                                           |
| `mcp`            | An MCP server as an Edge Function, exposing tables as tools, with `requiredRoles`                                                                                                               |
| `eve`            | An [eve](/docs/eve) agent on Next.js with the Supabase World, sign-in, Vault connections, memory and the inbox channel                                                                          |
| `vite-react`     | An SPA with `createClient`, `useSignIn`, `useLiveQuery`, debounced search and an optimistic create                                                                                              |

## Run them [#run-them]

```bash
supabase start
pnpm build:packages
pnpm --filter @better-supabase/example-nextjs dev
```

The examples use the stack from `supabase/config.toml` (API on port 55421).
To use another stack, set `SUPABASE_URL`, `SUPABASE_PUBLISHABLE_KEY` and
`SUPABASE_SECRET_KEY`.

## The Next.js SaaS app [#the-nextjs-saas-app]

`apps/examples/nextjs` is a generic SaaS app on Next.js Cache Components,
shadcn/ui (Base UI), next-intl and the local stack. Every page runs on the
real Auth server and database; nothing is mocked. Sign in with one of the
seeded users from `supabase/seed.sql` (password `password123`):

| User                | Role                            |
| ------------------- | ------------------------------- |
| `admin@acme.test`   | Owner of Acme, member of Globex |
| `member@acme.test`  | Member of Acme                  |
| `owner@globex.test` | Owner of Globex                 |

The features and the better-supabase parts they use:

| Feature                               | Uses                                                                                                                                                                                                         |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Sign-in, sign-up, password reset, MFA | `bs.proxy` with `protect`, the browser client, the Auth server and Mailpit                                                                                                                                   |
| Organizations, switcher, invitations  | the `organizations` and `invitations` SQL modules with `better-supabase/blocks/organizations`                                                                                                                |
| Customers list and detail             | `defineListQuery` with facets, `bs.cached()` in `"use cache: private"`, comments, logo upload                                                                                                                |
| Notifications                         | the `notifications` module with `better-supabase/blocks/notifications`, a live unread badge                                                                                                                  |
| Workflows                             | `better-supabase/blocks/workflows` and the Supabase World: a live runs list, run detail with cancel, an approval with `authorizeHook`, schedules                                                             |
| Workflow builder                      | `better-supabase/blocks/workflow-builder` on `@xyflow/react`: a node palette from the step library, a live run overlay, approvals on the canvas, trigger and credential drawers, publish with a version diff |
| Members, roles and permissions        | the `access` module in custom mode, `can()` in policies and the UI                                                                                                                                           |
| Billing and plan features             | the `entitlements` and `usage` modules                                                                                                                                                                       |
| API keys and audit log                | the `api-keys` and `audit` modules, with a CSV export                                                                                                                                                        |
| Profile, settings, onboarding         | the `profiles`, `settings` and `onboarding` modules                                                                                                                                                          |
| Announcements and a beta page         | the `announcements` and `flags` modules                                                                                                                                                                      |

The routes are localized under `/en` and `/nl` with `next/root-params`, and
the translations are `.po` files read directly by next-intl. The theme
(light, dark or system) is applied before the first paint.

`pnpm --filter @better-supabase/example-nextjs test:e2e` resets the stack,
builds the app with the testing API and runs the Playwright specs in
`apps/examples/nextjs/e2e`. They click every menu page for each user in both
languages under `instant()`, and walk through sign-in, the organization
switch, the language and theme switches and an invitation.

## Real-world ports [#real-world-ports]

`tests/validation-crm` and `tests/validation-request-context` rewrite code
from two production apps on better-supabase:

* **CRM customers service**: the customer list, which has status,
  tag, assignee and nested contact search filters plus four includes, becomes
  one `paginate` call that sends a single request. It is generated from and
  typechecked against a 263-table schema.
* **CRM latency ports**, each asserting its request budget with
  `db.$stats()`:
  * The customers overview is a `defineListQuery` with facet counts and
    per-row aggregates: 2 calls in 1 wave.
  * The app-chrome badges (unread notifications, open tasks, pending
    approvals) are one read set: 1 call.
  * The customer portal is scoped by `tenant({ claim: 'customer_id' })` on
    claims validated by `betterSupabase.claims()`: 2 calls in 1 wave.
* **An app Supabase package**: env validation, request headers, a
  user-auth guard, a cron guard with a named secret key, avatar storage and
  the browser client. Its request-context tests run against the local stack.