No REST for the Wicked

E5b31154e4e71265a60728fef2bacbde?s=47 Ben Wilson
September 01, 2016

No REST for the Wicked

Build a GraphQL API with http://absinthe-graphql.org/

E5b31154e4e71265a60728fef2bacbde?s=128

Ben Wilson

September 01, 2016
Tweet

Transcript

  1. 4.

    REST GET /users/1 { name: "Joe Bob" email: "joe.bob@foo.com" friends:

    [ "/users/5", "/users/7", "/users/9", ], organization_id: 1 } http://absinthe-graphql.org Ben Wilson @benwilson512
  2. 5.
  3. 6.

    REST, Camp 2 GET /users/1 { name: "Joe Bob”, organization_id:

    1, friends: [ { name: "Fred Jones", organization: { name: "Cool Folks LLC", payment_plan_id: 5 } }, … } http://absinthe-graphql.org Ben Wilson @benwilson512
  4. 7.
  5. 8.

    { name: "Joe Bob” organization_id: 1, friends: [ { name:

    "Fred Jones", organization: { name: "Cool Folks LLC", payment_plan_id: 5 } }, … ] } GET /users/1 REST, Camp 2 http://absinthe-graphql.org Ben Wilson @benwilson512
  6. 11.

    GraphQL http://absinthe-graphql.org Ben Wilson @benwilson512 { user: { name: "Joe

    Bob", email: "joe.bob@foo.com", friends: [ {name: "Fred Jones"}, {name: "Jane Smith"}, {name: "Rebecca Jones"}, ... ] } } { user(id: 1) { name email friends { name } } }
  7. 14.

    GET /events?date=2016-08-30&location_id=4 http://absinthe-graphql.org Ben Wilson @benwilson512 @valid_filters ~w(date location_id name)a

    def index(conn, params) do filters = @valid_filters |> Enum.reduce(%{}, fn key, filters -> case Map.fetch(params, Atom.to_string(key)) do {:ok, value} -> add_filter(key, value, filters) _ -> query end end) render("index.json", events: Event.list(filters)) end
  8. 15.

    But wait! there’s more… def add_filter(:date, date, filters) do case

    Calendar.parse(date) do {:ok, date} -> Map.put(filters, :date, date) _ -> filters end end def add_filter(key, value, filters) do Map.put(filters, key, value) end
  9. 16.

    GET /events?date=2016-08-30&location_id=4 http://absinthe-graphql.org Ben Wilson @benwilson512 @valid_filters ~w(date location_id name)a

    def index(conn, params) do filters = @valid_filters |> Enum.reduce(%{}, fn key, filters -> case Map.fetch(params, Atom.to_string(key)) do {:ok, value} -> add_filter(key, value, filters) _ -> query end end) render("index.json", events: Event.list(filters)) end
  10. 17.

    { events(location_id: 4, date: "2016-08-30") { name } } http://absinthe-graphql.org

    Ben Wilson @benwilson512 Using: def events(filters, _) do {:ok, Event.list(filters)} end
  11. 18.

    { events(location_id: 4, date: "2016-08-30") { name } } http://absinthe-graphql.org

    Ben Wilson @benwilson512 def events(filters, _) do ... end field :events, list_of(:event) do arg :date, :date arg :location_id, :id arg :name, :string resolve &events/2 end
  12. 19.

    http://absinthe-graphql.org Ben Wilson @benwilson512 Custom Scalar @desc """ ISO Date

    2015-11-21 2015-01-02 """ scalar :date do parse &Calendar.Date.Parse.iso8601/1 serialize &Calendar.Date.Format.iso8601/1 end
  13. 20.

    http://absinthe-graphql.org Ben Wilson @benwilson512 Built-in Scalar @desc """ The `String`

    scalar type represents textual data, represented as UTF-8 character sequences. ... """ scalar :string do serialize &to_string/1 parse &parse_string/1 end
  14. 21.

    http://absinthe-graphql.org Ben Wilson @benwilson512 @desc """ A planned occasion, like

    a meeting or conference """ object :event do @desc "The date on which the event occurs" field :date, non_null(:date) @desc "The location at which the event happens" field :location, non_null(:location) @desc "The name of the event" field :name, non_null(:string) field :attendees, list_of(:person) end
  15. 22.

    Introspection http://absinthe-graphql.org Ben Wilson @benwilson512 { __schema { types {

    name description } } } { __schema: { types: [ { name: "Event", description: "A planned …" }, { name: "Person", description: "Someone …" }, ... ] } }
  16. 24.

    { user: { name: "Bob", primaryOrganization: { id: 7, name:

    "Elixir Co INC" } }, location: { name: "Headquarters", organization: { id: 7, name: "Elixir Co INC" } } } http://absinthe-graphql.org Ben Wilson @benwilson512
  17. 25.

    Directives http://absinthe-graphql.org Ben Wilson @benwilson512 { feed: { stories: [

    { author: { name: "Ben Wilson" }, message: "Elixir Rocks!" }, { author: { name: "Bruce Williams" }, message: "GraphQL is the bomb!" }, ... ] } } { feed { stories { author { name } comments @defer { author { name } } message } } }
  18. 26.

    Deferred Content http://absinthe-graphql.org Ben Wilson @benwilson512 { path: ["feed", "stories",

    0, "comments"], data: [ { author: { name: "Joe Bob", message: "Commenting is fun" } }, ... ] }
  19. 27.

    Directives http://absinthe-graphql.org Ben Wilson @benwilson512 { feed { stories @stream

    { author { name } comments @defer { author { name } } message } } } { feed { stories @live { author { name } comments @defer { author { name } } message } } } { feed { stories { author { name } comments @defer { author { name } } message } } }
  20. 28.

    Absinthe Goals Correct Easy to Use Idiomatic Performant Community Oriented

    http://absinthe-graphql.org Ben Wilson @benwilson512
  21. 29.

    Schema Errors web/schema.ex:12: Users :usre is not defined in your

    schema. Types must exist if referenced. object :organization do field :name, :string field :users, list_of(:usre) end http://absinthe-graphql.org Ben Wilson @benwilson512
  22. 30.

    Schema Compilation http://absinthe-graphql.org Ben Wilson @benwilson512 @desc "A faction in

    the Star Wars saga" node object :faction do @desc "The name of the faction" field :name, :string @desc "The ships used by the faction." connection field :ships, node_type: :ship do resolve &Ship.list/2 end end
  23. 32.

    ? http://absinthe-graphql.org Ben Wilson @benwilson512 { user: { name: "Joe

    Bob", email: "joe.bob@foo.com", friends: [ {name: "Fred Jones"}, {name: "Jane Smith"}, {name: "Rebecca Jones"}, ... ] } } { user(id: 1) { name email friends { name } } }
  24. 34.

    Query Compilation ~A"""
 query Fast($id: ID){ performanceCritical(id: $id) { stuff

    things { name } } } """ http://absinthe-graphql.org Ben Wilson @benwilson512
  25. 35.

    Projection http://absinthe-graphql.org Ben Wilson @benwilson512 def events(filters, _) do {:ok

    Event.list(filters)} end def list(filters) do Event |> where(^filters) |> Repo.all end
  26. 36.

    Projection http://absinthe-graphql.org Ben Wilson @benwilson512 def events(filters, graphql_info) do preloads

    = Absinthe.Ecto.preloads(graphql_info) selections = Absinthe.Ecto.preloads(graphql_info) {:ok Event.list(filters, preloads, selections)} end def list(filters, preloads, selections) do Event |> where(^filters) |> select(^selections) |> preload(^preloads) |> Repo.all end
  27. 37.

    Real Time Data http://absinthe-graphql.org Ben Wilson @benwilson512 { feed {

    stories @live { author { name } comments @defer { author { name } } message } } } subscription { stories(author_id: 1) { message author { name } } }