FSharp.Data.GraphQL.Client 2.2.1

dotnet add package FSharp.Data.GraphQL.Client --version 2.2.1                
NuGet\Install-Package FSharp.Data.GraphQL.Client -Version 2.2.1                
此命令旨在在 Visual Studio 的包管理器控制台中使用,因为它使用 NuGet 模块的 Install-Package 版本。
<PackageReference Include="FSharp.Data.GraphQL.Client" Version="2.2.1" />                
对于支持 PackageReference 的项目,将此 XML 节点复制到项目文件中以引用包。
paket add FSharp.Data.GraphQL.Client --version 2.2.1                
#r "nuget: FSharp.Data.GraphQL.Client, 2.2.1"                
#r 指令可用于 F# Interactive 和 Polyglot Notebooks。将此内容复制到交互工具或脚本的源代码中以引用包。
// Install FSharp.Data.GraphQL.Client as a Cake Addin
#addin nuget:?package=FSharp.Data.GraphQL.Client&version=2.2.1

// Install FSharp.Data.GraphQL.Client as a Cake Tool
#tool nuget:?package=FSharp.Data.GraphQL.Client&version=2.2.1                

FSharp.Data.GraphQL

F# 实现 Facebook GraphQL 查询语言规范

Publish to GitHub Publish to NuGet

Join the chat at https://gitter.im/FSharp-Data-GraphQL/community

安装项目模板

键入以下命令来安装创建 Web 应用程序的模板

从 GitHub:dotnet new -i "FSharp.Data.GraphQL::2.0.0-ci-*" --nuget-source "https://nuget.pkg.github.com/fsprojects/index.json"

从 NuGet:dotnet new -i "FSharp.Data.GraphQL::2.0.0"

快速入门

open FSharp.Data.GraphQL
open FSharp.Data.GraphQL.Types

type Person =
    { FirstName: string
      LastName: string }

// Define GraphQL type 
let PersonType = Define.Object(
    name = "Person",
    fields = [
        // Property resolver will be auto-generated
        Define.AutoField("firstName", StringType)
        // Asynchronous explicit member resolver
        Define.AsyncField("lastName", StringType, resolve = fun context person -> async { return person.LastName })
    ])

// Include person as a root query of a schema
let schema = Schema(query = PersonType)
// Create an Exector for the schema
let executor = Executor(schema)

// Retrieve person data
let johnSnow = { FirstName = "John"; LastName = "Snow" }
let reply = executor.AsyncExecute(Parser.parse "{ firstName, lastName }", johnSnow) |> Async.RunSynchronously
// #> { data: { "firstName", "John", "lastName", "Snow" } } 

它是类型安全的。无效字段或无效返回类型等将在编译时进行检查。

ASP.NET / Giraffe / WebSocket (用于 GraphQL 订阅) 的使用

AspNetCore/README.md

演示

GraphiQL 客户端

转到 GraphiQL 示例目录。为了运行它,以Debug设置构建并运行 Star Wars API 示例项目 - 这将创建一个与 GraphQL 规范兼容的 Giraffe 服务器,在 8086 端口运行。然后您需要运行 node.js graphiql 前端。要这样做,运行 npm i 以获取所有依赖项,然后运行 npm run serve | npm run dev - 这将在 http://localhost:8090/ 上运行 webpack 服务器。访问此链接,GraphiQL 编辑器应该会出现。您可以尝试以下查询

{
  hero(id:"1000") {
    id,
    name,
    appearsIn,
    homePlanet,
    friends {
      ... on Human {
        name
      }
      ... on Droid {
        name
      }
    }
  }
}

Relay.js 快速入门套件

第二个示例是流行的 Relay Starter Kit 的 F# 版本 - 这是一个使用 React.js + Relay 及与其兼容的服务器 API 的示例应用程序。[查看](https://github.com/bazingatechnologies/FSharp.Data.GraphQL/tree/dev/samples/relay-starter-kit)

要运行它,请使用调试设置构建 FSharp.Data.GraphQLFSharp.Data.GraphQL.Relay 项目。然后在 FSI 中运行 server.fsx 脚本以启动服务器 - 这将在端口 8083 上启动一个兼容 Relay 的 F# 服务器。然后通过运行 npm i 获取所有依赖项并在终端中运行它 (npm run serve | npm run dev) 来构建 node.js 前端 - 这将启动使用 Relay 管理应用程序状态的 React 应用程序的 webpack 服务器。您可以在 http://localhost:8083/ 上访问它。

