Skip to content

Latest commit

 

History

History
95 lines (79 loc) · 4.25 KB

File metadata and controls

95 lines (79 loc) · 4.25 KB

Usage

Server

In a Startup class...

namespace MyApp

open Giraffe
open FSharp.Data.GraphQL.Server.AspNetCore.Giraffe
open FSharp.Data.GraphQL.Server.AspNetCore
open Microsoft.AspNetCore.Server.Kestrel.Core
open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.Configuration
open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.Hosting
open Microsoft.Extensions.Logging
open System
open System.Text.Json

type Startup private () =
    // Factory for object holding request-wide info. You define Root somewhere else.
    let rootFactory () : Root =
        { RequestId = Guid.NewGuid().ToString() }

    new (configuration: IConfiguration) as this =
        Startup() then
        this.Configuration <- configuration

    member _.ConfigureServices(services: IServiceCollection) =
        services.AddGiraffe()
                .AddGraphQL<Root>( // STEP 1: Setting the options
                    Schema.executor, // --> Schema.executor is defined by yourself somewhere else (in another file)
                    rootFactory,
                    "/ws" // --> endpoint for websocket connections (optional. Default value: "/ws")
                )
        |> ignore

    member _.Configure(app: IApplicationBuilder, applicationLifetime : IHostApplicationLifetime, loggerFactory : ILoggerFactory) =
        let errorHandler (ex : Exception) (log : ILogger) =
            log.LogError(EventId(), ex, "An unhandled exception has occurred while executing the request.")
            clearResponse >=> setStatusCode 500
        app
            .UseGiraffeErrorHandler(errorHandler)
            .UseWebSockets()
            .UseWebSocketsForGraphQL<Root>() // STEP 2: using the GraphQL websocket middleware
            .UseGiraffe (HttpHandlers.handleGraphQL<Root>)

    member val Configuration : IConfiguration = null with get, set

In your schema, you'll want to define a subscription, like in (example taken from the star-wars-api sample in the "samples/" folder):

    let Subscription =
        Define.SubscriptionObject<Root>(
            name = "Subscription",
            fields = [
                Define.SubscriptionField(
                    "watchMoon",
                    RootType,
                    PlanetType,
                    "Watches to see if a planet is a moon.",
                    [ Define.Input("id", StringType) ],
                    (fun ctx _ p -> if ctx.Arg("id") = p.Id then Some p else None)) ])

Don't forget to notify subscribers about new values:

    let Mutation =
        Define.Object<Root>(
            name = "Mutation",
            fields = [
                Define.Field(
                    "setMoon",
                    Nullable PlanetType,
                    "Defines if a planet is actually a moon or not.",
                    [ Define.Input("id", StringType); Define.Input("isMoon", BooleanType) ],
                    fun ctx _ ->
                        getPlanet (ctx.Arg("id"))
                        |> Option.map (fun x ->
                            x.SetMoon(Some(ctx.Arg("isMoon"))) |> ignore
                            schemaConfig.SubscriptionProvider.Publish<Planet> "watchMoon" x // here you notify the subscribers upon a mutation
                            x))])

Finally run the server (e.g. make it listen at localhost:8086).

There's a demo chat application backend in the samples/chat-app folder that showcases the use of FSharp.Data.GraphQL.Server.AspNetCore in a real-time application scenario, that is: with usage of GraphQL subscriptions (but not only). The tried and trusted star-wars-api also shows how to use subscriptions, but is a more basic example in that regard. As a side note, the implementation in star-wars-api was used as a starting point for the development of FSharp.Data.GraphQL.Server.AspNetCore.

Client

Using your favorite (or not :)) client library (e.g.: Apollo Client, Relay, Strawberry Shake, elm-graphql ❤️), just point to localhost:8086/graphql (as per the example above) and, as long as the client implements the graphql-transport-ws subprotocol, subscriptions should work.