Banana Cake Popv2

Client Registry

Image

The client registries is an important tool for managing your GraphQL Clients. It provides a centralized location for clients and queries.

You can use the client registry to manage your clients and their queries. It allows you to validate your queries against the schema, ensuring that all the operations defined by a client are compatible with the current schema. This validation step is critical to prevent the execution of invalid queries that might result in runtime errors.

Understanding Clients

A client, in the context of a GraphQL API, is an entity that interacts with the API by defining and executing GraphQL operations. These operations are stored on the API as persisted queries.

What is a Persisted Query?

A persisted query is a GraphQL operation that has been sent to the server, stored, and assigned an unique identifier (hash). Instead of sending the full text of a GraphQL operation to the server for execution, clients can send the hash of the operation, reducing the amount of data transmitted over the network. This practice is particularly beneficial for mobile clients operating in environments with limited network capacity.

Persisted queries also add an extra layer of security as the server can be configured to only execute operations that have been previously stored, which prevents malicious queries. This is the cheapest and most effective way to secure your GraphQL API from potential attacks.

Image

Persisted queries can be inspected in the Operations tab.

The Role of the Client Registry

The client registry plays a crucial role in managing these persisted queries. It is used to validate the queries against the schema, ensuring that all the operations defined by a client are compatible with the current schema. This validation step is critical to prevent the execution of invalid queries that might result in runtime errors.

Additionally, the client registry is responsible for distributing the queries to the GraphQL server. It maintains a mapping of hashes to query keys, informing the server which hash corresponds to which query. This allows the server to efficiently look up and execute the appropriate query when it receives a request from a client.

Client Versions

A client can have multiple versions, with each version containing a different set of persisted queries. This versioning system allows for incremental updates and changes to the client's operations without disrupting the existing functionality. As new versions are released, they can be validated and registered with the client registry, ensuring that they are compatible with the current schema and can be executed by the server.

By managing client versions and persisted queries, the client registry helps maintain the integrity and smooth operation of your GraphQL API. It ensures that your clients and API can evolve together without breaking, contributing to a more robust and reliable system.

The number of active client versions can vary depending on the nature of the client. For instance, a website usually has one active client version per stage. However, during deployment, you might temporarily have two active versions as the new version is phased in and the old version is phased out.

On the other hand, for mobile clients, you often have multiple versions active simultaneously. This is because users may be using different versions of the app, and not all users update their apps at the same time.

Once a client version is no longer in use, it reaches its end of life. At this point, you can unpublish the client version from the client registry. This will remove its persisted queries from distribution, and they will no longer be validated against the schema.

The Operations File

In the context of GraphQL, the operations file is a structured file that holds a collection of persisted queries for a client. This file serves as a reference for the client to manage and execute specific operations against a GraphQL API.

Understanding the Format and Structure

The operations file typically adopts the JSON format as used by Relay. It comprises key-value pairs, with each pair representing a unique persisted query. The key corresponds to a hash identifier for the query, and the value is the GraphQL query string. Below is an illustrative example of an operations file (operations.json):

JSON
{
"913abc361487c481cf6015841c0eca22": "{ me { username } }",
"0e7cf2125e8eb711b470cc72c73ca77e": "{ me { id } }"
...
}

Compatibility with GraphQL Clients

Several GraphQL clients have built-in support for this Relay-style operations file format. This compatibility allows for a standardized way of handling persisted queries across different clients. For more details on how various clients implement and work with persisted queries, consider referring to their respective documentation:

Setting Up a Client Registry

To set up a client registry, first, visit eat.bananacakepop.com and sign up for an account. Next, you'll need to download and install Barista, the .NET tool used to manage your client registry. You can find more information about Barista in the Barista Documentation.

After installing Barista, create a new API either through the Bananacakepop UI or the CLI. In the UI, simply right-click the document explorer and select "New API." If you prefer using the CLI, ensure you're logged in with the command barista login, then create a new API with the command barista api create. With these steps complete, you are ready to start using the client registry.

To get the id of your API, use the command barista api list. This command will list all of your APIs, their names, and their ids. You will need the id of your API to perform most operations on the schema registry.

Using Persisted Queries

To use persisted queries, the server needs to know how to translate the hash into the corresponding GraphQL operation. This is where the client registry comes in. The client registry maintains a mapping of hashes to query keys, informing the server which hash corresponds to which query. This allows the server to efficiently look up and execute the appropriate query when it receives a request from a client.