要更新客户端模式,请访问 http://localhost:8083/ 并将响应(即当前 F# 服务器中的查询结果)复制粘贴到 data/schema.json 中。

流功能

现在 stream 指令具有额外的功能,例如基于间隔和/或批大小进行批处理(缓冲)。为了使其正常工作,必须在 SchemaConfig.Directives 列表中放置一个自定义流指令,该指令包含两个可选参数,分别称为 intervalpreferredBatchSize

let customStreamDirective =
    let args = [|
        Define.Input(
            "interval",
            Nullable IntType,
            defaultValue = Some 2000,
            description = "An optional argument used to buffer stream results. ")
        Define.Input(
            "preferredBatchSize",
            Nullable IntType,
            defaultValue = None,
            description = "An optional argument used to buffer stream results. ") |]
    { StreamDirective with Args = args }
let schemaConfig =
    { SchemaConfig.Default with
        Directives = [
            IncludeDirective
            SkipDirective
            DeferDirective
            customStreamDirective
            LiveDirective ] }

此样板代码可以轻松地用一个内置实现进行减少

let streamOptions =
    { Interval = Some 2000; PreferredBatchSize = None }
let schemaConfig =
    SchemaConfig.DefaultWithBufferedStream(streamOptions)

实时查询

现在服务器组件支持 live 指令。为了支持实时查询,需要将模式的每种类型的每个字段配置为实时字段。这可以通过使用 ILiveFieldSubscriptionILiveQuerySubscriptionProvider 来完成,这两个都可以在 SchemaConfig 中进行配置

type ILiveFieldSubscription =
    interface
        abstract member Identity : obj -> obj
        abstract member TypeName : string
        abstract member FieldName : string
    end

and ILiveFieldSubscription<'Object, 'Identity> =
    interface
        inherit ILiveFieldSubscription
        abstract member Identity : 'Object -> 'Identity
    end

and ILiveFieldSubscriptionProvider =
    interface
        abstract member HasSubscribers : string -> string -> bool
        abstract member IsRegistered : string -> string -> bool
        abstract member AsyncRegister : ILiveFieldSubscription -> Async<unit>
        abstract member TryFind : string -> string -> ILiveFieldSubscription option
        abstract member Add : obj -> string -> string -> IObservable<obj>
        abstract member AsyncPublish<'T> : string -> string -> 'T -> Async<unit>
    end

要将字段设置为实时字段,请调用 Register 扩展方法。每个订阅都需要知道对象身份,因此必须在 ILiveFieldSubscription 的 Identity 功能上进行配置。还需要传递 ObjectDef 内部的类型名称和字段名称

let schemaConfig = SchemaConfig.Default
let schema = Schema(root, config = schemaConfig)
let subscription =
    { Identity = fun (x : Human) -> x.Id
      TypeName = "Hero"
      FieldName = "name" }

schemaConfig.LiveFieldSubscriptionProvider.Register subscription

有了这个,英雄的字段名称现在可以实时更新了,当使用 live 指令查询时,它会被更新到客户端。要向订阅者推送更新,只需调用 Publish 方法,传递类型名称、字段名称和更新的对象

let updatedHero = { hero with Name = "Han Solo - Test" }
schemaConfig.LiveFieldSubscriptionProvider.Publish "Hero" "name" updatedHero

客户端提供者

我们的客户端库现在有一个完全重新设计过的类型提供者。要开始使用它,您首先需要访问您要连接的服务器的查询结果。可以通过提供者以以下两种方式之一实现:

  1. 提供所需 GraphQL 服务器(不需要任何自定义 HTTP 标头)的 URL。提供者将访问服务器,发送一个查询,并使用该模式提供用于执行查询的类型。
type MyProvider = GraphQLProvider<"http://some.graphqlserver.development.org">
  1. 为提供者提供 introspection json 文件。但请注意,introspection json 应该包含所有必需的字段。您可以通过在对所需服务器运行我们的标准查询并保存该文件到与使用提供者项目的文件夹相同的路径上[获取正确字段]。
type MyProvider = GraphQLProvider<"swapi_schema.json">

从现在起,您可以开始运行查询和突变

