diff --git a/versioned_docs/version-3.x/modeling/attribute.md b/versioned_docs/version-3.x/modeling/attribute.md index e7f0c6f7..a54b2c74 100644 --- a/versioned_docs/version-3.x/modeling/attribute.md +++ b/versioned_docs/version-3.x/modeling/attribute.md @@ -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]) } diff --git a/versioned_docs/version-3.x/modeling/index.md b/versioned_docs/version-3.x/modeling/index.md index 6aace027..4f9b8caf 100644 --- a/versioned_docs/version-3.x/modeling/index.md +++ b/versioned_docs/version-3.x/modeling/index.md @@ -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 diff --git a/versioned_docs/version-3.x/modeling/model.md b/versioned_docs/version-3.x/modeling/model.md index f51c3f8f..c5975918 100644 --- a/versioned_docs/version-3.x/modeling/model.md +++ b/versioned_docs/version-3.x/modeling/model.md @@ -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]) } @@ -51,7 +51,7 @@ model User { model City { country String - name String + name String // highlight-next-line @@unique([country, name]) } @@ -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 } @@ -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 } ``` @@ -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 } @@ -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 diff --git a/versioned_docs/version-3.x/modeling/polymorphism.md b/versioned_docs/version-3.x/modeling/polymorphism.md index 9e36f0e2..9f346cfa 100644 --- a/versioned_docs/version-3.x/modeling/polymorphism.md +++ b/versioned_docs/version-3.x/modeling/polymorphism.md @@ -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. diff --git a/versioned_docs/version-3.x/modeling/relation.md b/versioned_docs/version-3.x/modeling/relation.md index aad92970..b6802890 100644 --- a/versioned_docs/version-3.x/modeling/relation.md +++ b/versioned_docs/version-3.x/modeling/relation.md @@ -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 } ``` diff --git a/versioned_docs/version-3.x/recipe/_category_.yml b/versioned_docs/version-3.x/recipe/_category_.yml index 67bd5fa5..da95113d 100644 --- a/versioned_docs/version-3.x/recipe/_category_.yml +++ b/versioned_docs/version-3.x/recipe/_category_.yml @@ -2,3 +2,6 @@ position: 7 label: Recipes collapsible: true collapsed: true +link: + type: generated-index + title: Recipes diff --git a/versioned_docs/version-3.x/recipe/auth-integration/_category_.yml b/versioned_docs/version-3.x/recipe/auth-integration/_category_.yml index 382d3a16..d596be65 100644 --- a/versioned_docs/version-3.x/recipe/auth-integration/_category_.yml +++ b/versioned_docs/version-3.x/recipe/auth-integration/_category_.yml @@ -2,3 +2,6 @@ position: 1 label: Integrating With Authentication collapsible: true collapsed: true +link: + type: generated-index + title: Recipes diff --git a/versioned_docs/version-3.x/recipe/auth-integration/supabase.md b/versioned_docs/version-3.x/recipe/auth-integration/supabase.md new file mode 100644 index 00000000..ae6741be --- /dev/null +++ b/versioned_docs/version-3.x/recipe/auth-integration/supabase.md @@ -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.