# The Create core

> How better-supabase/create holds the design model, previews and Supabase deploy client that /create and other apps share.

Source: https://bettersupabase.com/docs/presets/create

`better-supabase/create` is the headless code behind
[Create](https://bettersupabase.com/create). It runs on every runtime,
including the browser, and has no dependency beyond the Supabase packages.
Designs stay in the URL or in your own storage, as ADR 0040 requires.

```ts
import { previewFiles, diffDesigns } from "better-supabase/create";
import { namedPreset } from "better-supabase/preset";

const base = { preset: namedPreset("saas") };
const files = previewFiles(base);
console.log(files.map((file) => file.path));
```

`diffDesigns(base, next)` classifies each change as safe or breaking, so an
app can block a breaking change before it deploys.

## Supabase deploy [#supabase-deploy]

`better-supabase/create/supabase` signs a user in with Supabase OAuth and
PKCE, seals the session in a cookie value, and wraps the Management API.

```ts
import { managementClient } from "better-supabase/create/supabase";

const client = managementClient(accessToken, fetch);
const projects = await client.projects();
```

Pass your own `fetch` to trace or stub the calls in tests.

## Migrate between versions [#migrate-between-versions]

`migrationBetween(code, base, next)` writes the migration that takes a project
deployed from one design to the next. It covers added tables and added
columns, which the app-table SQL applies idempotently.

```ts
import { migrationBetween } from "better-supabase/create";

const plan = migrationBetween(code, deployed, edited);

if (plan.ok) {
  await client.applyMigration(ref, "better_supabase_design", plan.sql);
} else {
  console.log(
    plan.reason,
    plan.changes.map((change) => change.steps),
  );
}
```

A `breaking` refusal lists the expand and contract steps for each change. An
`unsupported` refusal covers any other change, such as a new default or a
change outside `appTables`, so a deploy never guesses.

## Validate in Postgres [#validate-in-postgres]

`better-supabase/create/lite` applies the generated files to an in-memory
PGlite database through the optional `@supabase/lite` peer, and reports what
Postgres rejects before anything reaches a project.

```ts
import { previewFiles } from "better-supabase/create";
import { validateDesign } from "better-supabase/create/lite";
import { encodePreset, namedPreset } from "better-supabase/preset";

const files = previewFiles(encodePreset(namedPreset("saas")));
const { ok, issues, skipped } = await validateDesign(files);
```

PGlite starts without a Supabase project's roles and `auth` schema, so the
function creates a minimal copy first. A module that needs an extension PGlite
doesn't ship (`pgmq` for jobs, `btree_gist` for time tracking) lands in
`skipped` instead of failing. pgTAP is not available through
`@supabase/lite`, so database tests still run with `supabase test db`.