Introduction to GraphQL
GraphQL is a query language for APIs that allows clients to ask for exactly what they need and nothing more.
The examples on this page are for teaching the GraphQL syntax only. For runnable, OMS-specific examples see the Guides section and the schema reference.
Queries
A GraphQL query is basically a list of specific fields on objects.
{
brand {
name
}
}
{
"data": {
"brand": {
"name": "Brand name"
}
}
}
You can see immediately that the query has exactly the same shape as the result. This is essential to GraphQL, because you always get back what you expect, and the server knows exactly what fields the client is asking for.
In the first example the field we requested was a simple String, but fields can also refer to Objects, all you need to specify is a subselection of their fields.
GraphQL queries can traverse related objects and their fields, letting clients fetch lots of related data in one request, instead of making several roundtrips as one would need in a classic REST architecture.
This is the core principle of GraphQL — the client decides the shape of the response — and it applies to the whole OMS API. For example, a single orders query can return the orders together with their full detail and their documents: there is no need for a listing call followed by one call per order, unless you prefer that pattern. The practical limit is the query complexity of the request: richer and deeper queries cost more points.
{
# example of a comment
order {
ready
tickets {
url
}
}
}
{
"data": {
"order": {
"ready": true,
"tickets": [
{
"url": "http://ticket-url-1.com/"
},
{
"url": "http://ticket-url-2.com/"
}
]
}
}
}
Arguments
Queries can also accept arguments that are used to select and calculate the results that we receive in the response.
{
order(id: "2940819") {
ready
tickets {
url
}
}
}
{
"data": {
"order": {
"ready": true,
"tickets": []
}
}
}
Fragments
In GraphQL you sometimes need to fetch the same subselection of fields from a specific object many times in different queries, that's where fragments come in handy. A fragment is simply a reusable set of fields bound to a specific Object type.
{
oneHundredthUser: user(id: "100") {
...lightUser
}
oneHundredAndFirstUser: user(id: "101") {
...lightUser
}
}
fragment lightUser on User {
email
name
role
}
{
"data": {
"oneHundredthUser": {
"email": "user1@user.com",
"name": "User 100",
"role": "brand"
},
"oneHundredAndFirstUser": {
"email": "user2@user.com",
"name": "User 101",
"role": "default"
}
}
}
In this example we used another interesting GraphQL feature, you can give an alias to the same query repeated with different parameters and get all the results in one single fetch.
Complete Syntax
In all the examples shown up to now, we have omitted the query keyword and the query name, but in production apps it's useful to use these to make our code less ambiguous.
So let's rewrite the first query.
query brand {
brand(id: "100") {
name
}
}
{
"data": {
"brand": {
"name": "Brand name"
}
}
}
Variables
You might have noticed that in the last example we pass the query argument as a hard-coded value, but in most applications we need the arguments to be dynamic, that's where GraphQL variables become useful.
So while writing our query we replace the static arguments with dynamic variables ($variable).
- We declare the
$variableand itstypeas an input in the query. - We pass the
$variableas an argument to the backend query.
query brand($id: ID!) {
brand(id: $id) {
name
}
}
# variables
{
"id": "100"
}
{
"data": {
"brand": {
"name": "Brand name"
}
}
}
Mutations
Up to now we have only focused on data fetching, let's talk about server-side data updates. In GraphQL any operation that causes data writes is sent via a mutation.
Just like queries, mutations can fetch specific fields, usually to be aware of the new state after an update.
mutation orderConfirm($id: ID!) {
orderConfirm(id: $id) {
ready
}
}
# variables
{
"id": "312515"
}
{
"data": {
"orderConfirm": {
"ready": true
}
}
}
Although they might look similar, there is one important difference between a query and a mutation.
While query fields are executed in parallel, mutation fields run in series, one after the other.
This means that if we send two mutations that act on the same state, the first is guaranteed to finish before the second begins, avoiding a race condition.