279 lines
5.9 KiB
Markdown
279 lines
5.9 KiB
Markdown
# Schema Reference
|
|
|
|
## Contents
|
|
- [Defining Types](#defining-types)
|
|
- [Core Directives](#core-directives)
|
|
- [Relationships](#relationships)
|
|
- [Data Types](#data-types)
|
|
- [Enumerations](#enumerations)
|
|
|
|
---
|
|
|
|
## Defining Types
|
|
|
|
Types with `@table` map to PostgreSQL tables. Data Connect auto-generates an implicit `id: UUID!` primary key.
|
|
|
|
```graphql
|
|
type Movie @table {
|
|
# id: UUID! is auto-added
|
|
title: String!
|
|
releaseYear: Int
|
|
genre: String
|
|
}
|
|
```
|
|
|
|
### Customizing Tables
|
|
|
|
```graphql
|
|
type Movie @table(name: "movies", key: "id", singular: "movie", plural: "movies") {
|
|
id: UUID! @col(name: "movie_id") @default(expr: "uuidV4()")
|
|
title: String!
|
|
releaseYear: Int @col(name: "release_year")
|
|
genre: String @col(dataType: "varchar(20)")
|
|
}
|
|
```
|
|
|
|
### User Table with Auth
|
|
|
|
```graphql
|
|
type User @table(key: "uid") {
|
|
uid: String! @default(expr: "auth.uid")
|
|
email: String! @unique
|
|
displayName: String @col(dataType: "varchar(100)")
|
|
createdAt: Timestamp! @default(expr: "request.time")
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Core Directives
|
|
|
|
### @table
|
|
Defines a database table.
|
|
|
|
| Argument | Description |
|
|
|----------|-------------|
|
|
| `name` | PostgreSQL table name (snake_case default) |
|
|
| `key` | Primary key field(s), default `["id"]` |
|
|
| `singular` | Singular name for generated fields |
|
|
| `plural` | Plural name for generated fields |
|
|
|
|
### @col
|
|
Customizes column mapping.
|
|
|
|
| Argument | Description |
|
|
|----------|-------------|
|
|
| `name` | Column name in PostgreSQL |
|
|
| `dataType` | PostgreSQL type: `serial`, `varchar(n)`, `text`, etc. |
|
|
| `size` | Required for `Vector` type |
|
|
|
|
### @default
|
|
Sets default value for inserts.
|
|
|
|
| Argument | Description |
|
|
|----------|-------------|
|
|
| `value` | Literal value: `@default(value: "draft")` |
|
|
| `expr` | CEL expression: `@default(expr: "uuidV4()")`, `@default(expr: "auth.uid")`, `@default(expr: "request.time")` |
|
|
| `sql` | Raw SQL: `@default(sql: "now()")` |
|
|
|
|
**Common expressions:**
|
|
- `uuidV4()` - Generate UUID
|
|
- `auth.uid` - Current user's Firebase Auth UID
|
|
- `request.time` - Server timestamp
|
|
|
|
### @unique
|
|
Adds unique constraint.
|
|
|
|
```graphql
|
|
type User @table {
|
|
email: String! @unique
|
|
}
|
|
|
|
# Composite unique
|
|
type Review @table @unique(fields: ["movie", "user"]) {
|
|
movie: Movie!
|
|
user: User!
|
|
rating: Int
|
|
}
|
|
```
|
|
|
|
### @index
|
|
Creates database index for query performance.
|
|
|
|
```graphql
|
|
type Movie @table @index(fields: ["genre", "releaseYear"], order: [ASC, DESC]) {
|
|
title: String! @index
|
|
genre: String
|
|
releaseYear: Int
|
|
}
|
|
```
|
|
|
|
| Argument | Description |
|
|
|----------|-------------|
|
|
| `fields` | Fields for composite index (on @table) |
|
|
| `order` | `[ASC]` or `[DESC]` for each field |
|
|
| `type` | `BTREE` (default), `GIN` (arrays), `HNSW`/`IVFFLAT` (vectors) |
|
|
|
|
### @searchable
|
|
Enables full-text search on String fields.
|
|
|
|
```graphql
|
|
type Post @table {
|
|
title: String! @searchable
|
|
body: String! @searchable(language: "english")
|
|
}
|
|
|
|
# Usage
|
|
query SearchPosts($q: String!) @auth(level: PUBLIC) {
|
|
posts_search(query: $q) { id title body }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Relationships
|
|
|
|
### One-to-Many (Implicit Foreign Key)
|
|
|
|
```graphql
|
|
type Post @table {
|
|
id: UUID! @default(expr: "uuidV4()")
|
|
author: User! # Creates authorId foreign key
|
|
title: String!
|
|
}
|
|
|
|
type User @table {
|
|
id: UUID! @default(expr: "uuidV4()")
|
|
name: String!
|
|
# Auto-generated: posts_on_author: [Post!]!
|
|
}
|
|
```
|
|
|
|
### @ref Directive
|
|
Customizes foreign key reference.
|
|
|
|
```graphql
|
|
type Post @table {
|
|
author: User! @ref(fields: "authorId", references: "id")
|
|
authorId: UUID! # Explicit FK field
|
|
}
|
|
```
|
|
|
|
| Argument | Description |
|
|
|----------|-------------|
|
|
| `fields` | Local FK field name(s) |
|
|
| `references` | Target field(s) in referenced table |
|
|
| `constraintName` | PostgreSQL constraint name |
|
|
|
|
**Cascade behavior:**
|
|
- Required reference (`User!`): CASCADE DELETE (post deleted when user deleted)
|
|
- Optional reference (`User`): SET NULL (authorId set to null when user deleted)
|
|
|
|
### One-to-One
|
|
|
|
Use `@unique` on the reference field:
|
|
|
|
```graphql
|
|
type User @table { id: UUID! name: String! }
|
|
|
|
type UserProfile @table {
|
|
user: User! @unique # One profile per user
|
|
bio: String
|
|
avatarUrl: String
|
|
}
|
|
|
|
# Query: user.userProfile_on_user
|
|
```
|
|
|
|
### Many-to-Many
|
|
|
|
Use a join table with composite primary key:
|
|
|
|
```graphql
|
|
type Movie @table { id: UUID! title: String! }
|
|
type Actor @table { id: UUID! name: String! }
|
|
|
|
type MovieActor @table(key: ["movie", "actor"]) {
|
|
movie: Movie!
|
|
actor: Actor!
|
|
role: String! # Extra data on relationship
|
|
}
|
|
|
|
# Generated fields:
|
|
# - movie.actors_via_MovieActor: [Actor!]!
|
|
# - actor.movies_via_MovieActor: [Movie!]!
|
|
# - movie.movieActors_on_movie: [MovieActor!]!
|
|
```
|
|
|
|
---
|
|
|
|
## Data Types
|
|
|
|
| GraphQL Type | PostgreSQL Default | Other PostgreSQL Types |
|
|
|--------------|-------------------|----------------------|
|
|
| `String` | `text` | `varchar(n)`, `char(n)` |
|
|
| `Int` | `int4` | `int2`, `serial` |
|
|
| `Int64` | `bigint` | `bigserial`, `numeric` |
|
|
| `Float` | `float8` | `float4`, `numeric` |
|
|
| `Boolean` | `boolean` | |
|
|
| `UUID` | `uuid` | |
|
|
| `Date` | `date` | |
|
|
| `Timestamp` | `timestamptz` | Stored as UTC |
|
|
| `Any` | `jsonb` | |
|
|
| `Vector` | `vector` | Requires `@col(size: N)` |
|
|
| `[Type]` | Array | e.g., `[String]` → `text[]` |
|
|
|
|
---
|
|
|
|
## Enumerations
|
|
|
|
```graphql
|
|
enum Status {
|
|
DRAFT
|
|
PUBLISHED
|
|
ARCHIVED
|
|
}
|
|
|
|
type Post @table {
|
|
status: Status! @default(value: DRAFT)
|
|
allowedStatuses: [Status!]
|
|
}
|
|
```
|
|
|
|
**Rules:**
|
|
- Enum names: PascalCase, no underscores
|
|
- Enum values: UPPER_SNAKE_CASE
|
|
- Values are ordered (for comparison operations)
|
|
- Changing order or removing values is a breaking change
|
|
|
|
---
|
|
|
|
## Views (Advanced)
|
|
|
|
Map custom SQL queries to GraphQL types:
|
|
|
|
```graphql
|
|
type MovieStats @view(sql: """
|
|
SELECT
|
|
movie_id,
|
|
COUNT(*) as review_count,
|
|
AVG(rating) as avg_rating
|
|
FROM review
|
|
GROUP BY movie_id
|
|
""") {
|
|
movie: Movie @unique
|
|
reviewCount: Int
|
|
avgRating: Float
|
|
}
|
|
|
|
# Query movies with stats
|
|
query TopMovies @auth(level: PUBLIC) {
|
|
movies(orderBy: [{ rating: DESC }]) {
|
|
title
|
|
stats: movieStats_on_movie {
|
|
reviewCount avgRating
|
|
}
|
|
}
|
|
}
|
|
```
|