FSharp.Data.GraphQL.Shared 2.2.1

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

// Install FSharp.Data.GraphQL.Shared as a Cake Tool
#tool nuget:?package=FSharp.Data.GraphQL.Shared&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 设置构建和运行星战 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 的示例应用程序,其中包含与 Relay 兼容的服务器 API。

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

为了更新客户端模式,请访问 http://localhost:8083/,并将响应(当前 F# 服务器的 introspection 查询结果)复制粘贴到 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 的身份函数上进行配置。另外,还需要传递 ObjectDef 中 Type 和字段的名称

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

客户端提供者

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

  1. 提供所需 GraphQL 服务器的 URL(无需任何自定义 HTTP 标头)。提供者将访问服务器,发送一个 Introspection 查询,并用该模式提供用于制作查询的类型。
type MyProvider = GraphQLProvider<"http://some.graphqlserver.development.org">
  1. 提供由提供者使用的 introspection json 文件。不过请注意,introspection json 应包含提供者需要的所有字段。您可以通过在所需服务器上运行我们的标准 introspection 查询并保存到与使用提供者的项目相同的路径的文件中来获取正确的字段
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'>类被实例化时发生。在此阶段,使用类型模式映射来构建一个字段执行映射,其中包含所有字段定义及其字段解析函数。该映射在之后的规划和执行阶段用来检索查询的模式的值。

  • 操作规划阶段:此阶段在运行无执行计划的查询之前发生。该阶段负责分析由查询生成的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

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

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

一个实用的中间件示例可以是测量查询规划所需时间的中间件。其结果作为规划上下文的Metadata的一部分返回。Metadata对象是Map实现,作为信息袋在各个阶段之间传递,直到它作为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定义一个阈值中间件。如果我们执行的查询以递归方式请求“朋友的同朋友”,则执行器在查询超过2.0的权重阈值之前只会嵌套4次。

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

此中间件可以用来自动生成一个用于模式中对象的列表字段的筛选器。这个筛选器可以作为查询字段的参数传递,并在解析函数的字段解析函数的ResolveFieldContext参数中恢复。

例如,我们可以为Human对象创建筛选列表字段的中间件,该对象类型为Character option

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

筛选参数是一个对象,通过字段上的JSON定义映射到filter参数。一个简单的示例是过滤以其名字以字母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 已计算。
_mono_mac monomac 已计算。
Monotouch monotouch 已计算。
Tizen tizen40 已计算。 tizen60 已计算。
Xamarin.iOS xamarinios 已计算。
Xamarin.Mac xamarinmac 已计算。
Xamarin.TVOS xamarintvos 已计算。
Xamarin.WatchOS xamarinwatchos 已计算。
兼容的目标框架
包含的目标框架(在包中)
更多关于 目标框架.NET Standard 的信息。

NuGet 包 (5)

显示依赖 FSharp.Data.GraphQL.Shared 的前 5 个 NuGet 包

下载
FSharp.Data.GraphQL.Server

FSharp 实现 Facebook GraphQL 查询语言(服务器端)

FSharp.Data.GraphQL.Client

FSharp 实现 Facebook GraphQL 查询语言(客户端)

FSharp.Data.GraphQL.Server.Middleware

为 FSharp.Data.GraphQL.Server Executor 内置的通用中间件

FSharp.Data.GraphQL.Server.Relay

FSharp 实现 Relay GraphQL Cursor Connections Specification,用于 Facebook GraphQL 查询语言(服务器端)

FSharp.Data.GraphQL.Server.AspNetCore

FSharp 实现 Facebook GraphQL 查询语言(应用基础设施)

GitHub 仓库

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

版本 下载 最后更新
2.2.1 1,772 6/16/2024
2.2.0 424 5/8/2024
2.1.0 188 4/21/2024
2.0.0 205 3/23/2024
2.0.0-beta1 232 2/16/2024