204 lines
4.9 KiB
Markdown
204 lines
4.9 KiB
Markdown
# GraphQL Schema Reader
|
|
|
|
The GraphQL reader parses GraphQL Schema Definition Language (SDL) files and converts them into RelSpec's internal database model.
|
|
|
|
## Features
|
|
|
|
- **Standard GraphQL SDL** support (generic, non-framework-specific)
|
|
- **Type to Table mapping**: GraphQL types become database tables
|
|
- **Field to Column mapping**: GraphQL fields become table columns
|
|
- **Enum support**: GraphQL enums are preserved
|
|
- **Custom scalars**: DateTime, JSON, Date automatically mapped to appropriate SQL types
|
|
- **Implicit relationships**: Detects relationships from field types
|
|
- **Many-to-many support**: Creates junction tables for bidirectional array relationships
|
|
- **Configurable ID mapping**: Choose between bigint (default) or UUID for ID fields
|
|
|
|
## Supported GraphQL Features
|
|
|
|
### Built-in Scalars
|
|
- `ID` → bigint (default) or uuid (configurable)
|
|
- `String` → text
|
|
- `Int` → integer
|
|
- `Float` → double precision
|
|
- `Boolean` → boolean
|
|
|
|
### Custom Scalars
|
|
- `DateTime` → timestamp
|
|
- `JSON` → jsonb
|
|
- `Date` → date
|
|
- `Time` → time
|
|
- `Decimal` → numeric
|
|
|
|
Additional custom scalars can be mapped via metadata.
|
|
|
|
### Relationships
|
|
|
|
Relationships are inferred from field types:
|
|
|
|
```graphql
|
|
type Post {
|
|
id: ID!
|
|
title: String!
|
|
author: User! # Many-to-one (creates authorId FK column, NOT NULL)
|
|
reviewer: User # Many-to-one nullable (creates reviewerId FK column, NULL)
|
|
tags: [Tag!]! # One-to-many or many-to-many (depending on reverse)
|
|
}
|
|
|
|
type User {
|
|
id: ID!
|
|
posts: [Post!]! # Reverse of Post.author (no FK created)
|
|
}
|
|
|
|
type Tag {
|
|
id: ID!
|
|
posts: [Post!]! # Many-to-many with Post (creates PostTag junction table)
|
|
}
|
|
```
|
|
|
|
**Relationship Detection Rules:**
|
|
- Single type reference (`user: User`) → Creates FK column (e.g., `userId`)
|
|
- Array type reference (`posts: [Post!]!`) → One-to-many reverse (no FK on this table)
|
|
- Bidirectional arrays → Many-to-many (creates junction table)
|
|
|
|
### Enums
|
|
|
|
```graphql
|
|
enum Role {
|
|
ADMIN
|
|
USER
|
|
GUEST
|
|
}
|
|
|
|
type User {
|
|
role: Role!
|
|
}
|
|
```
|
|
|
|
Enums are preserved in the schema and can be used as column types.
|
|
|
|
## Usage
|
|
|
|
### Basic Usage
|
|
|
|
```go
|
|
import (
|
|
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/readers/graphql"
|
|
)
|
|
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: "schema.graphql",
|
|
}
|
|
|
|
reader := graphql.NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
```
|
|
|
|
### With UUID ID Type
|
|
|
|
```go
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: "schema.graphql",
|
|
Metadata: map[string]interface{}{
|
|
"idType": "uuid", // Map ID scalar to uuid instead of bigint
|
|
},
|
|
}
|
|
|
|
reader := graphql.NewReader(opts)
|
|
db, err := reader.ReadDatabase()
|
|
```
|
|
|
|
### With Per-Type ID Mapping
|
|
|
|
```go
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: "schema.graphql",
|
|
Metadata: map[string]interface{}{
|
|
"typeIdMappings": map[string]string{
|
|
"User": "uuid", // User.id → uuid
|
|
"Post": "bigint", // Post.id → bigint
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
### With Custom Scalar Mappings
|
|
|
|
```go
|
|
opts := &readers.ReaderOptions{
|
|
FilePath: "schema.graphql",
|
|
Metadata: map[string]interface{}{
|
|
"customScalarMappings": map[string]string{
|
|
"Upload": "bytea",
|
|
"Decimal": "numeric(10,2)",
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
## CLI Usage
|
|
|
|
```bash
|
|
# Convert GraphQL to JSON
|
|
relspec convert --from graphql --from-path schema.graphql \
|
|
--to json --to-path schema.json
|
|
|
|
# Convert GraphQL to GORM models
|
|
relspec convert --from graphql --from-path schema.graphql \
|
|
--to gorm --to-path models/ --package models
|
|
|
|
# Convert GraphQL to PostgreSQL SQL
|
|
relspec convert --from graphql --from-path schema.graphql \
|
|
--to pgsql --to-path schema.sql
|
|
```
|
|
|
|
## Metadata Options
|
|
|
|
| Option | Type | Description | Default |
|
|
|--------|------|-------------|---------|
|
|
| `idType` | string | Global ID type mapping ("bigint" or "uuid") | "bigint" |
|
|
| `typeIdMappings` | map[string]string | Per-type ID mappings | {} |
|
|
| `customScalarMappings` | map[string]string | Custom scalar to SQL type mappings | {} |
|
|
| `schemaName` | string | Schema name for all tables | "public" |
|
|
|
|
## Limitations
|
|
|
|
- Only supports GraphQL SDL (Schema Definition Language), not queries or mutations
|
|
- Directives are ignored (except for future extensibility)
|
|
- Interfaces and Unions are not supported
|
|
- GraphQL's concept of "schema" is different from database schemas; all types go into a single database schema (default: "public")
|
|
|
|
## Example
|
|
|
|
**Input** (`schema.graphql`):
|
|
```graphql
|
|
scalar DateTime
|
|
|
|
enum Role {
|
|
ADMIN
|
|
USER
|
|
}
|
|
|
|
type User {
|
|
id: ID!
|
|
email: String!
|
|
role: Role!
|
|
createdAt: DateTime!
|
|
posts: [Post!]!
|
|
}
|
|
|
|
type Post {
|
|
id: ID!
|
|
title: String!
|
|
content: String
|
|
published: Boolean!
|
|
author: User!
|
|
}
|
|
```
|
|
|
|
**Result**: Database with:
|
|
- 2 tables: `User` and `Post`
|
|
- `Post` table has `authorId` foreign key to `User.id`
|
|
- `Role` enum with values: ADMIN, USER
|
|
- Custom scalar `DateTime` mapped to `timestamp`
|