# Migration Guide From `supabase-js`

This guide helps teams move application code from `supabase-js`-style usage to `@tidbcloud/auth-js`.

`@tidbcloud/auth-js` is Supabase-like in developer experience, but it is not a drop-in replacement for `supabase-js`. Treat the migration as an application integration migration to TiDB-native published Data APIs, not as a package rename.

## What Carries Over

The following ideas are intentionally familiar:

- `createClient(url, key, options)` as the application client entry
- `client.from('table')` for published table resources
- chainable, immutable query builders
- typed table names, rows, insert payloads, update payloads, and columns
- request envelopes with `data`, `error`, status, headers, and request id
- Auth-aware request execution with a user access token
- `auth.signInWithOAuth()` for GitHub and Google, including `redirectTo`, `queryParams`, and `skipBrowserRedirect`
- `auth.exchangeCodeForSession()` for OAuth callback code exchange
- runtime-specific browser, server, and edge boundaries

## What Does Not Carry Over

The following `supabase-js`, PostgREST, or PostgreSQL expectations are not supported:

- no PostgreSQL compatibility promise
- no PostgreSQL RLS compatibility promise
- no PostgREST grammar compatibility promise
- no `rpc()` or procedure/function execution
- no raw SQL or arbitrary query API
- no DDL, DCL, schema mutation, grant, revoke, SQL console, or admin API
- no PostgreSQL extensions or extension-like client API
- no PostgreSQL `RETURNING` or `Prefer: return=representation`
- no transaction scripting through request headers
- no embedded relationship select grammar such as `comments(*)`
- no aggregate select grammar such as `status.count()`
- no direct database introspection from application SDK code

Unsupported features must be redesigned or moved behind controlled server-side application code. They must not be tunneled through this SDK.

## Client Creation

### Before

```ts
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(supabaseUrl, supabaseAnonKey);
```

### After

```ts
import { createClient } from '@tidbcloud/auth-js';

const tidb = createClient<Database>(tidbCloudApiUrl, publicClientKey, {
  auth: {
    getAccessToken: async () => currentUserJwt
  }
});
```

Use the TiDB Cloud Supabase-like service URL and public client key. Do not pass a service key into browser code.

## Selecting Rows

### Before

```ts
const { data, error } = await supabase
  .from('tasks')
  .select('id,title,status')
  .eq('status', 'todo')
  .limit(10);
```

### After

```ts
const { data, error } = await tidb
  .from('tasks')
  .select('id,title,status')
  .eq('status', 'todo')
  .limit(10);
```

Simple table selection, filters, order, limit, offset, range, and exact count map naturally when the target table is published by the TiDB-native Data API.

## Inserts, Updates, Deletes, Upserts

### Insert

```ts
await tidb.from('tasks').insert({ title: 'Ship SDK', status: 'todo' });
```

### Update

```ts
await tidb.from('tasks').update({ status: 'done' }).eq('id', 1);
```

### Delete

```ts
await tidb.from('tasks').delete().eq('id', 1);
```

### Upsert

```ts
await tidb
  .from('tasks')
  .upsert({ id: 1, title: 'Ship SDK', status: 'doing' }, { onConflict: 'id' });
```

Important difference:

- `PATCH` and `DELETE` require an explicit filter.
- `Prefer: return=representation`, `Prefer: return=minimal`, and transaction preferences are unsupported.
- If application code depended on returned mutation rows, fetch the affected row with a follow-up `select()` using a stable key.

## Auth Headers

Authenticated Data API calls must include both headers:

```http
apikey: <client-key>
Authorization: Bearer <user-jwt>
```

Bearer-only requests are invalid. This differs from migrations that assume the JWT alone identifies the application client.

## OAuth Migration

### Before

```ts
await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: {
    redirectTo: 'https://app.example.test/auth/callback',
    scopes: 'repo gist notifications',
    queryParams: { prompt: 'consent' }
  }
});
```

### After

```ts
await tidb.auth.signInWithOAuth({
  provider: 'github',
  options: {
    redirectTo: 'https://app.example.test/auth/callback',
    queryParams: { prompt: 'consent' }
  }
});
```

Supported application providers are currently `github` and `google`.

Important differences:

