package ppx_graphql
Install
Dune Dependency
Authors
Maintainers
Sources
sha256=1e4bce08f9219140711fa685ab3908c81e0c67379149fc2268cd427f1a991ac8
md5=867fd458e920f747f5557fdba257ce12
Description
Given a introspection query response in schema.json
, the expression [%graphql {| query { ... } |} ]
is rewritten to a 3-tuple (query, kvariables, parse)
:
query
(typestring
) is the GraphQL query to be submitted.kvariables
(type(Yojson.Basic.json -> 'a) -> arg1:_ -> ... -> argn:_ -> unit -> 'a
) is a function to construct the JSON value to submit as query variables. The labels and types ofargx
are extracted from the query. Required variables appear as labeled arguments, optional variables appear as optional arguments.parse
is a function for parsing the JSON response from the server and has the typeYojson.Basic.json -> < ... >
. The shape of the object type corresponds to the returned response.
Published: 23 Aug 2017
README
Type-safe GraphQL queries in OCaml
Given a GraphQL schema (introspection query response) and a GraphQL query, ppx_graphql
generates three values: (1) a GraphQL query (2) a function to construct the associated query variables, and (3) a function for parsing the GraphQL JSON response into a typed value (object type).
Here's an example of using ppx_graphql
and the generated values (the schema is shown at the top in GraphQL Schema Language):
(*
enum ROLE {
USER
ADMIN
}
type User {
id: ID!
role: ROLE
contacts: [User!]!
}
type Query {
user(id: ID!): User
}
schema {
query: Query
}
*)
let query, kvariables, parse = [%graphql {|
query FindUser($id: ID!) {
user(id: $id) {
id
role
contacts {
id
}
}
}
|}] in
(* ... *)
In this example, the following values are generated:
query
(typestring
) is the GraphQL query to be submitted. Currently it's an unmodified version of the string provided to%graphql
, but it will likely be modified in the future, e.g. to inject__typename
for interface disambiguation.kvariables
(type(Yojson.Basic.json -> 'a) -> id:string -> unit -> 'a
) is a function to construct the JSON value to submit as query variables (doc). Note that the first argument is a continuation to handle the resulting JSON value -- this makes it easier to write nice clients (see more below). The type is extracted from the query. Required variables appear as labeled arguments, optional variables appear as optional arguments.parse
is a function for parsing the JSON response from the server and has the type:Yojson.Basic.json -> <user: <id: string; role: [> `USER | `ADMIN] option; contacts: <id: string> list> >
This type captures the shape of the GraphQL response in a type-safe fashion based on the provided schema. Scalars are converted to their OCaml equivalent (e.g. a GraphQL
String
is an OCamlstring
), nullable types are converted tooption
types, enums to polymorphic variants, lists to list types and GraphQL objects to OCaml objects. Note that this function will likely return aresult
type in the future, as the GraphQL query can fail.
With the above, it's possible to write quite executable queries quite easily:
let executable_query (query, kvariables, parse) =
kvariables (fun variables ->
let response_body = (* construct HTTP body here and submit to GraphQL endpoint *) in
Yojson.Basic.of_string response_body
|> parse
)
let find_user_role = executable_query [%graphql {|
query FindUserRole($id: ID!) {
user(id: $id) {
role
}
}
|}]
Here find_user_role
has the type id:string -> unit -> <user: <role: [`USER | `ADMIN] option> option>
. See github.ml
for a real example using Lwt
and Cohttp
.
[%graphql ...]
expects a file schema.json
to be present in the same directory as the source file. This file should contain an introspection query response.
For use with jbuilder, use the preprocess
- and preprocessor_deps
-stanza:
(executable
(preprocess (pps (ppx_graphql)))
(preprocessor_deps ((file schema.json)))
...
)