One of the aspects that can become problematic as the schema grows is related to the size of the GraphQL requests. The problem arises from:
- the size of the information transmitted over the HTTP protocol
- Worsened server-side performance due to the time it takes to parse and validate the queries sent
Hotchocolate (like other GraphQL implementations) allows to overcome this problem with two types of solutions:
- Persisted queries: the mechanism is to save the queries at "build-time": in fact it involves configuring the client and server builds in order to extract graphQL queries from clients and replace them with keys of research; these search keys are stored on the server side and associated with queries that are pre-compiled/parsed/validated only once. This system is very efficient, but it requires you to complicate your build pipelines and is based on the prerequisite of managing your clients.
- Automatic Persisted queries: evolution of the previous mechanism that allows you to manage the saving of queries at run-time
Details and logic can be deepened in the Hotchocolate documentation, but the setup is very simple:
builder.Services
.AddLogging()
.AddMemoryCache()
.AddSha256DocumentHashProvider(HashFormat.Hex)
...
// Automatic Persisted Queries
.UseAutomaticPersistedQueryPipeline()
.AddInMemoryQueryStorage()
The client can send its own query either by going from the GET method or from POST; the choice of which method to use can depend on several factors (size of queries, ability to take advantage of HTTP caching, etc.). In the ex. shown below shows usage with both methods:
with GET
https://localhost:xxxx/graphql?query={publishers(first:5){nodes{id}}}&extensions={"persistedQuery":{"version":1,"sha256Hash":"dfb01045d5a6b78e01b03eb110feb095ece66073391040ce472f074fea912343"}}
with POST:
{
"query":"{publishers(first:5){nodes{id}}}",
"extensions":{
"persistedQuery":{
"version":1,
"sha256Hash":"dfb01045d5a6b78e01b03eb110feb095ece66073391040ce472f074fea912343"
}
}
}
the result of the request is the following, in which the server indicates to the client that the query has been stored and the key can be subsequently reused to retrieve the persisted query:
{
"data": {
"publishers": {
"nodes": [
{
"id": "UHVibGlzaGVyCmRjMzU0ODZlOS00ZTFlLTQ1YmYtYTMyZC1mODg0MjMxMWQxYjM="
},
{
"id": "UHVibGlzaGVyCmQ0MDdkOTcxOC05MDljLTQ1MjQtOTA5MS0yMzk1ZWM2NDZjNTI="
},
{
"id": "UHVibGlzaGVyCmQxOTE3ZTJjZi00NDJhLTRhMmYtYTdjNS1iOGQ1YmQ2NDVkMGQ="
},
{
"id": "UHVibGlzaGVyCmRlODhlYzZlNi0wZTMzLTRkOTEtYTY3MC00MzE5ZmI3MTdkZTQ="
},
{
"id": "UHVibGlzaGVyCmQ4ZDYxY2U0OS0xYjY5LTQxN2ItYjNjMS1mZDhhNzE2OTAyNTA="
}
]
}
},
"extensions": {
"persistedQuery": {
"sha256Hash": "dfb01045d5a6b78e01b03eb110feb095ece66073391040ce472f074fea912343",
"persisted": true
}
}
}
At this point, subsequent calls to the query can be simplified as follows:
with GET
https://localhost:xxxx/graphql?extensions={"persistedQuery":{"version":1,"sha256Hash":"dfb01045d5a6b78e01b03eb110feb095ece66073391040ce472f074fea912343"}}
with POST
{
"extensions":{
"persistedQuery":{
"version":1,
"sha256Hash":"dfb01045d5a6b78e01b03eb110feb095ece66073391040ce472f074fea912343"
}
}
}