let operation = 
    MyProvider.Operation<"""query q {
      hero (id: "1001") {
        name
        appearsIn
        homePlanet
        friends {
          ... on Human {
            name
            homePlanet
          }
          ... on Droid {
            name
            primaryFunction
          }
        }
      }
    }""">()

// This is a instance of GraphQLProviderRuntimeContext.
// You can use it to provider a runtime URL to access your server,
// and optionally additional HTTP headers (auth headers, for example).
// If you use a local introspection file to parse the schema,
// The runtime context is mandatory.
let runtimeContext =
  { ServerUrl = "http://some.graphqlserver.production.org"
    CustomHttpHeaders = None }

let result = operation.Run(runtimeContext)

// Query result objects have pretty-printing and structural equality.
printfn "Data: %A\n" result.Data
printfn "Errors: %A\n" result.Errors
printfn "Custom data: %A\n" result.CustomData

// Response from the server:
// Data: Some
//   {Hero = Some
//   {Name = Some "Darth Vader";
// AppearsIn = [|NewHope; Empire; Jedi|];
// HomePlanet = Some "Tatooine";
// Friends = [|Some {Name = Some "Wilhuff Tarkin";
// HomePlanet = <null>;}|];};}

// Errors: <null>

// Custom data: map [("documentId", 1221427401)]

有关如何使用客户端提供者的更多信息,请参阅示例文件夹。

中间件

您可以在 Executor<'Root> 对象之上创建和使用中间件。

使用 Executor 的查询执行过程包括三个阶段

  • 架构编译阶段:当实例化Executor<'Root>类时会发生此阶段。在此阶段,使用类型的Schema映射来构建一个字段执行映射,它包含所有字段定义及其字段解析函数。此映射在规划阶段和执行阶段后面用于检索模式查询字段的值。

  • 操作规划阶段:在此阶段,执行一个没有执行计划的查询之前。此阶段负责分析查询生成的AST文档,并构建执行计划以执行此操作。

  • 操作执行阶段:这是执行查询的阶段。它需要一个执行计划,所以通常在操作规划阶段之后发生。

所有阶段都将执行阶段所需的数据包装在一个上下文对象中。它们通过函数在内部分表示。

let internal compileSchema (ctx : SchemaCompileContext) : unit =
  // ...

let internal planOperation (ctx: PlanningContext) : ExecutionPlan =
  // ...

let internal executeOperation (ctx : ExecutionContext) : AsyncVal<GQLResponse> =
  // ...

因此,在编译架构阶段,在SchemaCompileContext对象内修改架构并生成执行映射。在操作规划阶段,使用PlanningContext对象的值生成执行计划,最后将该计划与其他值一起传递到ExecutionContext对象中,然后在操作执行阶段使用它们执行查询并生成GQLResponse

考虑到这一点,可以使用中间件来拦截每个阶段并按需自定义它们。每个中间件都必须作为一个特定签名的函数实现,并包裹在一个IExecutorMiddleware接口中

type SchemaCompileMiddleware =
    SchemaCompileContext -> (SchemaCompileContext -> unit) -> unit

type OperationPlanningMiddleware =
    PlanningContext -> (PlanningContext -> ExecutionPlan) -> ExecutionPlan

type OperationExecutionMiddleware =
    ExecutionContext -> (ExecutionContext -> AsyncVal<GQLResponse>) -> AsyncVal<GQLResponse>

type IExecutorMiddleware =
    abstract CompileSchema : SchemaCompileMiddleware option
    abstract PlanOperation : OperationPlanningMiddleware option
    abstract ExecuteOperationAsync : OperationExecutionMiddleware option

可选的,为了易于实现,可以使用从其中派生出的具体类,它仅在构造函数中接收可选的子中间件函数

type ExecutorMiddleware(?compile, ?plan, ?execute) =
    interface IExecutorMiddleware with
        member _.CompileSchema = compile
        member _.PlanOperation = plan
        member _.ExecuteOperationAsync = execute

每个中间件函数 acting like an intercept function,有两个参数:阶段的上下文,下一个中间件的功能(或实际阶段本身,即最后运行),以及返回值。这些函数可以作为参数传递给Executor<'Root>对象的构造函数

let middleware = [ ExecutorMiddleware(compileFn, planningFn, executionFn) ]
let executor = Executor(schema, middleware)

