$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. GraphQL:
    Понимаем, объясняем, внедряем
    !1

    View Slide

  2. !2

    View Slide

  3. !3

    View Slide

  4. !4

    View Slide

  5. !5

    View Slide

  6. !6

    View Slide

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

    View Slide

  8. GQL service
    Client
    Document
    Query
    Mutation
    Subscription
    Response
    Data
    Errors
    Validate
    Schema
    Execute
    Первый взгляд
    !8

    View Slide

  9. У нас есть схема.
    Запросы валидируются по схеме
    автоматически
    Если в результате выполнения мы
    сломаемся, нам об этом сообщат :)
    !9
    Надёжность

    View Slide

  10. !10
    Нет привязки к транспорту
    Использует GraphQL на этапе сборки

    View Slide

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

    View Slide

  12. WebSocket часто выбирают транспортом подписок
    (Subscriptions)
    !12
    Подчиняем сокеты

    View Slide

  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

    View Slide

  14. Данные удобно представлять в виде графа
    Видно не только сущности но и связи между ними
    !14
    Понятная структура данных

    View Slide

  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"
    }
    }
    }
    name={site.name}
    code={site.code}
    />

    View Slide

  16. Интроспекция
    !16
    {
    __schema {
    directives {
    name
    }
    }
    __type(name: "Query"){
    fields {
    name
    description
    }
    }
    }
    {
    "data": {
    "__schema": {
    "directives": [

    {
    "name": "deprecated"
    }
    ]
    },
    "__type": {
    "fields": [
    {
    "name": "address",
    "description": "Get seller’s …"
    },

    View Slide

  17. Интроспекция + Типизация = Крутые доки
    !17

    View Slide

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

    View Slide

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

    View Slide

  20. Что можно делать со схемой:
    !20
    Стянуть в IDE для подсветки и автодополнения
    Валидировать свои запросы по схеме
    CI / ваш любимый git hook

    View Slide

  21. Codegen
    !21
    apollo-cli
    graphql-code-generator
    Можно генерировать типы (ts / flow)
    …или тесты

    View Slide

  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
    }

    View Slide

  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": “[email protected]”,
    "online": true
    }
    }

    View Slide

  24. !24
    Запросы прозрачней для разработчика
    Можно связывать кусочки
    интерфейса с операцией
    …или фрагментом

    View Slide

  25. Например так
    !25
    {
    site {
    code
    name
    active
    }
    }

    View Slide

  26. Тонкая настройка
    !26
    Можно настраивать доступы для
    каждого поля
    Можно решать как кешировать
    каждое поле

    View Slide

  27. Объясняем.
    !27

    View Slide

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

    View Slide

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

    View Slide

  30. !30
    GraphQL ломает кеширование

    View Slide

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

    View Slide

  32. persisted-queries
    !32
    {
    "query": "query ViewerInfo { viewer: { name } }",
    "variables": {}
    }
    id: 1
    {
    "id": 1,
    "variables": {}
    }
    Вместо тела запроса отправляем ID

    View Slide

  33. persisted-queries: чуть сложнее
    !33
    Мы отправляем тело.
    Теперь сервер запомнил
    id этого запроса
    {
    "id": 1,
    "variables": {}
    }
    Генерировать id,
    отправлять его на сервер.
    {
    "errors": [
    {"message": "Give me the document!"}
    ]
    }
    Если сервер не знает
    такого запроса, он просит
    тело
    {
    "id": 1,
    "query": "query { viewer: { name } }",
    "variables": {}
    }

    View Slide

  34. !34
    GraphQL запросы слишком большие!

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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
    }

    View Slide

  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
    }

    View Slide

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

    View Slide

  43. Динамическая генерация запросов
    !43
    query {
    viewer: *
    }
    const getAllFields => async (fieldName) => {
    const fieldInfo = await introspect(fieldName);
    const fields = getAllFields(fieldInfo);
    return `query {
    viewer: {
    ${fields.join('\n')}
    }
    }`
    }

    View Slide

  44. Можно добавлять свои скалярные типы
    44
    scalar Time
    Нужно будет написать функцию проверки

    View Slide

  45. Можно добавлять свои директивы
    44
    directive @dryRun(
    env: String = "development"
    ) on FIELD_DEFINITION
    mutation {
    destroyYourDatabase @dryRun {
    products {
    id
    title
    }
    }
    }

    View Slide

  46. Схему можно генерировать
    !46
    Например вот так (graphQL.js):
    const queryType = new GraphQLObjectType({
    name: 'Query',
    fields: {
    cheburachka: {
    type: GraphQLString,
    resolve: function() {
    return 'cheburachka';
    }
    }
    }
    });
    const schema = new GraphQLSchema({query: queryType});

    View Slide

  47. GraphQL vs REST
    !47
    Надо сравнивать аккуратно
    GraphQL - query language + execution engine. Есть спека
    REST - архитектурный стиль
    OpenAPI (aka Swagger) - как описывать RESTful API

    View Slide

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

    View Slide

  49. А REST так тоже можно научить!
    !49

    View Slide

  50. !50
    GraphQL is DB over HTTP

    View Slide

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

    View Slide

  52. Внедряем.
    !52

    View Slide

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

    View Slide

  54. !54
    Operation-Based Fragment-based
    Отличные доки Хорошие доки
    Не всегда нужна схема Нужна схема
    React, RN, Vue, Angular …. React, RN
    Свой компилятор
    Развивается быстрее
    Больше оптимизаций из коробки
    Есть GC

    View Slide

  55. !55


    query GetViewer {
    viewer {
    id
    currency
    }
    }

    Normalized
    Cache
    {
    "data": {
    "viewer": {
    "id": "1",
    "currency": "CAD",
    }
    }
    }

    View Slide

  56. !56


    {
    "data": {
    "viewer": {
    "id": "1",
    "currency": "CAD",
    }
    }
    }
    Normalized
    Cache
    query GetViewer {
    viewer {
    id
    currency
    }
    }

    View Slide

  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 (

    {loading ?
    Loading :
    Currency: {data.viewer.currency}}

    );
    }

    View Slide

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

    View Slide

  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);

    View Slide

  60. Что почитать?
    !60
    evl.ms/blog
    graphql.org (docs + spec)

    View Slide

  61. Спасибо!
    !61
    @polina_gurtovaya
    @pgurtovaya
    [email protected]

    View Slide