TODO
GraphQL — язык запросов (query language) для API.
Клиент посылает запрос (query) к сервису GraphQL и получает ответ в виде JSON-объекта по указанной в запросе схеме.
Например, клиент может послать запрос.
query {
currentUser {
idusernamerole
}
}Если сервер позволяет получить данные по заданной выше схеме, по клиенту придёт ответ, соответствующий этой схеме.
{
"data": {
"currentUser": {
"id": "1",
"username": "Notes",
"role": "Admin"
}
}
}Существуют два типа запросов (requests): query и mutation.
Запрос query отвечает за получение данных, он не может их изменять (аналог GET-запроса в REST).
query {
users {
idusername
}
}Запрос mutation отвечает за изменение данных (объединяет в себе возможности POST, PUT, DELETE и других в REST).
mutation {
logOut
}Все поля, которые может получить клиент, должны быть описаны в типе (type).
Типы Query и Mutation являются типами по умолчанию. В них должны быть описаны все возможные запросы.
typeQuery {
currentUser: User
}
typeMutation {
logOut
}schema {
query: Querymutation: Mutation
}Могут создаваться пользовательские типы.
typeUser {
id: IDusername: Stringrole: Stringimage: String
}Если схема в запросе с клиента не удовлетворяет схеме на сервере, то возникнет валидационная ошибка. Например, поле age отсутствует в типе User.
{
"data": null,
"errors": [
{
"...": "...",
"message": "Validation error of type FieldUndefined: Field 'age' in type 'User' is undefined
}
]
}GraphQL не позволяет создавать динамические объекты в качестве type.
Примеры динамических объектов.
constcommentsMap={123: {id: "123",message: "Hello"},456: {id: "456",message: "Hi"}};constreportsInfoMap={spam: ["id1","id2"],flood: [],bullying: ["id3"]}constflags={isReviewed: true,isVisited: true,/* ... */}Есть два решения, как это можно обойти
- Передавать объекты текстовый как JSON и указывать встроенный тип
String(не лучшая валидация) или подключить библиотеку, которая имеет скалярный тип JSON (например, эту). Например, AWS AppSync имеет встроенный типAWSJSON.
typeRes {
commentsMap: StringreportsMap: JSONflags: AWSJSON
}- Представить объекты в виде массивов (предпочтительный вариант).
typeComment {
id: IDmessage: String
}
typeReportsInfo {
name: Stringvalues: [ID]
}
typeFlag {
name: StringvalueBoolean
}
typeRes {
commentsMap: [Comment]
reportsMap: [ReportsInfo]
flags: [Flag]
}queryUsers {
users {
idusernamerole
}
}queryUser($userId: ID!) {
user(userId: $userId) {
idusernamerole
}
}Variables
{
"userId": "auth0|72d45e398924235638341891"
}queryQueryLeaders($credentialsInput: Credentials) {
login(credentials: $credentialsInput) {
auth_token
}
}Variables
{
"credentialsInput": {
"username": "admin",
"password": "admin"
}
}- Строгая типизация. Конкретная схема, полностью описывающая, как можно работать с данными.
typeUser {
id: ID!username: String!
}
typeArticle {
id: ID!title: String!description: Stringauthor: User!
}
typeQuery {
articles: [Article]
article(id: ID!): Article
}- Клиент всегда запрашивает только то, что ему нужно. Он не может получить те поля, которые не запрашивал, ровно как и те, которые не предусмотрены описанной схемой.
Следующий запрос вернёт articles, содержащие только поля id и title. Все эти поля должны присутствовать в схеме запроса на бэкенде (описана выше), иначе будет ошибка.
query {
articles {
idtitle
}
}- Один запрос может объединять в себе несколько различных ресурсов.
Следующий запрос объединяет в себе информацию, собранную из Articles и Users.
query {
articles {
idtitledescriptionauthor {
idusername
}
}
}- Форма запроса на клиенте.
GET /route?field=value&anotherField=anotherValue/* axios */axios.get('/route',{params: {field: 'value',anotherField: 'anotherValue',},});- Форма запроса на сервере.
GET /route- Получение данных из запроса на сервере.
/* Express */app.get('/route',(request,response)=>{const{ field, anotherField }=request.query;});Передача массива arr [1, 3, 7] с клиента.
GET /route?arr[]=1&arr[]=3&arr[]=7Передача объекта obj { foo: '1', bar: '7' } с клиента.
GET /route?obj[foo]=1&obj[bar]=7- Форма запроса на клиенте.
POST /routeContent-Type: application/json
{ "field": "value", "anotherField": "anotherValue" }/* axios */axios.post('/route',{field: 'value',anotherField: 'anotherValue',});- Форма запроса на сервере.
POST /route- Получение данных из запроса на сервере.
/* Express */app.post('/route',(request,response)=>{const{ field, anotherField }=request.body;});- Форма запроса на клиенте.
GET /route/paramValue/* axios */axios.get(`/route/${paramValue}`);- Форма запроса на сервере.
GET /route/:param- Получение данных из запроса на сервере.
/* Express */app.post('/route/:param',(request,response)=>{const{ param }=request.params;});Пример отправки нескольких параметров.
PATCH /users/17/namePATCH /users/:id/:field