- The SDK calls the TiDB Cloud Auth route `/auth/v1/authorize/{provider}`, not Supabase GoTrue directly.
- OAuth provider scopes are configured by the TiDB Cloud Auth service; public clients cannot override them.
- The Auth service validates provider enablement and redirect allowlists.
- PKCE is always used for OAuth start and the code verifier is stored under `${storageKey}-code-verifier`.
- Callback exchange goes through `/auth/v1/callback/{provider}` and requires the public client key.
- Provider tokens are available as optional `session.provider_token` and `session.provider_refresh_token` only when the Auth service returns them.

Server and edge handlers can exchange a callback URL directly:

```ts
await tidb.auth.exchangeOAuthCallbackForSession(request.url, {
  provider: 'github',
  redirectTo: 'https://app.example.test/auth/callback'
});
```

Password recovery maps to TiDB Cloud Auth routes instead of Supabase GoTrue:

```ts
await tidb.auth.resetPasswordForEmail('ada@example.test', {
  redirectTo: 'https://app.example.test/reset-password'
});

await tidb.auth.updateUserPassword({
  token: recoveryToken,
  password: 'new-password'
});
```

## Replacing `rpc()`

There is no public `rpc()` replacement.

Use one of these patterns:

- publish a table/resource and access it through the Data API
- implement procedure-like business behavior in your own server application
- expose a controlled service endpoint outside the public Data API contract
- document the operation as unsupported if it depends on direct database procedure execution

Do not emulate `rpc()` by adding raw SQL, procedure routes, or admin routes to this SDK.

## Replacing Raw SQL

There is no public raw SQL replacement.

Migration options:

- remodel the workflow around published table resources
- add a server-side application endpoint with explicit validation and audit
- use controlled admin/migration tooling outside this application SDK

Raw SQL strings must not be generated by the browser, server, or edge application SDK API.

## Replacing PostgreSQL RLS Assumptions

`@tidbcloud/auth-js` does not implement PostgreSQL RLS semantics.

Migration work should map authorization to the TiDB-native service model:

- published resources
- visible columns
- allowed actions
- client key and user JWT requirements
- server-side scoped service principals where explicitly allowed
- audit trail requirements

Do not assume service keys bypass policies by default.

## Type Generation

`supabase-js` projects often use generated database types from PostgreSQL metadata. For `@tidbcloud/auth-js`, generated types must come from published TiDB-native metadata such as `/schema` or `/doc`.

The type model is:

```ts
interface Database {
  readonly Tables: {
    readonly tasks: {
      readonly Row: { readonly id: number; readonly title: string };
      readonly Insert: { readonly title: string };
      readonly Update: { readonly title?: string };
    };
  };
}
```

Only published tables and columns should appear in generated application types.

Generated types may also expose `PublishedResources` metadata:

```ts
type TaskActions = Database['PublishedResources']['tasks']['AllowedActions'];
type TaskColumns = Database['PublishedResources']['tasks']['AllowedColumns'];
```

These types help inspect the published contract, but the service remains the final policy and row-filter boundary.

## Migration Checklist

- Identify every `supabase.from()` call and map it to a published TiDB Data API table.
- Replace simple selects, filters, pagination, inserts, updates, deletes, and upserts with `@tidbcloud/auth-js` builders.
- Remove or redesign `rpc()`, raw SQL, schema mutation, admin, procedure, and SQL console usage.
- Replace PostgREST embedded relationship and aggregate grammar with explicit published resources or server-side application endpoints.
- Replace PostgreSQL RLS assumptions with TiDB-native Auth/Policy/resource visibility rules.
- Ensure authenticated Data API calls send both `apikey` and `Authorization`.
- Confirm browser bundles contain no service key, service-key helper, admin helper, or server-only import.
- Regenerate or hand-author the `Database` type from published metadata only.
- Run contract, type, security, export, bundle, and release checks before production rollout.

## Common Migration Errors

| Symptom | Meaning | Fix |
| --- | --- | --- |
| `bearer_requires_client_key` | A bearer token was sent without a client key. | Send both `apikey` and `Authorization`. |
| `unsupported_operation` for `/rpc/...` | Procedure execution is outside the public Data API boundary. | Move logic to controlled server-side application code. |
| `unsupported_operation` for `/sql` | Raw SQL is forbidden. | Use published Data API resources or controlled admin tooling. |
| `table_not_published` | The table exists but is not published for Data API use. | Publish the resource through the controlled service workflow. |
| `unsupported_predicate_grammar` | The request used raw or unsupported query grammar. | Use typed builder filters. |
