$30 off During Our Annual Pro Sale. View Details »

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

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

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

Polina Gurtovaya

September 26, 2019
Tweet

More Decks by Polina Gurtovaya

Other Decks in Programming

Transcript

  1. !2

  2. !3

  3. !4

  4. !5

  5. !6

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

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

    в результате выполнения мы сломаемся, нам об этом сообщат :) !9 Надёжность
  8. Можно использовать разный транспорт для разных операций …даже разный транспорт

    для одной и той же операции !11 Гибкость в выборе транспорта
  9. 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
  10. Данные удобно представлять в виде графа Видно не только сущности

    но и связи между ними !14 Понятная структура данных
  11. Побеждаем 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} />
  12. Интроспекция !16 { __schema { directives { name } }

    __type(name: "Query"){ fields { name description } } } { "data": { "__schema": { "directives": [ … { "name": "deprecated" } ] }, "__type": { "fields": [ { "name": "address", "description": "Get seller’s …" }, …
  13. Схема это слепок состояния вашего сервиса !19 Можно хранить схему

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

    подсветки и автодополнения Валидировать свои запросы по схеме CI / ваш любимый git hook
  15. !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 }
  16. !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": “[email protected]”, "online": true } }
  17. 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}` } }
  18. persisted-queries !32 { "query": "query ViewerInfo { viewer: { name

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

    id этого запроса { "id": 1, "variables": {} } Генерировать id, отправлять его на сервер. { "errors": [ {"message": "Give me the document!"} ] } Если сервер не знает такого запроса, он просит тело { "id": 1, "query": "query { viewer: { name } }", "variables": {} }
  20. 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 }
  21. 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 }
  22. Динамическая генерация запросов !42 const batchMutate = ids => `

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

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

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

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

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

    • Прикручиваем Swagger • Боремся с overfething за счет query-параметров • Придумываем что-то c underfetching • Придумываем что-то с сокетами
  28. Хорошо когда GraphQL отражает бизнес-логику !51 query { product(id: 5)

    { title quantity } } "data": { "product": { "title": "", "quantity": 112 } }
  29. !54 Operation-Based Fragment-based Отличные доки Хорошие доки Не всегда нужна

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

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

    "currency": "CAD", } } } Normalized Cache query GetViewer { viewer { id currency } }
  32. 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> ); }
  33. 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); });
  34. 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);