|
| 1 | +***WARNING***: This package is in beta. It's API is still evolving and is subject to breaking changes. |
| 2 | + |
1 | 3 | # GraphQL Generator for Swift |
2 | 4 |
|
3 | 5 | A Swift package plugin that generates server-side GraphQL API code from GraphQL schema files, inspired by [GraphQL Tools' makeExecutableSchema](https://the-guild.dev/graphql/tools/docs/generate-schema). |
@@ -57,7 +59,7 @@ type Query { |
57 | 59 |
|
58 | 60 | When you build, the plugin will automatically generate Swift code: |
59 | 61 | - `Types.swift` - Swift protocols for your GraphQL types |
60 | | -- `Schema.swift` - Schema |
| 62 | +- `Schema.swift` - Defines `buildGraphQLSchema` function that builds an executable schema |
61 | 63 |
|
62 | 64 | ### 3. Create required types |
63 | 65 |
|
@@ -118,11 +120,58 @@ let result = try await graphql(schema: schema, request: "{ users { name email } |
118 | 120 | print(result) |
119 | 121 | ``` |
120 | 122 |
|
121 | | -## Detailed Usage |
| 123 | +## Design |
| 124 | + |
| 125 | +### Root Types |
| 126 | +Root types (Query, Mutation, and Subscription) are modeled as Swift protocols with static method requirements for each field. The user must implement these types and provide them to the `buildGraphQLSchema` function. |
| 127 | + |
| 128 | +### Object Types |
| 129 | +Object types are modeled as Swift protocols with instance method requirements for each field. This is to enable maximum implementation flexibility. Internally, GraphQL passes result objects directly through to subsequent resolvers. By only specifying the interface, we allow the backing types to be incredibly dynamic - they can be simple codable structs or complex stateful actors, reference or values types, or any other type configuration. |
| 130 | + |
| 131 | +Furthermore, by only referencing protocols, we can have multiple Swift types back a particular GraphQL type, and can easily mock portions of the schema. As an example, consider the following schema snippet: |
| 132 | +```graphql |
| 133 | +type A { |
| 134 | + foo: String |
| 135 | +} |
| 136 | +``` |
| 137 | + |
| 138 | +This would result in the following protocol: |
| 139 | +```swift |
| 140 | +public protocol AProtocol: Sendable { |
| 141 | + func foo(context: Context, info: GraphQLResolveInfo) async throws -> String |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +You could define two conforming types. To use `ATest` in tests, simply return it from the relevant resolvers. |
| 146 | +```swift |
| 147 | +struct A: AProtocol { |
| 148 | + let foo: String |
| 149 | + func foo(context: Context, info: GraphQLResolveInfo) async throws -> String { |
| 150 | + return foo |
| 151 | + } |
| 152 | +} |
| 153 | +struct ATest: AProtocol { |
| 154 | + func foo(context: Context, info: GraphQLResolveInfo) async throws -> String { |
| 155 | + return "test" |
| 156 | + } |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | + |
| 161 | +### Interface Types |
| 162 | +Interfaces are modeled as a protocol with required methods for each relevant field. Implementing objects and interfaces are marked as requiring conformance to the interface protocol. |
| 163 | + |
| 164 | +### Union Types |
| 165 | +Union types are modeled as a marker protocol, with no required properties or functions. Related objects are marked as requiring conformance to the union protocol. |
| 166 | + |
| 167 | +### Input Object Types |
| 168 | +Input object types are modeled as a deterministic Codable struct with the declared fields. If more complex objects must be created from the codable struct, this can be done in the resolver itself, since input objects only relevant for their associated resolver (they are not passed to downstream resolvers). |
122 | 169 |
|
123 | | -### Scalars |
| 170 | +### Enum Types |
| 171 | +Enum types are modeled as a deterministic String enum with values matching the declared fields and associated representations. If you need different values or more complex implementations, simply convert to/from a different representation inside your resolvers. |
124 | 172 |
|
125 | | -Scalar types must be provided for each GraphQL scalar. Since GraphQL uses a different serialization system than Swift, you must conform the type to Swift's `Codable` and GraphQL's `Scalar`, and have them agree on a representation. |
| 173 | +### Scalar Types |
| 174 | +Scalar types are not modeled by the generator. They are simply referenced using the Scalar's name, and you are expected to implement the required type. Since GraphQL uses a different serialization system than Swift, you must conform the type to Swift's `Codable` and GraphQL's `Scalar`, and have them agree on a representation. |
126 | 175 |
|
127 | 176 | Below is an example that represents a scalar struct as a raw String: |
128 | 177 |
|
|
0 commit comments