一个实际的中间件示例可以是测量规划查询所需时间的中间件。结果作为规划上下文的Metadata部分返回。《Metadata》对象是一个Map<string, obj>实现,它像一个携带要传递到每个阶段的信息的袋子,直到它作为GQLResponse对象的一部分返回。您可以使用它通过中间件传递自定义信息。

let planningMiddleware (ctx : PlanningContext) (next : PlanningContext -> ExecutionPlan) =
    let watch = Stopwatch()
    watch.Start()
    let result = next ctx
    watch.Stop()
    let metadata = result.Metadata.Add("planningTime", watch.ElapsedMilliseconds)
    { result with Metadata = metadata }

内置中间件

FSharp.Data.GraphQL.Server.Middleware软件包中有一些内置的中间件

QueryWeightMiddleware

此中间件可用于对架构的字段放置权重。这些加权字段现在可用于保护服务器免受可能在DDoS攻击中使用的复杂查询。

在定义字段时,我们使用扩展方法WithQueryWeight来放置一个权重

let resolveFn (h : Human) =
  h.Friends |> List.map getCharacter |> List.toSeq

let field =
  Define.Field("friends", ListOf (Nullable CharacterType),
    resolve = resolveFn).WithQueryWeight(0.5)

然后我们为Executor定义阈值中间件。如果我们执行一个递归方式请求"朋友的朋友"的查询,executor将在查询超过2.0的权重阈值之前只接受4次嵌套。

let middleware = [ Define.QueryWeightMiddleware(2.0) ]
ObjectListFilterMiddleware

此中间件可用于自动生成对象内部列表字段的过滤器。此过滤器可以作为查询的字段参数传递,并在解析函数的字段解析函数的ResolveFieldContext参数中恢复。

例如,我们可以创建一个用于过滤Human对象列表字段的中间件,这些字段的类型为Character option

let middleware = [ Define.ObjectListFilterMiddleware<Human, Character option>() ]

过滤器参数是一个对象,它通过字段上的filter参数中的一个JSON定义进行映射。一个简单的示例是过滤出以字母A开头的英雄的朋友

query TestQuery {
    hero(id:"1000") {
        id
        name
        appearsIn
        homePlanet
        friends (filter : { name_starts_with: "A" }) {
            id
            name
        }
    }
}

此过滤器由中间件映射到ObjectListFilter定义中