To connect your HotChocolate server to the client registry, you need the BananaCakePop.Services NuGet package. This package contains the AddBananaCakePopServices() extension method, which can be used to configure the client registry.

To install the Banana Cake Pop services, run the following command in your project's root directory:

Bash
dotnet add package BananaCakePop.Services

After installing the package, you need to configure the services in your startup class. Below is a sample implementation in C#:

C#
public void ConfigureServices(IServiceCollection services)
{
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddBananaCakePopServices(x => // Connect to the client registry
{
x.ApiKey = "<<your-api-key>>";
x.ApiId = "QXBpCmc5NGYwZTIzNDZhZjQ0NjBmYTljNDNhZDA2ZmRkZDA2Ng==";
x.Stage = "dev";
})
.UsePersistedQueryPipeline(); // Enable the persisted query pipeline
}

Tip: Using Environment Variables

Alternatively, you can set the required values using environment variables. This method allows you to call AddBananaCakePopServices without explicitly passing parameters.

  • BCP_API_KEY maps to ApiKey
  • BCP_API_ID maps to ApiId
  • BCP_STAGE maps to Stage
C#
public void ConfigureServices(IServiceCollection services)
{
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddBananaCakePopServices() // Connect to the client registry
.UsePersistedQueryPipeline(); // Enable the persisted query pipeline
}

In this setup, the API key, ID, and stage are set through environment variables.

Block Ad-Hoc Queries

While you want to allow ad-hoc queries during development, you might want to disable them in production. This can be done by setting the OnlyAllowPersistedQueries option to true in the ModifyRequestOptions method.

C#
public void ConfigureServices(IServiceCollection services)
{
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddBananaCakePopServices() // Connect to the client registry
.ModifyRequestOptions(x => x.OnlyAllowPersistedQueries = true)
.UsePersistedQueryPipeline(); // Enable the persisted query pipeline
}

You can also customize the error message that is returned when an ad-hoc query is sent to the server.

C#
public void ConfigureServices(IServiceCollection services)
{
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddBananaCakePopServices() // Connect to the client registry
.ModifyRequestOptions(x =>
{
x.OnlyAllowPersistedQueries = true;
x.OnlyPersistedQueriesAreAllowedError = ErrorBuilder.New()
.SetMessage("Persisted queries are only allowed.")
.Build();
})
.UsePersistedQueryPipeline(); // Enable the persisted query pipeline
}

Setup the cache

You can setup a second level cache for persisted queries for improving your system's resilience and performance.

Find out more about the cache here Caching.

Integrating with Continuous Integration

Integrating the client registry into your Continuous Integration/Continuous Deployment (CI/CD) pipeline maximizes their benefits. It ensures that the clients in your API are always up-to-date and tested against potential breaking changes.

The schema and client registries work hand-in-hand to ensure the smooth functioning of your API. As you make changes to your schema, the schema registry helps manage these changes, preventing inadvertent breaking changes and preserving a history of your schemas. As you validate, upload, and publish new schemas, the client registry ensures that your clients remain compatible with these changes.

As you release new versions of your clients, the client registry helps manage these versions and the query documents associated with them. By working together, the schema and client registries help maintain the integrity of your API and the services that rely on it, ensuring that they can evolve together without breaking.

Understanding the Flow

The general flow for the client registry involves three main steps: validating the client, uploading it to the registry, and publishing it.

  1. Validate the Client: The first step takes place during your Pull Request (PR) build. Here, you validate the client against the API using barista client validate command. This ensures that the client is compatible with the API and will not break existing functionality.

  2. Upload the Client: The second step takes place during your release build. Here, you upload the client to the registry using the barista client upload command. This command requires the --tag and --api-id options. The --tag option specifies the tag for the client, and the --api-id option specifies the ID of the API to which you are uploading. This command create a new version of the client with the specified tag. The tag is a string that can be used to identify the client. It can be any string, but it is recommended to use a version number, such as v1 or v2; or a commit hash, such as a1b2c3d4e5f6g7h8i9j0k1l2m3n. The tag is used to identify the client when publishing it.

  3. Publish the Client : The third step takes place just before the release. Here, you publish the client using the barista client publish commands. This command requires the --tag and --api-id options. The --tag option specifies the tag for the client, and the --api-id option specifies the ID of the API to which you are uploading. This command publishes the client with the specified tag, making it the active version for the specified API.