Upgrade to Pro — share decks privately, control downloads, hide ads and more …

No REST for the Wicked

Ben Wilson
September 01, 2016

No REST for the Wicked

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

Ben Wilson

September 01, 2016
Tweet

Other Decks in Programming

Transcript

  1. REST GET /users/1 { name: "Joe Bob" email: "[email protected]" friends:

    [ "/users/5", "/users/7", "/users/9", ], organization_id: 1 } http://absinthe-graphql.org Ben Wilson @benwilson512
  2. 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
  3. { 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
  4. GraphQL http://absinthe-graphql.org Ben Wilson @benwilson512 { user: { name: "Joe

    Bob", email: "[email protected]", friends: [ {name: "Fred Jones"}, {name: "Jane Smith"}, {name: "Rebecca Jones"}, ... ] } } { user(id: 1) { name email friends { name } } }
  5. 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
  6. 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
  7. 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. { 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
  9. { 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
  10. 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
  11. 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
  12. 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
  13. Introspection http://absinthe-graphql.org Ben Wilson @benwilson512 { __schema { types {

    name description } } } { __schema: { types: [ { name: "Event", description: "A planned …" }, { name: "Person", description: "Someone …" }, ... ] } }
  14. { 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
  15. 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 } } }
  16. Deferred Content http://absinthe-graphql.org Ben Wilson @benwilson512 { path: ["feed", "stories",

    0, "comments"], data: [ { author: { name: "Joe Bob", message: "Commenting is fun" } }, ... ] }
  17. 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 } } }
  18. Absinthe Goals Correct Easy to Use Idiomatic Performant Community Oriented

    http://absinthe-graphql.org Ben Wilson @benwilson512
  19. 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
  20. 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
  21. ? http://absinthe-graphql.org Ben Wilson @benwilson512 { user: { name: "Joe

    Bob", email: "[email protected]", friends: [ {name: "Fred Jones"}, {name: "Jane Smith"}, {name: "Rebecca Jones"}, ... ] } } { user(id: 1) { name email friends { name } } }
  22. Query Compilation ~A"""
 query Fast($id: ID){ performanceCritical(id: $id) { stuff

    things { name } } } """ http://absinthe-graphql.org Ben Wilson @benwilson512
  23. 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
  24. 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
  25. 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 } } }