Skip to content

Instantly share code, notes, and snippets.

@kgoggin
Created April 10, 2018 13:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kgoggin/67d787558336a1ec3c2c74cb83d5bb73 to your computer and use it in GitHub Desktop.
Save kgoggin/67d787558336a1ec3c2c74cb83d5bb73 to your computer and use it in GitHub Desktop.
This gist shows how you could go about defining ReasonML types based on a GraphQL schema that reference each other, as well as a recursive decoder function used to parse a JSON response into the correct type.
/* Use Reason's ability to define recursive types with the `and` keyword */
type movie = {
id: option(string),
name: option(string),
rating: option(Js.null(string)),
runTime: option(Js.null(int)),
actors: option(list(actor)),
}
and actor = {
id: option(string),
name: option(string),
movies: option(list(movie)),
};
/* Next we'll write a recursive decoder. We need to define the different possible
types we're trying to define. We also need a generic "node" type so that our decoder
is always returning the same type of thing. Finally we'll use the "%identity" to allow
converting things to/from our generic "node" type */
type node;
external toNode : 'a => node = "%identity";
external fromNode : node => 'a = "%identity";
type gqlType =
| Movie
| Actor;
let rec decode: (gqlType, Js.Json.t) => node =
(t, json) =>
switch (t) {
| Movie =>
Json.Decode.{
id: optional(field("id", string)),
title: optional(field("name", string)),
rating: optional(field("rating", nullable(string))),
runTime: optional(field("runTime", nullable(int))),
actors:
optional(
field("actors"),
list(json => decode(Actor, json) |> fromNode),
),
}
|> toNode
| Actor =>
Json.Decode.{
id: optional(field("id"), string),
name: optional(field("id"), string),
movies:
optional(
field("movies"),
list(json => decode(Movie, json) |> fromNode),
),
}
|> toNode
};
/* Now we'll define a way to more easily use the decoder by making sure it returns the type
we're actually wanting... */
type decoder('graphqlType) = Js.Json.t => 'graphqlType;
let decodeType: (gqlType, Js.Json.t) => 'a =
(t, j) => decode(t, j) |> fromNode;
/* I'd still like to reference things by their module to keep my naming conventions a little
cleaner. So I'll still have a Movies.re file that looks like this: */
open GQLTypes;
type t = movie;
let decode: decoder(t) = decodeType(Movie);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment