Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions versioned_docs/version-3.x/modeling/attribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ By convention, attributes attached to models use a double `@@` prefix, while tho

```zmodel
model User {
id Int @id
email String @unique
id Int @id
email String @unique

@@index([email, name])
}
Expand Down
2 changes: 1 addition & 1 deletion versioned_docs/version-3.x/modeling/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ model User {
}

model Post {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
Expand Down
14 changes: 7 additions & 7 deletions versioned_docs/version-3.x/modeling/model.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ If your model needs a composite ID, you can use the `@@id` model-level attribute
```zmodel
model City {
country String
name String
name String
// highlight-next-line
@@id([country, name])
}
Expand All @@ -51,7 +51,7 @@ model User {

model City {
country String
name String
name String
// highlight-next-line
@@unique([country, name])
}
Expand Down Expand Up @@ -95,7 +95,7 @@ Each model field must at least have a name and a type. A field can be typed in o
}

model User {
id Int @id
id Int @id
// highlight-next-line
role Role
}
Expand All @@ -107,9 +107,9 @@ Each model field must at least have a name and a type. A field can be typed in o

```zmodel
model Post {
id Int @id
id Int @id
// highlight-next-line
author User @relation(fields: [authorId], references: [id])
author User @relation(fields: [authorId], references: [id])
authorId Int
}
```
Expand All @@ -126,7 +126,7 @@ Each model field must at least have a name and a type. A field can be typed in o
}

model User {
id Int @id
id Int @id
// highlight-next-line
address Address @json
}
Expand All @@ -136,7 +136,7 @@ A field can be set as optional by adding the `?` suffix to its type, or list by

```zmodel
model User {
id Int @id
id Int @id
// highlight-next-line
name String?
// highlight-next-line
Expand Down
2 changes: 1 addition & 1 deletion versioned_docs/version-3.x/modeling/polymorphism.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ There are two special things about a polymorphic base model:

You can also have a deep hierarchy involving multiple levels of base models. Just need to make sure each base model has its own discriminator field and `@@delegate` attribute. Extending from multiple base models directly is not supported.

## Migration behavior
## Database schema

The migration engine takes care of mapping both the base model and the concrete ones to tables, and creates one-to-one relations between the base and each of its derivations.

Expand Down
10 changes: 5 additions & 5 deletions versioned_docs/version-3.x/modeling/relation.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ A typical one-to-many relation looks like this:

```zmodel
model User {
id Int @id
posts Post[]
id Int @id
posts Post[]
}

model Post {
id Int @id
author User @relation(fields: [authorId], references: [id])
authorId Int
id Int @id
author User @relation(fields: [authorId], references: [id])
authorId Int
}
```

Expand Down
3 changes: 3 additions & 0 deletions versioned_docs/version-3.x/recipe/_category_.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ position: 7
label: Recipes
collapsible: true
collapsed: true
link:
type: generated-index
title: Recipes
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ position: 1
label: Integrating With Authentication
collapsible: true
collapsed: true
link:
type: generated-index
title: Recipes
71 changes: 71 additions & 0 deletions versioned_docs/version-3.x/recipe/auth-integration/supabase.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
description: Integrating with Supabase Auth.
sidebar_position: 4
sidebar_label: Supabase Auth
---

# Integrating With Supabase Auth

[Supabase](https://supabase.com) is a comprehensive Backend-as-a-Service that offers database, authentication, and other services.

To get access policies to work, ZenStack needs to be connected to the authentication system to get the user's identity. If you use Supabase as your authentication provider, this document will guide you through integrating ZenStack with it.

## Syncing Supabase Auth users to your database

:::info
This section is only relevant if you're also using Supabase's Database service as the underlying Postgres database of ZenStack.
:::

Supabase Auth stores user data in a separate Postgres schema called "auth". Since that schema is managed by Supabase, it's good idea NOT to directly import it into ZModel and use it in your application. Instead, if you want to have a synchronized copy of the data, refer to [Supabase's user management documentation](https://supabase.com/docs/guides/auth/managing-user-data) for how to set up database triggers and keep your user table in sync with Supabase Auth.

## Creating a user-bound ORM client

Supabase provides the `@supabase/ssr` package to help with server-side authentication. Please refer to [its documentation](https://supabase.com/docs/guides/auth/server-side) for full details. The following example shows how to do it in a Next.js application.

```ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
import { db } from '@/lib/db' // your ZenStackClient instance

// create a Supabase SSR client
async function createSupabaseSSRClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value }) => cookieStore.set(name, value))
} catch {
// The `setAll` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
},
}
)
}

// create a user-bound ORM client
async function getUserDb() {
const supabase = await createSupabaseSSRClient();
const { data: { user } } = await supabase.auth.getUser()

// you can selectively include fields into the context object
// depending on what your access policies need
const contextUser = user ? { id: user.id } : undefined;
return db.$setAuth(contextUser);
}
```

:::warning
It may be tempting to call the `supabase.auth.getSession()` API to get the current user. However, the data returned is not validated by Supabase's service, so it must not be trusted.
:::

You can then use this user-bound ORM client for CRUD operations governed by the access policies you defined in ZModel.