Skip to content

Latest commit

History

History
363 lines (318 loc) · 8.03 KB

File metadata and controls

363 lines (318 loc) · 8.03 KB

REST

TODO

GraphQL

GraphQLязык запросов (query language) для API.

Клиент посылает запрос (query) к сервису GraphQL и получает ответ в виде JSON-объекта по указанной в запросе схеме.

Например, клиент может послать запрос.

query {
currentUser {
idusernamerole
}
}

Если сервер позволяет получить данные по заданной выше схеме, по клиенту придёт ответ, соответствующий этой схеме.

{
"data": {
"currentUser": {
"id": "1",
"username": "Notes",
"role": "Admin"
}
}
}

Запросы query и mutation

Существуют два типа запросов (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

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]
}

Взаимодействие с сервером

Отправка query

queryUsers {
users {
idusernamerole
}
}

Query с переменными

queryUser($userId: ID!) {
user(userId: $userId) {
idusernamerole
}
}

Variables

{
"userId": "auth0|72d45e398924235638341891"
}

Query с input

queryQueryLeaders($credentialsInput: Credentials) {
login(credentials: $credentialsInput) {
auth_token
}
}

Variables

{
"credentialsInput": {
"username": "admin",
"password": "admin"
}
}

Преимущества GraphQL

  • Строгая типизация. Конкретная схема, полностью описывающая, как можно работать с данными.
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
}
}
}

REST на практике

Данные в QUERY

  • Форма запроса на клиенте.
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

Данные в BODY

  • Форма запроса на клиенте.
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;});

Данные в PARAMS

  • Форма запроса на клиенте.
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