Налаштування GraphQL Monitoring (Apollo Studio / GraphQL Hive)

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Налаштування GraphQL Monitoring (Apollo Studio / GraphQL Hive)
Середня
від 1 робочого дня до 3 робочих днів
Часті питання

Наші компетенції:

Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Моніторинг GraphQL через Apollo Studio та альтернативи

GraphQL-моніторинг відрізняється від REST: недостатньо дивитися на ендпоїнт /graphql—потрібно бачити продуктивність кожної операції, поля та resolver окремо. Apollo Studio, GraphQL Hive та self-hosted OpenTelemetry-рішення надають цей рівень деталізації.

Apollo Studio (GraphOS)

// Установка
npm install @apollo/server @apollo/server/plugin/usageReporting

import { ApolloServer } from '@apollo/server'
import { ApolloServerPluginUsageReporting } from '@apollo/server/plugin/usageReporting'

const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [
    ApolloServerPluginUsageReporting({
      // APOLLO_KEY з переменних середовища
      sendVariableValues: {
        // Не відправляти чутливі змінні
        exceptNames: ['password', 'token', 'secret']
      },
      sendHeaders: {
        exceptNames: ['Authorization', 'Cookie']
      },
      // Вибірка трасувань (100% дорого, 10% достатньо для аналізу)
      fieldLevelInstrumentation: 0.1
    })
  ]
})

Apollo Studio надає:

  • Розбивку p50/p95/p99 latency по операціях
  • Field usage—які поля реально використовують клієнти (допомагає видаляти deprecated)
  • Schema checks—порівняння змін схеми з реальними клієнтськими запитами
  • Сповіщення про деградацію продуктивності

GraphQL Hive (Self-Hosted альтернатива)

# Docker Compose для GraphQL Hive
docker run -d \
  -e HIVE_TOKEN=your-token \
  -e TARGET=your-org/your-project/production \
  ghcr.io/kamilkisiela/graphql-hive/cli:latest
import { useHive } from '@graphql-hive/client'
import { envelop, useSchema } from '@envelop/core'

const getEnveloped = envelop({
  plugins: [
    useSchema(schema),
    useHive({
      enabled: true,
      token: process.env.HIVE_TOKEN,
      usage: {
        sampleRate: 1.0,
        exclude: ['IntrospectionQuery']
      },
      reporting: {
        author: 'CI Pipeline',
        commit: process.env.GIT_SHA
      }
    })
  ]
})

OpenTelemetry трасування резолверів

Для self-hosted моніторингу з Jaeger/Tempo:

import { ApolloServer } from '@apollo/server'
import { trace, SpanStatusCode } from '@opentelemetry/api'

// Плагін для трасування кожного resolver
const tracingPlugin = {
  async requestDidStart({ request, contextValue }) {
    const tracer = trace.getTracer('graphql')
    const operationName = request.operationName || 'anonymous'

    const span = tracer.startSpan(`graphql.operation`, {
      attributes: {
        'graphql.operation.name': operationName,
        'graphql.operation.type': 'query',
      }
    })

    return {
      async executionDidStart() {
        return {
          willResolveField({ info }) {
            const fieldSpan = tracer.startSpan(
              `graphql.resolve.${info.parentType.name}.${info.fieldName}`,
              { parent: span }
            )

            return (error) => {
              if (error) {
                fieldSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message })
              }
              fieldSpan.end()
            }
          }
        }
      },

      async willSendResponse({ response }) {
        if (response.body.kind === 'single' && response.body.singleResult.errors) {
          span.setStatus({ code: SpanStatusCode.ERROR })
        }
        span.end()
      }
    }
  }
}

Prometheus метрики операцій

import { Counter, Histogram, register } from 'prom-client'

const gqlOperationDuration = new Histogram({
  name: 'graphql_operation_duration_seconds',
  help: 'GraphQL operation execution time',
  labelNames: ['operation_name', 'operation_type', 'status'],
  buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5]
})

const gqlFieldResolveDuration = new Histogram({
  name: 'graphql_field_resolve_duration_seconds',
  help: 'GraphQL field resolver execution time',
  labelNames: ['type_name', 'field_name'],
  buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5]
})

const gqlErrors = new Counter({
  name: 'graphql_errors_total',
  help: 'GraphQL errors count',
  labelNames: ['operation_name', 'error_code']
})

const metricsPlugin = {
  async requestDidStart({ request }) {
    const startTime = Date.now()
    const operationName = request.operationName || 'anonymous'

    return {
      async executionDidStart() {
        return {
          willResolveField({ info }) {
            const fieldStart = Date.now()
            return () => {
              gqlFieldResolveDuration
                .labels(info.parentType.name, info.fieldName)
                .observe((Date.now() - fieldStart) / 1000)
            }
          }
        }
      },

      async willSendResponse({ response }) {
        const duration = (Date.now() - startTime) / 1000
        const hasErrors = response.body?.singleResult?.errors?.length > 0

        gqlOperationDuration
          .labels(operationName, 'query', hasErrors ? 'error' : 'success')
          .observe(duration)

        if (hasErrors) {
          for (const err of response.body.singleResult.errors) {
            gqlErrors.labels(operationName, err.extensions?.code || 'UNKNOWN').inc()
          }
        }
      }
    }
  }
}

Grafana дашборд запитів

# grafana/dashboards/graphql.json (фрагмент panels)
panels:
  - title: "Top Slow Operations (p95)"
    type: table
    targets:
      - expr: |
          topk(10,
            histogram_quantile(0.95,
              rate(graphql_operation_duration_seconds_bucket[5m])
            ) by (operation_name)
          )

  - title: "Error Rate by Operation"
    type: timeseries
    targets:
      - expr: |
          rate(graphql_errors_total[5m]) by (operation_name)

  - title: "Slowest Resolvers (p99)"
    type: table
    targets:
      - expr: |
          topk(20,
            histogram_quantile(0.99,
              rate(graphql_field_resolve_duration_seconds_bucket[5m])
            ) by (type_name, field_name)
          )

Schema Change Alerts

// Автоматична перевірка зворотної сумісності при деплої
// rover schema check (Apollo) або @graphql-inspector/core
import { diff, CriticalityLevel } from '@graphql-inspector/core'

async function checkSchemaCompatibility(oldSchema, newSchema) {
  const changes = await diff(oldSchema, newSchema)
  const breaking = changes.filter(
    c => c.criticality.level === CriticalityLevel.Breaking
  )

  if (breaking.length > 0) {
    console.error('Breaking schema changes detected:')
    breaking.forEach(c => console.error(`  - ${c.message}`))
    process.exit(1)
  }
}

Терміни

Налаштування GraphQL моніторингу з Apollo Studio або OpenTelemetry + Prometheus/Grafana—1–2 робочих дні.