type FieldFilter<'Val> =
    { FieldName : string
      Value : 'Val }

type ObjectListFilter =
    | And of ObjectListFilter * ObjectListFilter
    | Or of ObjectListFilter * ObjectListFilter
    | Not of ObjectListFilter
    | Equals of FieldFilter<System.IComparable>
    | GreaterThan of FieldFilter<System.IComparable>
    | LessThan of FieldFilter<System.IComparable>
    | StartsWith of FieldFilter<string>
    | EndsWith of FieldFilter<string>
    | Contains of FieldFilter<string>
    | FilterField of FieldFilter<ObjectListFilter>

查询中过滤器恢复的值可以在字段解析函数的ResolveFieldContext中使用。为了方便访问,你可以使用扩展方法Filter,它返回一个ObjectListFilter option(如果没有实现中介件泛型定义的列表,或者没有提供过滤器输入,则该选项没有值)。

Define.Field("friends", ListOf (Nullable CharacterType),
    resolve = fun ctx (d : Droid) -> 
        ctx.Filter |> printfn "Droid friends filter: %A"
        d.Friends |> List.map getCharacter |> List.toSeq)

从字段解析上下文中检索此过滤器后,可以使用客户端代码自定义针对数据库运行的查询,例如,并扩展您的GraphQL API功能。

LiveQueryMiddleware

此中间件可用于快速允许您的模式字段能够使用live指令进行查询,前提是所有字段都具有可由函数IdentityNameResolver发现的标识属性名称。

/// A function that resolves an identity name for a schema object, based on a object definition of it.
type IdentityNameResolver = ObjectDef -> string

例如,如果我们的所有模式对象都具有名为Id的标识字段,我们可以像这样使用我们的中间件

let schema = Schema(query = queryType)

let middleware = [ Define.LiveQueryMiddleware(fun _ -> "Id") ]

let executor = Executor(schema, middleware)

IdentityNameResolver是可选的。如果没有提供解析函数,则使用此默认实现。此外,还必须通过如上所述的PublishILiveFieldSubscriptionProvider进行订阅者的通知。

使用扩展来构建自己的中间件

您可以使用FSharp.Data.GraphQL.Shared包提供的扩展方法来帮助构建自己的中间件。在制作中间件时,通常需要修改架构定义以向由用户代码定义的架构添加功能。ObjectListFilter中间件是一个示例,其中所有实现特定类型列表的字段都需要修改,通过接受一个名为filter的参数。

因为字段定义默认是不可变的,有时生成具有改进功能的副本会很费劲。这就是扩展方法可以帮助的地方:例如,如果您需要在模式编译阶段内部现有的字段添加一个参数,您可以使用FieldDef<'Val>接口的WithArgs方法。

let field : FieldDef<'Val> = // Search for field inside ISchema
let arg : Define.Input("id", StringType)
let fieldWithArg = field.WithArgs([ arg ])

要查看用于增强定义的完整扩展列表,您可以查看包含在FSharp.Data.GraphQL.Shared包中的TypeSystemExtensions模块。

产品 兼容和附加计算的面向目标框架版本。
.NET net5.0 已计算。 net5.0-windows 已计算。 net6.0 已计算。 net6.0-android 已计算。 net6.0-ios 已计算。 net6.0-maccatalyst 已计算。 net6.0-macos 已计算。 net6.0-tvos 已计算。 net6.0-windows 已计算。 net7.0 已计算。 net7.0-android 已计算。 net7.0-ios 已计算。 net7.0-maccatalyst 已计算。 net7.0-macos 已计算。 net7.0-tvos 已计算。 net7.0-windows 已计算。 net8.0 已计算。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。
.NET Core netcoreapp2.0 已计算。 netcoreapp2.1 已计算。 netcoreapp2.2 已计算。 netcoreapp3.0 已计算。 netcoreapp3.1 已计算。
.NET Standard netstandard2.0 兼容。 netstandard2.1 已计算。
.NET Framework net461 已计算。 net462 已计算。 net463 已计算。 net47 已计算。 net471 已计算。 net472 已计算。 net48 已计算。 net481 已计算。
MonoAndroid monoandroid 已计算。
MonoMac monomac 已计算。
MonoTouch monotouch 已计算。
Tizen tizen40 已计算。 tizen60 已计算。
Xamarin.iOS xamarinios 已计算。
Xamarin.Mac xamarinmac 已计算。
Xamarin.TVOS xamarintvos 已计算。
Xamarin.WatchOS xamarinwatchos 已计算。
兼容目标框架
包含的目标框架(在包中)
更多关于目标框架.NET Standard的信息。

NuGet 包

此包未由任何 NuGet 包使用。

GitHub 仓库

此包未由任何流行的 GitHub 仓库使用。

版本 下载 最后更新
2.2.1 1,609 6/16/2024
2.2.0 275 5/8/2024
2.1.0 100 4/21/2024
2.0.0 110 3/24/2024
2.0.0-beta1 66 2/16/2024
1.0.7 7,660 12/30/2020
1.0.6 565 12/17/2020
1.0.5 2,317 3/24/2020
1.0.4 638 3/22/2020
1.0.3 834 3/3/2020
1.0.2 1,090 8/20/2019
1.0.1 1,052 7/10/2019
1.0.0 632 7/5/2019
1.0.0-beta9 500 5/22/2019
1.0.0-beta8 504 5/22/2019
1.0.0-beta7 492 5/18/2019
1.0.0-beta6 483 5/17/2019
1.0.0-beta5 496 5/15/2019
1.0.0-beta4 448 5/13/2019
1.0.0-beta3 516 5/10/2019
1.0.0-beta2 483 5/8/2019
1.0.0-beta 488 4/28/2019
0.0.18-beta 601 1/17/2019
0.0.17-beta 591 1/11/2019
0.0.16-beta 592 1/4/2019
0.0.15-beta 607 1/3/2019
0.0.14-beta 616 12/9/2018
0.0.13-beta 619 12/3/2018
0.0.12-beta 621 11/21/2018
0.0.11-beta 596 11/21/2018
0.0.10-beta 603 11/14/2018
0.0.8-beta01 617 10/28/2018
0.0.8-beta 592 10/28/2018
0.0.7-beta 694 9/17/2018
0.0.6-beta 721 8/10/2018
0.0.5-beta 683 8/10/2018
0.0.4-beta01 902 5/31/2018
0.0.3-beta 747 3/22/2018
0.0.2-beta 994 9/30/2016
0.0.1-beta 1,492 7/28/2016