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

No REST for the Wicked

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Ben Wilson Ben Wilson
September 01, 2016

No REST for the Wicked

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

Avatar for Ben Wilson

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 } } }