Created
April 10, 2018 13:19
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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