# Quickstart

> Install better-supabase, generate your schema and run your first typed query.

Source: https://bettersupabase.com/docs/getting-started

### 1. Install [#install]

```bash
pnpm add better-supabase @supabase/supabase-js
pnpm add -D pg @supabase/postgrest-typegen@0.4.0
```

The `better-supabase` command ships in the `better-supabase` package. `pg` and
`@supabase/postgrest-typegen` are optional peers that only the CLI loads: `pg`
reads your database schema, and postgrest-typegen writes `database.types.ts`.
Keep postgrest-typegen at the version above, which the CLI's output is tested
against; `gen` prints a notice for any other release, and without it, `gen`,
`introspect` and `doctor` stop with the install command. Every other peer is
optional too; [Peers](/docs/getting-started/peers) lists which subpath needs
which one. TypeScript 6 and 7 are
supported, with `nodenext` or
`bundler` module resolution. On runtimes without a native `Temporal` (Node 24,
Safari, Hermes), install `temporal-polyfill` and pass its namespace to
`defineSupabase(schema, { temporal: Temporal })`, or import
`temporal-polyfill/global` once at startup; see
[Temporal](/docs/concepts/temporal).

### 2. Configure [#configure]

Run `pnpm better-supabase init` to write the config, `src/lib/supabase/index.ts`,
and glue for the frameworks it finds (see [init](/docs/cli/init)). In a
terminal it asks for the row casing and the integrations; `--yes` takes the
defaults. Before installing anything, `npx better-supabase init` does
the same and prints the install command for your package manager. Or create
`better-supabase.config.ts` in your project root yourself:

```ts title="better-supabase.config.ts"
import { defineConfig, valibot } from "better-supabase/config";

export default defineConfig({
  casing: "camel",
  output: "src/lib/supabase/generated.ts",
  plugins: { timestamps: true, softDelete: true },
  generators: [valibot()],
});
```

Every option has a default, so an empty config works too. A JSON config
(`better-supabase.config.json`) is also supported and validated by
[`schemas/config-v1.json`](/docs/cli/config).

### 3. Generate [#generate]

Start your local stack and generate:

```bash
supabase start
pnpm better-supabase env   # URL and keys into .env.local
pnpm better-supabase gen
```

This writes `database.types.ts` (from `supabase gen types`) and
`generated.ts` with models, relation metadata and the `schema` object. Run
`better-supabase gen --check` in CI to fail on drift. As you add SQL modules,
an OpenAPI entry or a seed, `gen` runs those generators too, so it stays the
one command to run (see [gen tasks](/docs/cli/gen#tasks)).

### 4. Query [#query]

```ts
import { createClient } from "@supabase/supabase-js";
import { defineSupabase } from "better-supabase";

import { schema } from "./lib/supabase/generated.ts";

export const betterSupabase = defineSupabase(schema);

const db = betterSupabase.connect(createClient(url, publishableKey));

const customers = await db.customers
  .findMany({
    select: ["id", "name"],
    where: { status: "active", notes: { some: { kind: "call" } } },
    include: { organization: { select: ["name"] } },
    orderBy: { name: "asc" },
    limit: 20,
  })
  .orThrow();
// { id: string; name: string; organization: { name: string } }[]
```

`defineSupabase` holds no secrets and no connection. Call `connect()` per
request with the client for the current user, so RLS always applies.

## Next steps [#next-steps]

- [How codegen works](/docs/concepts)
- [Filtering](/docs/repository/filtering)
- [Results and errors](/docs/concepts/results)