Graphiti

Graphiti 是一个 Swift 库,用于快速、安全且轻松地构建 GraphQL schema。

Swift License GitHub Actions Maintainability Coverage

需要帮助吗?请在社区中查找资源。

开始使用

关于 GraphQL 的总体概述,请参阅 README,它是 GraphQL 规范的一部分。该概述描述了一组简单的 GraphQL 示例,这些示例以测试的形式存在于此仓库中。开始使用此仓库的一个好方法是并行阅读该 README 文档和相应的测试。

使用 Graphiti

将 Graphiti 添加到您的 Package.swift 文件中

import PackageDescription

let package = Package(
    dependencies: [
        .package(url: "https://github.com/GraphQLSwift/Graphiti.git", .upToNextMinor(from: "0.20.1")),
    ]
)

Graphiti 提供两个重要的功能:构建类型 schema,以及针对该类型 schema 提供查询服务。

定义实体

首先,我们声明常规的 Swift 实体。

struct Message : Codable {
    let content: String
}

⭐️ Graphiti 背后的主要设计决策之一是污染您的实体声明。 这样您就可以轻松地将您的实体引入任何其他解决方案。

定义上下文

第二步是创建应用程序的上下文。上下文将传递给您的所有字段解析器函数。 这允许您将依赖注入应用于您的 API。 您可以在此处放置与数据库或其他服务通信的代码。

struct Context {
    func message() -> Message {
        Message(content: "Hello, world!")
    }
}

⭐️ 再次注意,此步骤不需要 Graphiti。 这纯粹是业务逻辑。

定义 GraphQL API 解析器

现在我们有了实体和上下文,我们可以创建 GraphQL API 解析器。

import Graphiti

struct Resolver {
    func message(context: Context, arguments: NoArguments) -> Message {
        context.message()
    }
}

定义 GraphQL API schema

现在我们终于可以使用其 schema 定义 GraphQL API 了。

struct MessageAPI : API {
    let resolver: Resolver
    let schema: Schema<Resolver, Context>
}
        
let api = MessageAPI(
    resolver: Resolver()
    schema: try! Schema<Resolver, Context> {
        Type(Message.self) {
            Field("content", at: \.content)
        }

        Query {
            Field("message", at: Resolver.message)
        }
    }
)

Schema 也可以使用 SchemaBuilder 以模块化方式创建

SchemaBuilder API
let builder = SchemaBuilder(Resolver.self, Context.self)
builder.add(
    Type(Message.self) {
        Field("content", at: \.content)
    }
)
builder.query.add(
    Field("message", at: Resolver.message)
)
let schema = try builder.build()

let api = MessageAPI(
    resolver: Resolver()
    schema: schema
)
PartialSchema 实现
final class ChatSchema: PartialSchema<Resolver, Context> {
    @TypeDefinitions
    public override var types: Types {
        Type(Message.self) {
            Field("content", at: \.content)
        }        
    }

    @FieldDefinitions
    public override var query: Fields {
        Field("message", at: Resolver.message)
    }
}
let schema = try SchemaBuilder(Resolver.self, Context.self)
    .use(partials: [ChatSchema(), ...])
    .build()

let api = MessageAPI(
    resolver: Resolver()
    schema: schema
)
PartialSchema 实例
let chatSchema = PartialSchema<Resolver, Context>(
    types:  {
        Type(Message.self) {
            Field("content", at: \.content)
        }        
    },
    query: {
        Field("message", at: Resolver.message)
    }
)
let schema = try SchemaBuilder(Resolver.self, Context.self)
    .use(partials: [chatSchema, ...])
    .build()

let api = MessageAPI(
    resolver: Resolver()
    schema: schema
)

⭐️ 请注意,API 允许依赖注入。 例如,在测试时,您可以传递 resolvercontext 的 mock 对象。

查询

要查询 schema,我们需要传入一个 NIO EventLoopGroup,以便将 execute 函数与查询本身一起提供。

import NIO

let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
defer {
    try? group.syncShutdownGracefully()
}

let result = try await api.execute(
    request: "{ message { content } }",
    context: Context(),
    on: group
)
print(result)

输出将是

{"data":{"message":{"content":"Hello, world!"}}}

API.execute 返回一个 GraphQLResult,它采用了 Encodable 协议。 您可以将其与 JSONEncoder 一起使用,以使用 JSON 将响应发送回客户端。

异步解析器

解析器函数也可以是 async

struct Resolver {
    func message(context: Context, arguments: NoArguments) async -> Message {
        await someAsyncMethodToGetMessage()
    }
}

NIO 解析器

解析器函数也支持 NIO 风格的并发。 为此,只需在解析器函数中添加一个类型为 EventLoopGroup 的参数,并将返回类型更改为 EventLoopFuture<YouReturnType>。 不要忘记导入 NIO。

import NIO

struct Resolver {
    func message(context: Context, arguments: NoArguments, group: EventLoopGroup) -> EventLoopFuture<Message> {
        group.next().makeSucceededFuture(context.message())
    }
}

订阅

此库支持 GraphQL 订阅,并通过 Swift Concurrency AsyncThrowingStream 类型支持它们。 有关详细信息,请参阅使用指南

如果您无法使用 Swift Concurrency,则必须创建 EventStream 类的具体子类来实现事件流功能。 如果您不想自己创建子类,则可以使用 GraphQLRxSwift 仓库来开箱即用地集成 RxSwift observables。 或者,您可以将该仓库用作参考,以连接不同的流库,例如 ReactiveSwiftOpenCombine,或者您自己创建的库。

更多示例

有关渐进式演练,请参阅使用指南星球大战 API 提供了一个相当完整的示例。

支持

此软件包支持与 Swift NIO 对齐的 Swift 版本

贡献

此仓库使用 SwiftFormat,并包含 lint 检查以强制执行这些格式标准。 要格式化您的代码,请安装 swiftformat 并运行

swiftformat .

许可证

本项目根据 MIT 许可证发布。 有关详细信息,请参阅 LICENSE