Schema DSL #15
Description
I've been lurking in this repo for a while and I've tried to come up with some kind of DSL to describe a schema at a high level. Has anybody already given some thought about this?
I've tried to replicate the Star Wars example from the reference graphql repository from Facebook (https://github.com/graphql/graphql-js/tree/master/src/__tests__), this is what I've come up with:
defmodule Character do
use GraphQL.ObjectInterface
field :id, null: false
field :name
field :friends, type: List.of(Character)
field :appearsIn, type: List.of(Episode)
end
defmodule Human do
use GraphQL.Object, deriving: Character
field :homePlanet
end
defmodule Droid do
use GraphQL.Object, deriving: Character
field :primaryFunction
end
defmodule Schema do
use GraphQL.Schema
field :hero, type: Character do
argument :episode, description: "foo"
resolve %{episode: episode} do
getHero(episode)
end
resolve do
getHero(1000)
end
end
field :human, type: Human do
argument :id, description: "id of the human", null: false
resolve %{id: id} do
getHuman(id)
end
end
field :droid, type: Droid do
argument :id, description: "id of the droid", null: false
resolve %{id: id} do
getDroid(id)
end
end
@humans [
"1000": %Human{
id: "1000",
name: "Luke Skywalker",
friends: ["1002", "1003", "2000", "2001"],
appearsIn: [4, 5, 6],
homePlanet: "Tatooine",
},
"1001": %Human{
id: "1001",
name: "Darth Vader",
friends: [ "1004" ],
appearsIn: [ 4, 5, 6 ],
homePlanet: "Tatooine",
},
[ ... ]
]
@droids [ ... ]
defp getHero(5), do: @humans["1000"]
defp getHero(_), do: @droids["2001"]
defp getHuman(id), do: @humans[id]
defp getDroid(id), do: @droids[id]
end
The Schema module defines the schema with its field. Each field has some arguments and one or more resolve functions, depending on whether some arguments are nullable.
Fields and arguments accept a "type" option, and nullability is expressed with a "null: false" option.
Interfaces are defined by using GraphQL.ObjectInterface
, with a similar DSL to define the fields, while objects deriving the interface specify doing so when "using" GraphQL.Object
(see Human and Droid).
Modules importing GraphQL.Object
behave very similarly to structs, and inherit all the fields defined in the parent interface, if any.
That's just a proof of concept but I'd like to hear some comments and if there's any idea somebody's written down for a DSL.
Disclaimer: I'm quite new with Elixir, there surely are syntax errors in the POC code above.