0

Работает ли Apollo Client на Node.js?

15

Я ищу библиотеку GraphQL-клиента, которая должна работать на Node.js для проведения тестирования и создания некоторого мэшапа данных — не в производственной среде. Везде, где я использую Apollo, например, в <code>react-apollo</code> и graphql-server-express от Apollo. Мои требования довольно просты.

Является ли <code>apollo-client</code> подходящим выбором? Я не могу найти примеров или документации по его использованию на Node.js — если у вас есть такая информация, пожалуйста, поделитесь.

Или, возможно, мне стоит/я могу использовать референсный GraphQL-клиент на Node?

4 ответ(ов)

0

Если вы ищете эквивалентный код на JavaScript, вот пример, который использует Apollo Client для выполнения мутации GraphQL:

require('dotenv').config();
const gql = require('graphql-tag');
const ApolloClient = require('apollo-boost').ApolloClient;
const fetch = require('cross-fetch/polyfill').fetch;
const createHttpLink = require('apollo-link-http').createHttpLink;
const InMemoryCache = require('apollo-cache-inmemory').InMemoryCache;

const client = new ApolloClient({
    link: createHttpLink({
        uri: process.env.API,
        fetch: fetch
    }),
    cache: new InMemoryCache()
});

client.mutate({
    mutation: gql`
    mutation popJob {
        popJob {
            id
            type
            param
            status
            progress
            creation_date
            expiration_date
        }
    }
    `,
}).then(job => {
    console.log(job);
}).catch(error => {
    console.error(error);
});

Этот код сначала загружает переменные окружения из файла .env с помощью dotenv, а затем устанавливает Apollo Client с использованием HTTP-ссылки на указанный API-адрес. Далее он выполняет мутацию popJob и выводит результат в консоль. Не забудьте обработать возможные ошибки с помощью .catch, чтобы избежать возникновения необработанных исключений.

0

Вот простая реализация на Node.js с использованием GraphQL.

Клиент 'graphiql' вполне подходит для разработки.

  1. Установите зависимости с помощью npm install.
  2. Запустите сервер командой node server.js.
  3. Откройте в браузере адрес http://localhost:8080/graphiql для доступа к клиенту graphiql.

server.js

var graphql = require('graphql').graphql;
var express = require('express');
var graphQLHTTP = require('express-graphql');

var Schema = require('./schema');

// Это просто внутренний тест
var query = 'query{starwar{name, gender, mass}}';
graphql(Schema, query).then(function(result) {  
  console.log(JSON.stringify(result, null, " "));
});

var app = express()  
  .use('/', graphQLHTTP({ schema: Schema, pretty: true, graphiql: true }))
  .listen(8080, function(err) {
    console.log('GraphQL сервер теперь работает на localhost:8080');
  });

schema.js

// schema.js
var graphql = require('graphql');
var http = require('http');

var StarWar = [  
  { 
    "name": "default",
    "gender": "default",
    "mass": "default"
  }
];

var TodoType = new graphql.GraphQLObjectType({  
  name: 'starwar',
  fields: function() {
    return {
      name: {
        type: graphql.GraphQLString
      },
      gender: {
        type: graphql.GraphQLString
      },
      mass: {
        type: graphql.GraphQLString
      }
    }
  }
});

var QueryType = new graphql.GraphQLObjectType({  
  name: 'Query',
  fields: function() {
    return {
      starwar: {
        type: new graphql.GraphQLList(TodoType),
        resolve: function() {
          return new Promise(function(resolve, reject) {
            var request = http.get({
              hostname: 'swapi.dev',
              path: '/api/people/1/',
              method: 'GET'
            }, function(res) {
              res.setEncoding('utf8');
              res.on('data', function(response) {
                StarWar = [JSON.parse(response)];
                resolve(StarWar);

                console.log('Успешный ответ:', StarWar);
              });
            });

            request.on('error', function(response) {
              console.log('Произошла ошибка:', response.message);
            });

            request.end();                      
          });
        }
      }
    }
  }
});

module.exports = new graphql.GraphQLSchema({  
  query: QueryType
});

Пожалуйста, обратите внимание, что в schema.js я обновил адрес API с swapi.co на swapi.dev, так как оригинальный endpoint может быть недоступен.

0

В ответ на комментарий @YakirNa:

