http://localhost:5000/notes?limit=10&sortBy=name
⇡ ⇡ ⇡ ⇡ ⇡
scheme host port path query
POST /notes HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Content-Type: application/json; charset=utf-8
Host: localhost:8080
User-Agent: HTTPie/0.9.3
{
"name": "films",
"text": "Films to watch"
}
HTTP/1.1 200 OK
Content-Length: 67
Content-Type: application/json; charset=utf-8
Date: Wed, 16 Mar 2016 14:32:18 GMT
X-Powered-By: Express
{
"createdAt": 1458138738899,
"name": "films",
"text": "Films to watch"
}
GET получение ресурса
POST создание ресурса
DELETE удаление ресурса
200 Ok 301 Moved Permanently
201 Created 304 Not modified
204 No content
400 Bad request 500 Internal Server Error
401 Unauthorized 504 Gateway Timeout
403 Forbidden
404 Not found
Сам не хранит состояние клиента между запросами, всё состояние целиком описывается в каждом запросе
Notes.find(name)
⇡ ⇡
method arguments
Запрос
{
"jsonrpc": "2.0",
"id": 1,
"method": "findNote",
"params": ["films"]
}
Ответ
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"name": "films",
"text": "..."
}
}
REpresentational State Transfer
Запрос
GET /notes/films HTTP/1.1
Ответ
HTTP/1.1 200 Ok
Content-Type: application/json
{
"name": "films",
"text": "..."
}
service NotesService {
rpc Find (NoteIdRequest) returns (Note) {}
}
message Note {
string name = 1;
string text = 2;
}
message NoteIdRequest {
string name = 1;
}
const { notes as NotesService } = grpc.load('notes.proto');
const client = new NotesService('localhost:50051');
client.find({ name: 'films' }, (error, note) => {});
type Note {
name: String!
text: String
}
type Query {
note(name: String!): Note
}
POST /graphql
Content-Type: application/json
{
"query": "query { note(name: 'films') { name, text }"
}
HTTP/1.1 200 Ok
Content-Type: application/json
{
"data": { "note": {"name": "films", "text": "..."} },
"errors": [ ... ]
}
const socket = new WebSocket('ws://localhost:8080');
socket.send(JSON.stringify({
id: '1',
method: 'findNote',
params: ['films']
});
socket.onmessage = message => {
const { id, method, params } = JSON.parse(message);
// ...
socket.send({ id, result });
});
Получает состояние ресурса в одном из представлений (JSON, XML, HTML)
GET /notes
GET /notes/films
GET /notes/films/pinned
GET /notes?limit=10
200 Ok
404 Not found
400 Bad request /notes?limit=muahahaha
Создаёт новый ресурс с начальным состоянием, когда мы не знаем его ID
POST /notes
201 Created
409 Conflict
Создаёт новый ресурс с начальным состоянием, когда мы знаем его ID
PUT /notes/films
PUT /notes/films/pinned
200 Ok
204 No content
Обновляет состояние существующего ресурса целиком
PUT /notes/films
PUT /notes/films/pinned
200 Ok
204 No content
404 Not found
Удаляет существующий ресурс
DELETE /notes/films
DELETE /notes/films/pinned
200 Ok
204 No content
404 Not found
Обновляет состояние существующего ресурса частично
PATCH /notes/films
200 Ok
204 No content
404 Not found
Запрашивает заголовки, чтобы проверить существование ресурса
HEAD /notes/films
200 Ok
404 Not found
Запрашивает правила взаимодействия, например, доступные методы
OPTIONS /search
204 No content
Allow: OPTIONS, GET, HEAD
POST /search
405 Method not allowed
Один и тот же запрос приводит к одному и тому же состоянию
GET – да (не модифицирующий)
OPTIONS – да (не модифицирующий)
HEAD – да (не модифицирующий)
POST – нет
PUT – да
DELETE – да
PATCH – нет
const promise = fetch(url[, options]);
{
method: 'POST',
headers: {
'Accept': 'application/json'
},
body: JSON.stringify({
id: 1,
name: 'films'
})
}
fetch('/notes')
.then(res => {
res.headers.get('Content-Type'); // application/json
res.status; // 200
return res.json();
})
.then(notes => {
console.info(notes);
})
.catch(error => {
console.error(error);
});
const controller = new AbortController();
const signal = controller.signal;
fetch('/notes', { signal }); // pending
signal.abort();
Язык запросов к API, а так же среда исполнения для этих запросов
query {
note(name: "Books") {
name
text
comments {
text
author {
name
}
}
}
}
ID, Int, Float, String, Boolean
|
|
|
|
type Admin {
id: ID
name: String
accessLevel: String
}
type NormalUser {
id: ID
name: String
age: Int
}
union User = Admin | NormalUser
|
|
|
|
|
|
query NotesQuery {
firstNote: note(name: "Books") {
text
}
secondNote: note(name: "Films") {
text
}
}
fragment NoteFields on Note {
id
name
text
}
query {
firstNote: note(name: "Books") {
...NoteFields
}
secondNote: note(name: "Films") {
...NoteFields
}
}
query {
users {
__typename
... on Admin {
name
accessLevel
}
... on NormalUser {
name
age
}
}
}
query NoteQuery($name: String!) {
note(name: $name) {
name
text
}
}
// "variables"
{ "name": "Books" }
query NoteQuery($name: String!, $withComments: Boolean!) {
note(name: $name) {
name
text
comments @include(if: $withComments) {
text
}
}
}
query NoteQuery($name: String!, $withoutComments: Boolean!) {
note(name: $name) {
name
text
comments @skip(if: $withoutComments) {
text
}
}
}
mutation CreateNote($name: String!, $text: String!) {
createNote(name: $name, text: $text) {
name
text
}
}
|
|
| Lokka | Максимально простой в использовании. Базовая поддержка запросов и мутаций. Простейшее кэширование |
| Apollo | Более гибкий. Хороший баланс между функциональностью и сложностью использования |
| Relay | Наиболее функциональный, из-за чего наиболее сложный в использовании. Много внимания уделено производительности (особенно на мобильных). |