Save 37% off PRO during our Black Friday Sale! »

GraphQL понимаем, объясняем, внедряем

GraphQL понимаем, объясняем, внедряем

Доклад о том как разобраться в GraphQL, объяснить его коллегам и отправить на production.

C27317d1ebe4e4a01504acab9d87fe61?s=128

Polina Gurtovaya

September 26, 2019
Tweet

Transcript

  1. GraphQL: Понимаем, объясняем, внедряем !1

  2. !2

  3. !3

  4. !4

  5. !5

  6. !6

  7. Понимаем. !7

  8. GQL service Client Document Query Mutation Subscription Response Data Errors

    Validate Schema Execute Первый взгляд !8
  9. У нас есть схема. Запросы валидируются по схеме автоматически Если

    в результате выполнения мы сломаемся, нам об этом сообщат :) !9 Надёжность
  10. !10 Нет привязки к транспорту Использует GraphQL на этапе сборки

  11. Можно использовать разный транспорт для разных операций …даже разный транспорт

    для одной и той же операции !11 Гибкость в выборе транспорта
  12. WebSocket часто выбирают транспортом подписок (Subscriptions) !12 Подчиняем сокеты

  13. Operation Schema !13 query { viewer { id name }

    product(id: 5) { title quantity tags } } type Query { viewer: User product(id: ID!): Product … } type Product { title: String quantity: Float! tags: [String] user: User … } Operation Type Fileds Selection Set Input type Output type Wrapper type Object type Scalar type
  14. Данные удобно представлять в виде графа Видно не только сущности

    но и связи между ними !14 Понятная структура данных
  15. Побеждаем Overfetching и Underfetching !15 { "showcase": { "name": "ebay.com",

    "active": true, "id": "92", "primary": true, "currency": "USD", "site": { "code": "US", "name": "ebay.com", "currency": "USD", "country": "US", "domain": "ebay.com" } } } <Site name={site.name} code={site.code} />
  16. Интроспекция !16 { __schema { directives { name } }

    __type(name: "Query"){ fields { name description } } } { "data": { "__schema": { "directives": [ … { "name": "deprecated" } ] }, "__type": { "fields": [ { "name": "address", "description": "Get seller’s …" }, …
  17. Интроспекция + Типизация = Крутые доки !17

  18. Доки это крутой референс !18 Легко договариваться Легко поддерживать

  19. Схема это слепок состояния вашего сервиса !19 Можно хранить схему

    в облаке Можно отслеживать историю изменений
  20. Что можно делать со схемой: !20 Стянуть в IDE для

    подсветки и автодополнения Валидировать свои запросы по схеме CI / ваш любимый git hook
  21. Codegen !21 apollo-cli graphql-code-generator Можно генерировать типы (ts / flow)

    …или тесты
  22. !22 query { viewer { email name id } product(id:

    5) { title quantity users { email name id } } } Фрагменты query { viewer { ...UserInfo } product(id: 5) { title quantity users { ...UserInfo } } } fragment UserInfo on User { email name id }
  23. !23 Фрагменты мёржатся fragment UserInfo on User { id email

    name } fragment UserStatus on User { id name online } query { viewer { ...UserInfo ...UserStatus } } "data": { "viewer": { "id": 1, "name": "zloymult", "email": “zloymult@evl.ms”, "online": true } }
  24. !24 Запросы прозрачней для разработчика Можно связывать кусочки интерфейса с

    операцией …или фрагментом
  25. Например так !25 { site { code name active }

    } <Site {...site} />
  26. Тонкая настройка !26 Можно настраивать доступы для каждого поля Можно

    решать как кешировать каждое поле
  27. Объясняем. !27

  28. !28 GraphQL это чОрная магия и сложно на бекенде

  29. Execution !29 query { product(id: 5) { title user {

    name id } } } Рекурсивно обходим наш граф и выполняем каждое поле { Query: { product: (_, { id }) => ({ id, title: `Product${id}`, user: { id: 1 } }) }, User: { name: ({ id }) => `User${id}` } }
  30. !30 GraphQL ломает кеширование

  31. На самом деле !31 GraphQL усложняет http кеширование С клиентским

    кешированием все хорошо
  32. persisted-queries !32 { "query": "query ViewerInfo { viewer: { name

    } }", "variables": {} } id: 1 { "id": 1, "variables": {} } Вместо тела запроса отправляем ID
  33. persisted-queries: чуть сложнее !33 Мы отправляем тело. Теперь сервер запомнил

    id этого запроса { "id": 1, "variables": {} } Генерировать id, отправлять его на сервер. { "errors": [ {"message": "Give me the document!"} ] } Если сервер не знает такого запроса, он просит тело { "id": 1, "query": "query { viewer: { name } }", "variables": {} }
  34. !34 GraphQL запросы слишком большие!

  35. !35 GraphQL запросы тормозят БД!

  36. DataLoaders !36 https:/ /github.com/graphql/dataloader https:/ /github.com/Shopify/graphql-batch

  37. !37 Apollo это плохо! Relay это неудобно!

  38. !38 GraphQL это не гибко!

  39. Мы жертвуем гибкостью ради надёжности !39

  40. Unions !40 { info { id ... on User {

    name } ... on Product { title } } } union UserOrProduct = User | Product type Query { info: UserOrProduct } type User { id: ID! name: String } type Product { id: ID! title: String }
  41. Interfaces !41 { info { id ... on User {

    name } ... on Product { title } } } interface Node = { id: ID! } type Product implements Node { id: ID! title: String } type User implements Node { id: ID! name: String } type Query { info: Node }
  42. Динамическая генерация запросов !42 const batchMutate = ids => `

    mutation UpdateProducts( ) { ${ids .map( id => ` result${id}: UpdateProduct(name: Product-${Math.random()}, id: ${id}) { id name }` ) .join('\n')} } `
  43. Динамическая генерация запросов !43 query { viewer: * } const

    getAllFields => async (fieldName) => { const fieldInfo = await introspect(fieldName); const fields = getAllFields(fieldInfo); return `query { viewer: { ${fields.join('\n')} } }` }
  44. Можно добавлять свои скалярные типы 44 scalar Time Нужно будет

    написать функцию проверки
  45. Можно добавлять свои директивы 44 directive @dryRun( env: String =

    "development" ) on FIELD_DEFINITION mutation { destroyYourDatabase @dryRun { products { id title } } }
  46. Схему можно генерировать !46 Например вот так (graphQL.js): const queryType

    = new GraphQLObjectType({ name: 'Query', fields: { cheburachka: { type: GraphQLString, resolve: function() { return 'cheburachka'; } } } }); const schema = new GraphQLSchema({query: queryType});
  47. GraphQL vs REST !47 Надо сравнивать аккуратно GraphQL - query

    language + execution engine. Есть спека REST - архитектурный стиль OpenAPI (aka Swagger) - как описывать RESTful API
  48. GraphQL vs OpenAPI (А REST так тоже можно научить!) !48

    • Прикручиваем Swagger • Боремся с overfething за счет query-параметров • Придумываем что-то c underfetching • Придумываем что-то с сокетами
  49. А REST так тоже можно научить! !49

  50. !50 GraphQL is DB over HTTP

  51. Хорошо когда GraphQL отражает бизнес-логику !51 query { product(id: 5)

    { title quantity } } "data": { "product": { "title": "", "quantity": 112 } }
  52. Внедряем. !52

  53. Фичастые решения !53

  54. !54 Operation-Based Fragment-based Отличные доки Хорошие доки Не всегда нужна

    схема Нужна схема React, RN, Vue, Angular …. React, RN Свой компилятор Развивается быстрее Больше оптимизаций из коробки Есть GC
  55. !55 query GetViewer { viewer { id currency } }

    <UserInfo name={props.name}/> Normalized Cache { "data": { "viewer": { "id": "1", "currency": "CAD", } } }
  56. !56 <UserInfo name={props.name}/> { "data": { "viewer": { "id": "1",

    "currency": "CAD", } } } Normalized Cache query GetViewer { viewer { id currency } }
  57. Code example !57 # viewerInfo.graphql query ViewerInfo { viewer {

    id currency } } // ViewerSettings.js import { useQuery } from '@apollo/react-hooks'; import { ViewerInfo } from './viewerInfo.graphql'; export function ViewerSettings() { const { loading, data } = useQuery(ViewerInfo); return ( <div> {loading ? <p>Loading</p> : <p>Currency: {data.viewer.currency}</p>} </div> ); }
  58. GraphQL своими руками !58 const { graphql, buildSchema } =

    require('graphql'); const schema = buildSchema(` type Query { userName: String } `); const rootResolver = { userName: () => 'HellSquirrel' }; graphql(schema, '{ userName }', rootResolver).then((response) => { console.log(response); });
  59. GraphQL своими руками !59 fetch("http:!//localhost:3000/graphql", { headers: { "content-type": "application/json",

    “x-csrf-token": “…” }, body: JSON.stringify({ query: "{items { title } }" }), method: "POST" }).then(r !=> r.json()).then(console.log);
  60. Что почитать? !60 evl.ms/blog graphql.org (docs + spec)

  61. Спасибо! !61 @polina_gurtovaya @pgurtovaya hellsquirrel@evl.ms