Movatterモバイル変換


[0]ホーム

URL:


packagegraphql-lwt

  1. Overview
  2. Docs

You can search for identifiers within the package.

in-package search v0.2.0

Build GraphQL schemas with Lwt support

Install

Dune Dependency

Authors

Maintainers

Sources

graphql-0.14.0.tbz
sha256=bf8bf5b9e17e355ecbbd82158a769fe2b138e746753fd3a23008ada3afcd1c06
sha512=1d303d9ab67faecea8081f007b3696e36033aa65eba0854f50067b4d667d9a9ad185ad949371790a03509cb31bf6356b75c58f3066da9c35d82e620df5780185

Description

graphql-lwt adds support for Lwt tographql, so you can use Lwt in your GraphQL schema resolver functions.

Published:19 Jul 2022

README

GraphQL Servers in OCaml

This repo contains a library for creating GraphQL servers in OCaml. Note that the API is still under active development.

Current feature set:

  • [x] Type-safe schema design

  • [x] GraphQL parser in pure OCaml using Menhir

  • [x] Query execution

  • [x] Introspection of schemas

  • [x] Arguments for fields

  • [x] Allows variables in queries

  • [x] Lwt support

  • [x] Async support

  • [x] Example with HTTP server and GraphiQL

  • [x] GraphQL Subscriptions

Documentation

Four OPAM packages are provided:

  • graphql provides the core functionality and is IO-agnostic. It provides a functorGraphql.Schema.Make(IO) to instantiate with your own IO monad.

  • graphql-lwt provides the moduleGraphql_lwt.Schema withLwt support in field resolvers.

  • graphql-async provides the moduleGraphql_async.Schema withAsync support in field resolvers.

  • graphql_parser provides query parsing functionality.

  • graphql-cohttp allows exposing a schema over HTTP usingCohttp.

API documentation:

Examples

GraphiQL

To run a sample GraphQL server also serving GraphiQL, do the following:

opam install dune graphql-lwt graphql-cohttp cohttp-lwt-unixgit clone git@github.com:andreas/ocaml-graphql-server.gitdune exec examples/server.exe

Now openhttp://localhost:8080/graphql.

Defining a Schema

open Graphqltype role = User | Admintype user = {  id   : int;  name : string;  role : role;}let users = [  { id = 1; name = "Alice"; role = Admin };  { id = 2; name = "Bob"; role = User }]let role = Schema.(enum "role"  ~doc:"The role of a user"  ~values:[    enum_value "USER" ~value:User;    enum_value "ADMIN" ~value:Admin;  ])let user = Schema.(obj "user"  ~doc:"A user in the system"  ~fields:[    field "id"      ~doc:"Unique user identifier"      ~typ:(non_null int)      ~args:Arg.[]      ~resolve:(fun info p -> p.id)    ;    field "name"      ~typ:(non_null string)      ~args:Arg.[]      ~resolve:(fun info p -> p.name)    ;    field "role"      ~typ:(non_null role)      ~args:Arg.[]      ~resolve:(fun info p -> p.role)  ])let schema = Schema.(schema [  field "users"    ~typ:(non_null (list (non_null user)))    ~args:Arg.[]    ~resolve:(fun info () -> users)])

Running a Query

Without variables:

match Graphql_parser.parse "{ users { name } }" with| Ok query -> Graphql.Schema.execute schema ctx query| Error err -> failwith err

With variables parsed from JSON:

match Graphql_parser.parse "{ users(limit: $x) { name } }" with| Ok query ->    let json_variables = Yojson.Basic.(from_string "{\"x\": 42}" |> Util.to_assoc) in    let variables = (json_variables :> (string * Graphql_parser.const_value) list)    Graphql.Schema.execute schema ctx ~variables query| Error err ->    failwith err

Recursive Objects

The functionSchema.fix can be used to define both self-recursive and mutually recursive objects:

(* self-recursive *)type tweet = {  id : int;  replies : tweet list;}let tweet = Schema.(fix (fun recursive ->  recursive.obj "tweet"    ~fields:(fun tweet -> [      field "id"        ~typ:(non_null int)        ~args:Arg.[]        ~resolve:(fun info t -> t.id)        ;      field "replies"        ~typ:(non_null (list (non_null tweet)))        ~args:Arg.[]        ~resolve:(fun info t -> t.replies)    ])))
(* mutually recursive *)let foo, bar = Schema.(fix (fun recursive ->  let foo = recursive.obj "foo" ~fields:(fun (_, bar) -> [      field "bar"        ~typ:bar        ~args:Arg.[]        ~resolve:(fun info foo -> foo.bar)    ])  in  let bar = recursive.obj "bar" ~fields:(fun (foo, _) -> [    field "foo"      ~typ:foo      ~args:Arg.[]      ~resolve:(fun info bar -> bar.foo)      ])  in  foo, bar))

Lwt Support

open Lwt.Infixopen Graphql_lwtlet schema = Schema.(schema [  io_field "wait"    ~typ:(non_null float)    ~args:Arg.[      arg "duration" ~typ:float;    ]    ~resolve:(fun info () ->      Lwt_result.ok (Lwt_unix.sleep duration >|= fun () -> duration)    )])

Async Support

open Core.Stdopen Async.Stdopen Graphql_asynclet schema = Schema.(schema [  io_field "wait"    ~typ:(non_null float)    ~args:Arg.[      arg "duration" ~typ:float;    ]    ~resolve:(fun info () ->      after (Time.Span.of_float duration) >>| fun () -> duration    )])

Arguments

Arguments for a field can either be required, optional or optional with a default value:

Schema.(obj "math"  ~fields:(fun _ -> [    field "sum"      ~typ:int      ~args:Arg.[        arg  "x" ~typ:(non_null int); (* <-- required *)        arg  "y" ~typ:int;            (* <-- optional *)        arg' "z" ~typ:int ~default:7  (* <-- optional w/ default *)      ]      ~resolve:(fun info () x y z ->        let y' = match y with Some n -> n | None -> 42 in        x + y' + z      )  ]))

Note that you must usearg' to provide a default value.

Subscriptions

Schema.(schema [     ...  ]  ~subscriptions:[    subscription_field "user_created"      ~typ:(non_null user)      ~resolve:(fun info ->        let user_stream, push_to_user_stream = Lwt_stream.create () in        let destroy_stream = (fun () -> push_to_user_stream None) in        Lwt_result.return (user_stream, destroy_stream))    ])

HTTP Server

Using Lwt:

open Graphql_lwtlet schema = Schema.(schema [  ...])module Graphql_cohttp_lwt = Graphql_cohttp.Make (Schema) (Cohttp_lwt.Body)let () =  let callback = Graphql_cohttp_lwt.make_callback (fun _req -> ()) schema in  let server = Cohttp_lwt_unix.Server.make ~callback () in  let mode = `TCP (`Port 8080) in  Cohttp_lwt_unix.Server.create ~mode server  |> Lwt_main.run

Design

Only valid schemas should pass the type checker. If a schema compiles, the following holds:

  1. The type of a field agrees with the return type of the resolve function.

  2. The arguments of a field agrees with the accepted arguments of the resolve function.

  3. The source of a field agrees with the type of the object to which it belongs.

  4. The context argument for all resolver functions in a schema agree.

The following blog posts introduces the core design concepts:

  • https://andreas.github.io/2017/11/29/type-safe-graphql-with-ocaml-part-1/

  • https://andreas.github.io/2018/01/05/modeling-graphql-type-modifiers-with-gadts-part-2/

  • https://andreas.github.io/2019/05/20/graphql-resolver-arguments-as-diff-lists-part-3/

Dependencies (4)

  1. lwt
  2. graphql>= "0.13.0"
  3. dune>= "1.11"
  4. ocaml>= "4.08.0"

Dev Dependencies (2)

  1. yojsonwith-test & >= "1.6.0"
  2. alcotestwith-test

Used by (4)

  1. dream
  2. graphql-cohttp>= "0.13.0"
  3. irmin-graphql!= "3.3.2"
  4. opium-graphql

Conflicts

None


[8]ページ先頭

©2009-2025 Movatter.jp