title | description | ms.topic | ms.date | ms.custom |
---|---|---|---|---|
include file graphql-basics.md |
include file graphql-basics.md |
include |
07/20/2021 |
devx-track-js, devx-graphql |
GraphQL provides a query language that allows you to ask for data from a server in a declarative way. You can ask for:
- The specific data you need, in the schema you need it. Changes to the data schema are done by the client in the schema definition for the API.
- Returning the data in a nested schema representing a collection of objects, regardless of how many data sources are required. Contrast this with a typical REST API that needs several API requests to provide the same data.
GraphQL acts as a layer between the API endpoint and the database. GraphQL providers, such as Apollo, provide much of the functionality you need to build your GraphQL APIs. Like most software that uses databases, the provider can't write the actual database queries for you, because only you know your data. The providers do usually generate much of the boilerplate code to use GraphQL, so you are left with just your business or middleware logic.
A GraphQL query, asking for the value hello
from the server, looks like JSON but isn't a true JSON object:
{
hello
}
The server responds with JSON:
{
"hello":"Hello from GraphQL backend"
}
In GraphQL, you perform read operations against data by using queries, and write operations, such as inserts and updates, by using mutations.
Suppose you have a data source that contains messages with an ID, an author, and the content of each message.
To query for all messages, your GraphQL query looks like:
{
getMessages {
id
content
author
}
}
Your API endpoint might look like: /api/graphql
, and the cURL request might look like:
curl -X POST 'http://localhost:7071/api/graphql' \
-H 'content-type: application/json' \
--data-raw '{"query":"{ getMessages { id content author } }"}'
The API response looks like:
{
"data": {
"getMessages": [
{
"id": "d8732ed5-26d8-4975-98a5-8923e320a77f",
"author": "dina",
"content": "good morning"
},
{
"id": "33febdf6-e618-4884-ae4d-90827280d2b2",
"author": "john",
"content": "oh happy day"
}
]
}
}
Although the previous example returned every message and every field within a message, there might be times when the client knows it only wants certain fields. This doesn't require any new code for the API, but does require a new query from the client, describing the schema of the expected response.
Here's a query that gets all messages, but only the id
and author
fields of them. The query tells the GraphQL server not to send the values for content
to the client:
{
getMessages {
id
author
}
}
Your API endpoint might look like: /api/graphql
, and the cURL request might look like:
curl -X POST 'http://localhost:7071/api/graphql' \
-H 'content-type: application/json' \
--data-raw '{"query":"{ getMessages { id author } }"}'
The API response looks like:
{
"data": {
"getMessages": [
{
"id": "d8732ed5-26d8-4975-98a5-8923e320a77f",
"author": "dina"
},
{
"id": "33febdf6-e618-4884-ae4d-90827280d2b2",
"author": "john"
}
]
}
}
To change the data, use a mutation that defines the change, and also defines what data to return from the change. Suppose you have a data source that contains messages with an ID, an author, and the content of each message, and you want to add a new message.
To add a new message, your GraphQL mutation looks like:
mutation {
createMessage(input: { author: "John Doe", content: "Oh happy day" }) {
id
}
}
Notice that the last curly brace section, { id }
, describes the schema the client wants in the response.
Your API endpoint might look like: /api/graphql
, and the cURL request might look like:
curl 'http://localhost:7071/api/graphql' \
-X POST \
-H 'Content-Type: application/json' \
--data-raw '{"query": "mutation{ createMessage(input: { author: \"John Doe\", content: \"Oh happy day\" }){ id } }"}'
The API response looks like:
{
"data": {
"createMessage": {
"id":"7f1413ec-4ffa-45bc-bce2-583072745d84"
}
}
}
The preceding query hard-coded the values of the author
and content
. This method isn't recommended, but is used here to illustrate where the values are expected on the request. Now, you can change the same mutation request to allow variables, and allow the client making the request to inject the appropriate values.
To pass variables, send them in the variables
property. Describe them in the mutation parameters with the $
, and a type that matches what the mutation expects, such as String!
. Then assign them to the mutation arguments as required.
{
"variables": { "author": "jimbob", "content": "sunny in the `ham" },
"query": "mutation ($author: String!, $content: String!) { createMessage(input: { author: $author, content: $content }){ id }}"
}
The following request body, --data-raw
value, is stripped of all formatting.
curl 'http://localhost:7071/api/graphql' \
-X POST \
-H 'Content-Type: application/json' \
--data-raw '{"variables": { "author": "jimbob", "content": "sunny in the `ham" },"query": "mutation ($author: String!, $content: String!){ createMessage(input: { author: $author, content: $content }){ id } }"}'