Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds more documentation for BCP #6709

Merged
merged 7 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions website/src/docs/bananacakepop/v2/apis.md

This file was deleted.

175 changes: 175 additions & 0 deletions website/src/docs/bananacakepop/v2/apis/client-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
---
title: "Client Registry"
---

![Image](images/client-registry-1.png)
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](images/client-registry-2.png)
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:

- [StrawberryShake](https://chillicream.com/docs/strawberryshake/v13/performance/persisted-queries)
- [URQL](https://formidable.com/open-source/urql/docs/advanced/persisted-queries/)
- [Relay](https://relay.dev/docs/guides/persisted-queries/)

# 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](/docs/barista/v1).

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#:

```csharp
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
}
```

> **Tipp: 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`
>```csharp
>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.

```csharp
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.

```csharp
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
}
```


# 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.
Loading
Loading