Skip to content
This repository has been archived by the owner on Apr 1, 2024. It is now read-only.

An F# implementation of Apollo's graphql-transport-ws subprotocol

License

Notifications You must be signed in to change notification settings

valbers/graphql-transport-ws.fsharp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

70 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

graphql-transport-ws.fsharp

An FSharp implementation of the graphql-transport-ws subprotocol for use with FSharp.Data.GraphQL. This repository is not an affiliate of FSharp.Data.GraphQL.

Usage

Server

In a Startup class...

namespace MyApp

open Giraffe
open GraphQLTransportWS.Giraffe
open GraphQLTransportWS
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()
                .Configure(Action<KestrelServerOptions>(fun x -> x.AllowSynchronousIO <- true))
                .AddGraphQLTransportWS<Root>( // STEP 1: Setting the options
                    Schema.executor,
                    rootFactory,
                    "/ws" // --> endpoint for websocket connections
                )
        |> 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>
                    applicationLifetime.ApplicationStopping
                    (loggerFactory.CreateLogger("HttpHandlers.handlerGraphQL"))
                )

    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", String) ],
                    (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", String); Define.Input("isMoon", Boolean) ],
                    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).

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.

About

An F# implementation of Apollo's graphql-transport-ws subprotocol

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages