После беглого просмотра документации Graphql я начал реализовывать его в проекте игрушечных рельсов / reactJS. Проекты позволяют пользователю войти в систему через devise, а затем получить доступ к маршруту фиктивного / исполнителя, который отображает список художников. Кажется, все работает нормально, пока я не попытаюсь использовать данные api с помощью GraphQL из приложения реакции, чтобы привлечь художников и отобразить их.
На стороне сервера у меня есть graphql_controller.rb, например:
class GraphqlController < ApiController
rescue_from Errors::Unauthorized do |exception|
render json: {errors: ['Unauthorized.']}, status: :unauthorized
end
def index
result = Schema.execute params[:query], variables: params[:variables], context: context
render json: result, status: result['errors'] ? 422 : 200
end
private
def context
token, options = ActionController::HttpAuthentication::Token.token_and_options request
{
ip_address: request.remote_ip
}
end
end
Затем, следуя логике моей модели, я настроил graphql в разделе graph / со следующими файлами:
график / запросы / artist_query.rb
ArtistQuery = GraphQL::ObjectType.define do
name 'ArtistQuery'
description 'The query root for this schema'
field :artists, types[Types::ArtistType] do
resolve(->(_, _, _) {
Artist.all
})
end
end
типы / artist_type.rb
Types::ArtistType = GraphQL::ObjectType.define do
name 'Artist'
description 'A single artist.'
field :id, !types.ID
field :name, types.String
field :description, types.String
end
schema.rb
Schema = GraphQL::Schema.define do
query ArtistQuery
end
На стороне клиента, чтобы все было организовано, я использую 3 файла для рендеринга этого списка исполнителей:
Во-первых, ArtistSchema.js
import { gql } from 'react-apollo';
const artistListQuery = gql`
{
query {
artists {
id
name
description
}
}
}
`;
export default artistListQuery;
Затем Artist.js
import React, { Component } from 'react';
class Artist extends Component {
render() {
return (
<tr>
<td>{this.props.index + 1}</td>
<td>{this.props.data.name}</td>
<td>{this.props.data.description} min</td>
</tr>
);
}
}
export default Artist;
И, наконец, объединение этих двух вместе в более крупный макет: Artists.jsx:
import React, { Component } from 'react';
import {graphql} from 'react-apollo';
import Artist from './Artist';
import artistListQuery from './ArtistSchema';
class Artists extends Component {
render() {
if (this.props.data.loading) {
return (<p>Loading</p>)
} else {
console.info(this.props.data)
const ArtistsItems = this.props.data.artists.map((data,i) => {
return (<Artist key = {i} index = {i} data = {data}></Artist>);
});
return (
<div>
<h1>Artists</h1>
<table className = "table table-striped table">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{ ArtistsItems }
</tbody>
</table>
</div>
);
}
}
}
export default graphql(artistListQuery)(Artists);
Что происходит при выполнении этого кода:
На стороне сервера (извините за неформатированный вывод, но в консоли он отображается так):
Processing by GraphqlController#index as */*
18:49:46 api.1 | Parameters: {"query"=>"{\n query {\n artists {\n id\n name\n description\n __typename\n }\n __typename\n }\n}\n", "operationName"=>nil, "graphql"=>{"query"=>"{\n query {\n artists {\n id\n name\n description\n __typename\n }\n __typename\n }\n}\n", "operationName"=>nil}}
Далее следует ошибка:
Completed 422 Unprocessable Entity in 36ms (Views: 0.2ms | ActiveRecord: 0.0ms)
На стороне клиента, если я отслеживаю Network> Response для graphql, я (конечно) получаю код ошибки 422 и следующее сообщение об ошибке:
{"errors":[{"message":"Field 'query' doesn't exist on type 'ArtistQuery'","locations":[{"line":2,"column":3}],"fields":["query","query"]}]}
Я предполагаю, что мой запрос выполнен неправильно. Я пробовал различные форматы запросов (из примеров документации или gists), но не могу найти правильный способ вернуть данные моего исполнителя.
Что я делаю неправильно?





Отправленный вами запрос GQL имеет неправильный формат, он запрашивает поле query корневого объекта Query. Используйте вместо этого:
const artistListQuery = gql`
query UseTheNameYouWantHere {
artists {
id
name
description
}
}
`;
Кстати, вы можете добавить гем graphiql (https://github.com/rmosolgo/graphiql-rails), чтобы получить игровую площадку с вашим GraphQL API.
Ладно, проблема не в самом коде. Я изменил запрос на тот, который вы посоветовали, и обновил имена художников на что-то другое как на стороне клиента, так и на стороне сервера. Кажется, что apollo-rails кэширует предыдущие вызовы api, даже если они не удались, поэтому я всегда получал одну и ту же ошибку. Мне еще предстоит выяснить, как очистить этот кеш. Спасибо за ваше время.
Я не думаю, что это проблема в данном конкретном случае, но я получал эту ошибку, и она оказалась из-за простой синтаксической ошибки. Атрибуты запроса должны быть в формате camelCase, а не в формате подчеркивания. Может быть, это поможет кому-то еще, кто попадет сюда при поиске этой ошибки, как это сделал я.
Ооо, я провел час или больше, не понимая, почему мое второе возвращенное поле сработало ... вот оно что. Дох. Спасибо
Угу, это убивало меня больше часа. Спасибо вам большое за размещение этого!
У меня была эта ошибка в RSpec, но не при реальном вызове, т.е. Postman, для того же запроса. Решением было изменить запрос в спецификации с
<<~QUERY
query users {
posts {
...
}
}
QUERY
к
<<~QUERY
query users {
users {
posts {
...
}
}
}
QUERY
Однако в других спецификациях первый подход работал с похожей мутацией, поэтому я не уверен, что было не так.
В моем случае проблема заключалась в том, что у меня была такая структура:
module Types
class SomeType < Types::BaseObject
field :comparator,
Types::ComparatorType
field :options,
[Types::OptionType]
end
end
НО в запросе у меня была другая вложенная структура под названием данные, о которой я забыл:
mutation {
myMutation(
...
) {
someType {
comparator {
...
}
data {
options {
...
}
}
}
}
}
Итак, после изменения класса SomeType, чтобы добавить этот недостающий ключ, я решил мою проблему. Итак, теперь это выглядит так:
module Types
class SomeType < Types::BaseObject
field :comparator,
Types::ComparatorType
field :data,
Types::DataType
end
end
# New file
module Types
class DataType < Types::BaseObject
field :options,
[Types::OptionType]
end
end
Большое спасибо за ваш ответ, но я пробовал это раньше, то, что вы мне советуете, приводит к ошибке на стороне клиента, например:
apollo.umd.js:1409 Uncaught Error: Encountered a sub-selection on the query, but the store doesn't have an object reference. This should never happen during normal use unless you have custom code that is directly manipulating the store; please file an issue.Сервер даже не получает запрос, когда я использую предоставленный вами код запроса. Что касается клиента graphiql, предоставляемого игрой, у меня нет к нему доступа, потому что мое приложение представляет собой api без представлений.