Sometimes, we want to add a new item to a collection. For example, to create a new article in a blog. Let's see how we would tackle this in each API style.
Many authors distinguish two types of repositories in REST: collections and stores. Let's see each one of them.
Adding an item to a collection is a unsafe and non-idempotent operation. It's not idempotent because the identifier for the new resource is chosen by the server, so running the same operation twice will generate two resources. For this, we have the POST
method.
Typically, to create a document resource in a collection, the resource we operate into is the collection itself, so that the combination POST
plus collection works as a factory method. For the sake of simplicity, let's consider that /articles
is the URI of the collection.
The entity body of the HTTP request will contain the resource to be added. This resource (the article) might be represented in any way, for example in JSON. This Media Type should be specified in the entity header of the request. Some servers may consider the value of the optional header Slug
(Slug
is defined in the Atom (RFC 5023)) to generate the identifier (URI).
The response, if successful, would be a 201 Created
. Typically, two things are included:
- A redirection to the new resource. This redirection is expressed through the
Location
header. - A representation of the new resource. The resource will be added to the entity body; the
Content-Location
header will identify the URI of the returned resource; and theContent-Type
will specify the Media Type used for the resource.
Example request:
POST /articles HTTP/1.1
Content-Type: application/json
Slug: first-article
{
'title': 'This is my first post',
'description': 'This is the beginning of a beautiful friendship'
}
Response:
HTTP/1.1 201 Created
Content-Type: application/json
Location: /articles/first-post
Content-Location: /articles/first-post
{
'title': 'This is my first post',
'description': 'This is the beginning of a beautiful friendship'
}
Stores are repositories of documents. But unlike collections, this process is client-driven, i.e. the identifier is provided by the client. We can use a resource of type store to upload binary resources, for example.
Adding a resource to a store is unsafe and idempotent, so PUT
will be used instead of POST
. The identifier will be the one provided by the client to run the HTTP request, for example /products/computer
.
The rest of the request will behave as a regular POST
request to add a resource in a collection.
PUT /products/computer HTTP/1.1
Content-Type: application/json
{
'name': 'Personal Computer',
'price': 500
}
As with any other unsafe operations, in GraphQL we will use a regular mutator. It's a best practice to return an object as a result of a mutation:
type Mutation {
createArticle(article:ArticleInput): Article
}
There are some usage cases where idempotency is a convenient mechanism to enhance our API. For example, to make it resilient to some failures. To provide this, several strategies are available, like requiring a unique key in each mutation.
- GitHub GraphQL API the
createProject
mutation returns aProject
.
Following the resource-oriented design, a Create
method will be defined for each resource that can be created.
service Blog {
rpc CreateArticle(CreateArticleRequest)
returns (Article);
}
Optionally, the request resource may contain a client-assigned id, similarly to REST stores. If the resource exists:
- The server might fail, returning a
ALREADY_EXISTS
error message. - The server may also assign a new resource identifier.
To create a new entry in our example application, we will use a collection. Run:
curl -v -H "Content-Type: application/json" \
-d '{ "title": "This is my first post", "description": "This is the beginning of a beautiful friendship"}' \
http://localhost:4000/articles
The above request will return a Location header. To fetch the entry created above, run (updating the identifier):
curl -v --request GET http://localhost:4000/articles/5fa5694ce5f5657b361d7cfe
To create a new article
, run:
mutation {
createArticle(article: {
title:"This is the title",
description:"This is the description"
}) {
id
}
}
The above request will return the id of the newly created resource. To fetch the resource, you can run:
query {
article(id:"5fa9941a8067e43ad0d79e88") {
id
title
description
}
}
The rpc
to create a new article is as follows:
service Blog {
rpc CreateArticle(CreateArticleRequest)
returns (Article);
}
message CreateArticleRequest {
Article article = 1;
}
We can create a new article using the client application, npm run grpcc
, and then:
client.createArticle({article:{title:"New article", description:"This is the description"}}, pr)
The newly-created article will be returned.