first commit
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# schema-builder
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Declares `SchemaBuilder`, the fluent builder for GraphQL schema definitions. Consumers describe types, queries, mutations, and subscriptions via chained method calls; `build()` returns a fully resolved `SchemaDefinition` consumed by `defineGraphQLPlugin` (or by any future executor).
|
||||
|
||||
`SchemaBuilder` is the canonical authoring surface — callers should not construct `SchemaDefinition` literals by hand. The builder enforces sensible defaults (`required: false`, `isList: false`), maintains insertion order for downstream determinism, and round-trips description/interfaces metadata correctly.
|
||||
|
||||
## Features
|
||||
|
||||
- `addType` / `addInputType` / `addEnumType` / `addInterfaceType` / `addUnionType` — every GraphQL type kind except `scalar` (consumers register scalars as plain strings on field types).
|
||||
- `addQuery` / `addMutation` / `addSubscription` — root-field declarations with optional `required`, `isList`, `description`, `args`.
|
||||
- `hasType` / `getType` — lookup helpers.
|
||||
- `typeCount` / `queryCount` / `mutationCount` — read-only counters.
|
||||
- `build()` — snapshot into an independent `SchemaDefinition`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { SchemaBuilder } from "@suaveplan/api-plugin-graphql";
|
||||
|
||||
const schema = new SchemaBuilder()
|
||||
.addType("User", (t) =>
|
||||
t.field("id", "ID", { required: true })
|
||||
.field("name", "String", { required: true })
|
||||
)
|
||||
.addQuery("user", "User", {
|
||||
args: [{ name: "id", type: "ID", required: true, isList: false }],
|
||||
})
|
||||
.build();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { SchemaBuilder } from "@suaveplan/api-plugin-graphql";
|
||||
|
||||
const schema = new SchemaBuilder()
|
||||
.addEnumType("Role", ["ADMIN", "USER", "GUEST"], "Authorization role")
|
||||
.addInterfaceType("Node", (t) => t.field("id", "ID", { required: true }))
|
||||
.addType("Account", (t) =>
|
||||
t.field("id", "ID", { required: true }).field("role", "Role", { required: true }),
|
||||
{ description: "User account", interfaces: ["Node"] }
|
||||
)
|
||||
.addUnionType("SearchHit", ["Account", "Post"], "Polymorphic search result")
|
||||
.addInputType("CreateAccountInput", (t) =>
|
||||
t.field("name", "String", { required: true })
|
||||
.field("role", "Role", { required: true })
|
||||
)
|
||||
.addMutation("createAccount", "Account", {
|
||||
required: true,
|
||||
args: [{ name: "input", type: "CreateAccountInput", required: true, isList: false }],
|
||||
})
|
||||
.addSubscription("onAccountCreated", "Account", { required: true })
|
||||
.build();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Method | Returns | Notes |
|
||||
|---|---|---|
|
||||
| `addType(name, builderFn, options?)` | `this` | `options.description`, `options.interfaces`. |
|
||||
| `addInputType(name, builderFn, description?)` | `this` | |
|
||||
| `addEnumType(name, values, description?)` | `this` | `values` is copied. |
|
||||
| `addInterfaceType(name, builderFn, description?)` | `this` | |
|
||||
| `addUnionType(name, memberTypes, description?)` | `this` | `memberTypes` stored on `enumValues`. |
|
||||
| `addQuery(name, returnType, options?)` | `this` | `required`/`isList`/`description`/`args`. |
|
||||
| `addMutation(name, returnType, options?)` | `this` | Same options as queries. |
|
||||
| `addSubscription(name, returnType, options?)` | `this` | Same options as queries. |
|
||||
| `hasType(name)` | `boolean` | |
|
||||
| `getType(name)` | `TypeDefinition \| undefined` | |
|
||||
| `typeCount` / `queryCount` / `mutationCount` | `number` | |
|
||||
| `build()` | `SchemaDefinition` | Returns an independent snapshot. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`addType` / `addInterfaceType` / `addInputType` re-construct the `TypeDefinition` from the `TypeBuilder` output to merge `description` and `interfaces` cleanly under `exactOptionalPropertyTypes` — TypeScript rejects assigning `undefined` to an optional property when that strict flag is on, so each merge spreads conditionally.
|
||||
|
||||
Union types reuse `enumValues` to carry their member-type name list. This matches upstream and keeps `TypeDefinition` flat — adding a separate `memberTypes` field would have introduced a new public type with no consumer benefit. Downstream code distinguishes via `kind === "union"`.
|
||||
|
||||
`build()` is the only method that exposes structure to callers; until then, the builder owns its internal `Map` / arrays. Each `build()` call returns fresh arrays so callers may mutate them safely or invoke `build()` multiple times during incremental construction.
|
||||
Reference in New Issue
Block a user