Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

What/How to do with GraphQL? - Valentyn Ostakh (ENG) | Ruby Meditation 27

70 vues

Publié le

Speech of Valentyn Ostakh, Ruby Developer at Ruby Garage, at Ruby Meditation 27, Dnipro, 19.05.2019
Slideshare -
Next conference - http://www.rubymeditation.com/

This talk explores basic concepts of GraphQL.
The main goal is to show how GraphQL works and of what parts it consists of.
From the Ruby side we will look at how to create a GraphQL schema.
In addition, we will consider what pitfalls can be encountered at the start of work with GraphQL.


Announcements and conference materials https://www.fb.me/RubyMeditation
News https://twitter.com/RubyMeditation
Photos https://www.instagram.com/RubyMeditation
The stream of Ruby conferences (not just ours) https://t.me/RubyMeditation

Publié dans : Technologie
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

What/How to do with GraphQL? - Valentyn Ostakh (ENG) | Ruby Meditation 27

  1. 1. What/how to do with GraphQL? Valentyn Ostakh #27
  2. 2. query { me { firstName lastName company position socialLinks { name url } } } { "data": { "me": { "firstName": "Valentyn", "lastName": "Ostakh", "company": "RubyGarage", "position": "Ruby/JS developer", "socialLinks": [ { "name": "facebook", "url": "https://facebook.com/valikos" }, { "name": "twitter", "url": "https://twitter.com/valikos_ost" }, { "name": "github", "url": "https://github.com/valikos" } ] } } }
  3. 3. Agenda • Introduction into GraphQL • GraphQL SDL(Schema Definition Language) • GraphQL Execution • Pitfalls
  4. 4. What is GraphQL? 🤔
  5. 5. What is GraphQL? • A query language for API • A type system of defined data • A platform of both backend + frontend applications
  6. 6. More About GraphQL • GraphQL was developed internally by Facebook in 2012 before being publicly released in 2015 • The idea of GraphQL became from the frontend team • GraphQL services can be written in any language • GraphQL is transport-agnostic • GraphQL represents data in graphs
  7. 7. GraphQL SDL What to do with GraphQL? 🗺
  8. 8. GraphQL SDL GraphQL schema is at the center of any GraphQL server implementation. GraphQL schema describes the functionality available to the clients who connect to it
  9. 9. GraphQL SDL • Schema • Object Types • Fields • Scalars • Enums • Lists • Non-Null • Directives • Aliases • Interfaces • Unions • Fragments • Input Objects
  10. 10. Object Type
  11. 11. GraphQL Object Type { "data": { "character": { "firstName": "Peter", "lastName": "Griffin", "friends": [ { "firstName": "Brian", "lastName": null }, { "firstName": "Homer", "lastName": "Simpson" } ] } } } query { character { firstName lastName friends { firstName lastName } } }
  12. 12. GraphQL Object Type { "data": { "character": { "firstName": "Peter", "lastName": "Griffin", "friends": [ { "firstName": "Brian", "lastName": null }, { "firstName": "Homer", "lastName": "Simpson" } ] } } } query { character { firstName lastName friends { firstName lastName } } }
  13. 13. GraphQL Object Type type Character { firstName: String! lastName: String friends: [Character!] } module Types class CharacterType < BaseObject field :first_name, String, null: false field :last_name, String, null: true field :friends, [Types::CharacterType], null: true end end
  14. 14. Fields
  15. 15. GraphQL Fields type Character { firstName: String! lastName: String friends: [Character!] } module Types class CharacterType < BaseObject field :first_name, String, null: false field :last_name, String, null: true field :friends, [Types::CharacterType], null: true end end
  16. 16. GraphQL Fields { "data": { "character": { "firstName": "Peter", "lastName": "Griffin", "friends": [ { "firstName": "Brian", "lastName": null }, { "firstName": "Homer", "lastName": "Simpson" } ] } } } query { character { firstName lastName friends { firstName lastName } } }
  17. 17. GraphQL Fields { "data": { "character": { "firstName": "Peter", "lastName": "Griffin", "friends": [ { "firstName": "Brian", "lastName": null }, { "firstName": "Homer", "lastName": "Simpson" } ] } } } query { character { firstName lastName friends { firstName lastName } } }
  18. 18. GraphQL Fields • Arguments • Resolvers
  19. 19. GraphQL Fields: Arguments { "data": { "character": { "firstName": "Jon", "lastName": "wonS", "friends": [ { "firstName": "Samwell", "lastName": "Tarly" } ] } } } query { character { firstName lastName(reverse: true) friends(last: 1) { firstName lastName } } }
  20. 20. GraphQL Fields: Arguments { "data": { "character": { "firstName": "Jon", "lastName": "wonS", "friends": [ { "firstName": "Samwell", "lastName": "Tarly" } ] } } } query { character { firstName lastName(reverse: true) friends(last: 1) { firstName lastName } } }
  21. 21. GraphQL Fields: Arguments type Character { firstName: String! lastName(reverse: Boolean): String friends(last: Int): [Character!] } module Types class CharacterType < BaseObject field :first_name, String, null: false field :last_name, String, null: true do argument :reverse, Boolean, required: false end field :friends, [Types::CharacterType], null: true do argument :last, Integer, required: false end end end
  22. 22. GraphQL Fields: Arguments type Character { firstName: String! lastName(reverse: Boolean): String friends(last: Int): [Character!] } module Types class CharacterType < BaseObject field :first_name, String, null: false field :last_name, String, null: true do argument :reverse, Boolean, required: false end field :friends, [Types::CharacterType], null: true do argument :last, Integer, required: false end end end
  23. 23. GraphQL Fields: Resolvers module Types class CharacterType < BaseObject field :first_name, String, null: false field :last_name, String, null: true field :full_name, String, null: false end end
  24. 24. GraphQL Fields: Resolvers module Types class CharacterType < BaseObject field :first_name, String, null: false field :last_name, String, null: true field :full_name, String, null: false def full_name [object.full_name, object.last_name].join(' ') end end end
  25. 25. GraphQL Fields: Resolvers module Types class CharacterType < BaseObject field :first_name, String, null: false field :last_name, String, null: true field :full_name, String, null: false, resolver: Resolvers::FullName end end
  26. 26. GraphQL Fields: Resolvers module Resolvers class Character < Resolvers::Base type Character, null: false argument :id, ID, required: true def resolve(id:) Character.find(id) end end end
  27. 27. Scalars
  28. 28. GraphQL Scalars A GraphQL object type has a name and fields but at some point those fields have to become some concrete data. That's where the scalar types come in: they represent the leaves of the query.
  29. 29. GraphQL Scalars: Built-in Types • Int: A signed 32‐bit integer • Float: A signed double-precision floating-point value • String: A UTF‐8 character sequence • Boolean: true or false • ID: The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an ID signifies that it is not intended to be human‐readable
  30. 30. GraphQL Scalars In most GraphQL service implementations, there is also a way to specify custom scalar types.
  31. 31. GraphQL Scalars: GraphQL-Ruby Scalar Types • Int: like a JSON or Ruby integer • Float: like a JSON or Ruby floating point decimal • String: like a JSON or Ruby string • Boolean: like a JSON or Ruby boolean (true or false) • ID: which a specialized String for representing unique object identifiers • ISO8601DateTime: an ISO 8601-encoded datetime
  32. 32. GraphQL Scalars: Custom Scalar class Types::Url < Types::BaseScalar description "A valid URL, transported as a string" def self.coerce_input(input_value, context) url = URI.parse(input_value) if url.is_a?(URI::HTTP) || url.is_a?(URI::HTTPS) url else raise GraphQL::CoercionError, "#{input_value.inspect} is not a valid URL" end end def self.coerce_result(ruby_value, context) ruby_value.to_s end end
  33. 33. GraphQL Scalars: Custom Scalar class Types::Url < Types::BaseScalar description "A valid URL, transported as a string" def self.coerce_input(input_value, context) url = URI.parse(input_value) if url.is_a?(URI::HTTP) || url.is_a?(URI::HTTPS) url else raise GraphQL::CoercionError, "#{input_value.inspect} is not a valid URL" end end def self.coerce_result(ruby_value, context) ruby_value.to_s end end
  34. 34. Enums
  35. 35. GraphQL Enums Enumerations are similar to custom scalars with the limitation that their values can only be one of a pre-defined list of strings.
  36. 36. GraphQL Enums { "data": { "character": { "firstName": "Peter", "role": "FATHER", "friends": [ { "firstName": "Brian", "role": "PET" }, { "firstName": "Stewie", "role": "SON" } ] } } } query { character { firstName role friends { firstName role } } }
  37. 37. GraphQL Enums enum RoleEnum { FATHER MOTHER SON DAUGHTER DOG } class Types::RoleEnum < Types::BaseEnum value ‘FATHER' value ‘MOTHER' value ‘SON' value ‘DAUGHTER' value ‘PET' end
  38. 38. GraphQL Enums enum RoleEnum { FATHER MOTHER SON DAUGHTER DOG } class Types::RoleEnum < Types::BaseEnum value 'FATHER', value: 1 value 'MOTHER', value: 2 value 'SON', value: 3 value 'DAUGHTER', value: 4 value 'PET', value: 5 end
  39. 39. GraphQL Enums enum RoleEnum { FATHER MOTHER SON DAUGHTER DOG } class Types::RoleEnum < Types::BaseEnum value 'FATHER', value: :father value 'MOTHER', value: :mom value 'SON', value: :son value 'DAUGHTER', value: :daughter value 'PET', value: :animal end
  40. 40. Lists
  41. 41. GraphQL Lists { "data": { "character": { "firstName": "Peter", "lastName": "Griffin", "friends": [ { "firstName": "Brian", "lastName": null }, { "firstName": "Homer", "lastName": "Simpson" } ] } } } query { character { firstName lastName friends { firstName lastName } } }
  42. 42. GraphQL Lists type Character { firstName: String! lastName: String friends: [Character!] } module Types class CharacterType < BaseObject field :first_name, String, null: false field :last_name, String, null: true field :friends, [Types::CharacterType], null: true end end
  43. 43. Non-Null
  44. 44. GraphQL Non-Null In the GraphQL type system all types are nullable by default. This means that a type like Int can take any integer (1, 2, etc.) or null which represents the absence of any value. However, the GraphQL type system allows you to make any type non- null which means that the type will never produce a null value. When using a non-null type there will always be a value.
  45. 45. GraphQL Non-Null type Character { firstName: String! lastName: String friends: [Character!]! } type Character { firstName: String! lastName: String friends: [Character]! } type Character { firstName: String! lastName: String friends: [Character!] } type Character { firstName: String! lastName: String friends: [Character] }
  46. 46. GraphQL Non-Null module Types class CharacterType < BaseObject field :first_name, String, null: false field :last_name, String, null: true field :friends, [Types::CharacterType, null: true], null: true field :friends, [Types::CharacterType, null: true], null: false field :friends, [Types::CharacterType, null: false], null: true field :friends, [Types::CharacterType, null: false], null: false end end
  47. 47. Unions
  48. 48. GraphQL Unions { search(in: "Adventure Time") { __typename ... on Character { firstName } ... on land { name } ... on Building { type } } } { "data": { "search": [ { "__typename": "Character", "firstName": "Finn" }, { "__typename": "Land", "name": "Land of Ooo" }, { "__typename": "Building", "type": "Fort" } ] } }
  49. 49. GraphQL Unions union Result = Character | Land | Building type Character { firstName: String! } type Building { type: String! } type Land { name: String! } type Query { search: [Result] }
  50. 50. GraphQL Unions union Result = Character | Land | Building type Character { firstName: String! } type Building { type: String! } type Land { name: String! } type Query { search: [Result] }
  51. 51. GraphQL Unions class Types::ResultType < Types::BaseUnion possible_types Types::CharacterType, Types::LandType, Types::BuildingType # Optional: if this method is defined, it will override `Schema.resolve_type` def self.resolve_type(object, context) if object.is_a?(Character) Types::CharacterType elsif object.is_a?(Land) Types::LandType else Types::BuildingType end end end
  52. 52. Interfaces
  53. 53. GraphQL Interfaces interface Node { id: ID! } interface Person { firstName: String! lastName: String } type Land implements Node { id: ID! name: String! } type Character implements Node & Person { id: ID! firstName: String! lastName: String }
  54. 54. GraphQL Interfaces module Types::Interfaces::Node include Types::BaseInterface field :id, ID, null: false end module Types::Interfaces::Person include Types::BaseInterface field :first_name, String, null: false field :last_name, String, null: true end module Types class CharacterType < BaseObject implements Types::Interfaces::Node implements Types::Interfaces::Person end end
  55. 55. Fragments
  56. 56. GraphQL Fragments query { character { firstName lastName friends { firstName lastName } } }
  57. 57. GraphQL Fragments fragment CharacterFragment on Character { firstName lastName } query { character { ...CharacterFragment friends { ...CharacterFragment } } }
  58. 58. Aliases
  59. 59. GraphQL Aliases query { hero: character(hero: true) { firstName } antihero: character(hero: false) { firstName } } { "data": { "hero": { "firstName": "Harrison", }, "antihero": { "firstName": "Sebastian", } } }
  60. 60. Directives
  61. 61. GraphQL Directives • @deprecated(reason: String) - marks fields as deprecated with messages • @skip(if: Boolean!) - GraphQL execution skips the field if true by not calling the resolver • @include(id: Boolean!) - Calls resolver for annotated field if true GraphQL provides several built-in directives:
  62. 62. GraphQL Directives query { character { firstName lastName friends @skip(if: $isAlone){ firstName lastName } } } type Character { firstName: String! lastName: String surname: String @deprecated(reason: "Use lastName. Will be removed...") friends: [Character!] }
  63. 63. GraphQL Directives: Custom Directive class Directives::Rest < GraphQL::Schema::Directive description "..." locations( GraphQL::Schema::Directive::FIELD, GraphQL::Schema::Directive::FRAGMENT_SPREAD, GraphQL::Schema::Directive::INLINE_FRAGMENT ) argument :url, String, required: true, description: "..." def self.include?(obj, args, ctx) # implementation end def self.resolve(obj, args, ctx) # implementation end end
  64. 64. query { character @rest(url: "/path") { firstName lastName friends { firstName lastName } } } GraphQL Directives: Custom Directive
  65. 65. Input Objects
  66. 66. GraphQL Input Objects mutation CreateCharacter( $firstName: String! $lastName: String $role: RoleEnum! ) { createCharacter( firstName: $firstName lastName: $lastName role: $role ) { firstName lastName role } } mutation CreateCharacter( $input: CharacterAttributes! ) { createCharacter(input: $input) { firstName lastName role } }
  67. 67. GraphQL Input Objects input CharacterAttributes { firstName: String! lastName: String role: RoleEnum! } class Types::CharacterAttributes < Types::BaseInputObject argument :first_name, String, required: true argument :last_name, String, required: false argument :role, Types::RoleEnum, required: false end
  68. 68. Schema 🍒
  69. 69. GraphQL Schema schema { query: Query }
  70. 70. GraphQL Schema schema { query: Query mutation: Mutation subscription: Subscription } class MySchema < GraphQL::Schema # Required: query Types::QueryType # Optional: mutation Types::MutationType subscription Types::SubscriptionType end
  71. 71. GraphQL Schema class Types::QueryType < GraphQL::Schema::Object field :character, Types::CharacterType, resolver: Resolvers::Character end # Similarly: class Types::MutationType < GraphQL::Schema::Object # ... end # and class Types::SubscriptionType < GraphQL::Schema::Object # ... end type Query { character(id: ID!): CharacterType }
  72. 72. What GraphQL SDL gives us?
  73. 73. What GraphQL SDL gives us? Documentation
  74. 74. What GraphQL SDL gives us? Documentation is the first class citizen in GraphQL
  75. 75. GraphQL Execution How to do with GraphQL? 🚀
  76. 76. When executing a GraphQL query one of the very first things the server needs to do is transform the query (currently a string) into something it understands. Transport
  77. 77. Transport • HTTP POST • one endpoint (e.g. /graphql) • response code 200 OK
  78. 78. Transport
  79. 79. GraphQL-Ruby Phases of Execution • Tokenize: splits the string into a stream of tokens • Parse: builds an abstract syntax tree (AST) out of the stream of tokens • Validate: validates the incoming AST as a valid query for the schema • Rewrite: builds a tree of nodes which express the query in a simpler way than the AST • Analyze: if there are any query analyzers, they are run • Execute: the query is traversed, resolve functions are called and the response is built • Respond: the response is returned as a Hash
  80. 80. Tokenize & Parsing GraphQL has its own grammar. We need this rules to split up the query.  GraphQL Ruby uses a tool called Ragel to generate its lexer. How to understand that we have valid data?
  81. 81. How to verify incoming data? • Breaks up a stream of characters (in our case a GraphQL query) into tokens • Turns sequences of tokens into a more abstract representation of the language
  82. 82. Lexer query { character { firstName lastName friends { firstName lastName } } } GraphQL.scan(query) => [ (QUERY "query" [2:1]), (LCURLY "{" [2:7]), (IDENTIFIER "character" [3:3]), (LCURLY "{" [3:13]), (IDENTIFIER "firstName" [4:5]), (IDENTIFIER "lastName" [5:5]), (IDENTIFIER "friends" [6:5]), (LCURLY "{" [6:13]), (IDENTIFIER "firstName" [7:7]), (IDENTIFIER "lastName" [8:7]), (RCURLY "}" [9:5]), (RCURLY "}" [10:3]), (RCURLY "}" [11:1]) ]
  83. 83. Parser query { character { firstName lastName friends { firstName lastName } } } GraphQL.parse(query) => #<GraphQL::Language::Nodes::Document:0x00007fbd8ae3dec0 @definitions= [#<GraphQL::Language::Nodes::OperationDefinition:… @col=1, @directives=[], @filename=nil, @line=2, @name=nil, @operation_type="query", @selections= [#<GraphQL::Language::Nodes::Field:… @alias=nil, @arguments=[], @col=3, @directives=[], @filename=nil, @line=3, @name="character"
  84. 84. Lexer & Parser Javascript implementation of GraphQL has hand-written lexer and parser. GraphQL Ruby uses a tool called Ragel to generate its lexer. GraphQL Ruby uses a tool called Racc to generate its parser.
  85. 85. GraphQL-Ruby Phases of Execution Tokenizing and Parsing are fundamental steps in the execution process. Validating, Rewriting, Analyzing, Executing are just details of the implementation.
  86. 86. Pitfalls 🤕
  87. 87. Pitfalls • Authentication • File uploading • N+1 • Caching • Performance
  88. 88. Authentication • One endpoint • Handle guests and registered users • Fetch schema definition • Solutions: 1. auth processes with GraphQL 2. auth by tokens (JWT, OAuth) (check tokens)
  89. 89. File uploading • Vanilla GraphQL doesn’t support throwing raw files into your mutations • Solutions: 1. Base64 Encoding 2. Separate Upload Requests 3. GraphQL Multipart Requests(most recommended)
  90. 90. N+1 • The server executes multiple unnecessary round trips to data stores for nested data • Problem in how GraphQL works, not Ruby • Solutions: 1. preload data 2. gem ‘graphql-batch’ 3. gem ‘batch-loader’
  91. 91. Caching • Transport-agnostic • Uses POST with HTTP • Don’t have url with identifier • Solution: 1. Server side caching(Memcache, Redis, etc.)
  92. 92. Performance • Big queries can bring your server down to its knees • Solutions: 1. Set query timeout 2. Check query depth 3. Check query complexity
  93. 93. Links • https://graphql.org • https://gql.foundation • https://graphql.github.io/graphql-spec • https://graphql-ruby.org • https://github.com/valikos/rm-27
  94. 94. Thanks 🙌
  95. 95. Questions? 🤔

×