Работает ли Apollo Client на Node.js?
Я ищу библиотеку GraphQL-клиента, которая должна работать на Node.js для проведения тестирования и создания некоторого мэшапа данных — не в производственной среде. Везде, где я использую Apollo, например, в <code>react-apollo</code>
и graphql-server-express
от Apollo. Мои требования довольно просты.
Является ли <code>apollo-client</code>
подходящим выбором? Я не могу найти примеров или документации по его использованию на Node.js — если у вас есть такая информация, пожалуйста, поделитесь.
Или, возможно, мне стоит/я могу использовать референсный GraphQL-клиент на Node?
4 ответ(ов)
Если вы ищете эквивалентный код на 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
, чтобы избежать возникновения необработанных исключений.
Вот простая реализация на Node.js с использованием GraphQL.
Клиент 'graphiql' вполне подходит для разработки.
- Установите зависимости с помощью
npm install
. - Запустите сервер командой
node server.js
. - Откройте в браузере адрес
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 может быть недоступен.
В ответ на комментарий @YakirNa:
Я не могу говорить о других потребностях, которые я описал, но я провел довольно много тестирования. В итоге я сделал все свои тесты в процессе выполнения.
Большинство тестов сводится к тестированию резольверов, которое я осуществляю с помощью инструмента (jig), который вызывает функцию graphql
библиотекой graphql с тестовым запросом и затем проверяет ответ.
У меня также есть (почти) уровень энд-ту-энд тестирования, который работает на уровне обработки HTTP в Express. Он создает фиктивный HTTP-запрос и проверяет ответ в процессе. Все это происходит в процессе сервера; ничего не передается по сети. Я использую это не очень часто, в основном для тестирования аутентификации JWT и другого поведения на уровне запросов, которое не зависит от тела запроса GraphQL.
Я столкнулся с вашей же проблемой, поскольку хотел создать промежуточный сервис для подготовки данных из 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" с инициализационным скриптом)
Публикую это, чтобы поделиться своим опытом и, возможно, предоставить более подробную информацию по вашему вопросу. Также буду рад комментариям и предложениям по улучшению этой обертки.
Использование async/await с циклом forEach
Как получить полный объект в console.log() Node.js, а не '[Object]'?
Обнаружена ошибка: Невозможное нарушение: Неверный тип элемента: ожидался строковый тип (для встроенных компонентов) или класс/функция, но получен объект
Как остановить Babel от трансформации 'this' в 'undefined' и добавления "use strict"
Использование IndexRoute в react-router v4 с React