Я не могу говорить о других потребностях, которые я описал, но я провел довольно много тестирования. В итоге я сделал все свои тесты в процессе выполнения.

Большинство тестов сводится к тестированию резольверов, которое я осуществляю с помощью инструмента (jig), который вызывает функцию graphql библиотекой graphql с тестовым запросом и затем проверяет ответ.

У меня также есть (почти) уровень энд-ту-энд тестирования, который работает на уровне обработки HTTP в Express. Он создает фиктивный HTTP-запрос и проверяет ответ в процессе. Все это происходит в процессе сервера; ничего не передается по сети. Я использую это не очень часто, в основном для тестирования аутентификации JWT и другого поведения на уровне запросов, которое не зависит от тела запроса GraphQL.

0

Я столкнулся с вашей же проблемой, поскольку хотел создать промежуточный сервис для подготовки данных из GraphQL для финального фронтенд-приложения с целью:

  • оптимизированного представления данных (и стандартизированного интерфейса выходных данных)
  • более быстрого времени отклика

Предполагая, что сервер GraphQL предоставляется внешним провайдером, у меня не было возможности управлять моделью данных напрямую через GQL.

Я не хотел реализовывать Apollo Client для GraphQL непосредственно в таких фронтенд-фреймворках, как React, Angular или Vue.js... Вместо этого я хотел управлять запросами через Node.js на бэкенде REST API.

Вот класс-обертка для Apollo Client, который мне удалось собрать (с использованием TypeScript):

import ApolloClient from "apollo-client";
import { ApolloLink } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import fetch from 'node-fetch';
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import introspectionQueryResultData from '../../fragmentTypes.json';
import { AppConfig } from 'app-config';

const config: AppConfig = require('../../../appConfig.js');

export class GraphQLQueryClient {
    protected apolloClient: any;

    constructor(headers: { [name: string]: string }) {
        const api: any = {
            spaceId: config.app.spaceId,
            environmentId: config.app.environmentId,
            uri: config.app.uri,
            cdnApiPreviewToken: config.cdnApiPreviewToken,
        };

        const ACCESS_TOKEN = api.cdnApiPreviewToken;
        const uri = api.uri;

        console.log(`Настройка Apollo клиента для запроса к uri: ${uri}`);

        const fragmentMatcher = new IntrospectionFragmentMatcher({
            introspectionQueryResultData
        });

        this.apolloClient = new ApolloClient({
            link: ApolloLink.from([
                onError(({ graphQLErrors, networkError }:any) => {
                    if (graphQLErrors) {
                        graphQLErrors.map((el:any) =>
                            console.warn(
                                el.message || el
                            )
                        );
                        graphQLErrors.map(({ message, locations, path }:any) =>
                            console.warn(
                                `[GraphQL ошибка - Env ${api.environmentId}]: Сообщение: ${message}, Местоположение: ${JSON.stringify(locations)}, Путь: ${path}`
                            )
                        );
                    }
                    if (networkError) console.log(`[Сетевая ошибка]: ${networkError}`);
                }),
                new HttpLink({
                    uri,
                    credentials: 'same-origin',
                    headers: {
                        Authorization: `Bearer ${ACCESS_TOKEN}`
                    },
                    fetch
                })
            ]),
            cache: new InMemoryCache({ fragmentMatcher }),
            defaultOptions: {
                watchQuery: {
                    fetchPolicy: 'network-only',
                    errorPolicy: 'ignore',
                },
                query: {
                    fetchPolicy: 'network-only',
                    errorPolicy: 'all',
                },
            }
        });
    }
}

После этого конструктора я выполняю запросы следующим образом:

let response = await this.apolloClient.query({ query: gql`${query}` });

Как вы могли заметить:

  • Мне нужно было внедрить fetch в HttpLink
  • Я настроил заголовки авторизации для доступа к конечной точке GraphQL внешнего провайдера
  • Я использовал IntrospectionFragmentMatcher, чтобы использовать фрагменты в своих запросах, вместе с созданием типа схемы ("fragmentTypes.json" с инициализационным скриптом)

Публикую это, чтобы поделиться своим опытом и, возможно, предоставить более подробную информацию по вашему вопросу. Также буду рад комментариям и предложениям по улучшению этой обертки.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь