.NET Tools
Essential productivity kit for .NET and game developers
Building a GraphQL Client in .NET with JetBrains Rider and StrawberryShake
Many teams are using GraphQL for their API needs, thanks to its powerful query language and flexibility. Unlike traditional REST APIs, GraphQL lets you fetch the data you need in a single request, making your applications more efficient and responsive.
However, it can be daunting getting started creating schemas, writing queries, and integrating GraphQL into your .NET application. Using JetBrains Rider’s GraphQL plugin and the StrawberryShake framework, building an application that consumes a GraphQL API is not that hard.
In this blog post, we’ll discuss setting up your environment, defining schemas, generating client code, and seamlessly integrating it all into a sample project. Here we go!
What is GraphQL?
Before we dig into building a sample application, let’s quickly recap GraphQL and its benefits. GraphQL, developed by Facebook in 2012 and open-sourced in 2015, is a query language for APIs and a runtime for executing queries based on a user-defined type system.
If you have been a .NET developer for a while, you may have already worked with OData. Conceptually, GraphQL is similar in that it lets you request exactly the data you need from an API, eliminating the typical over-fetching or under-fetching issues you may see in REST APIs. For example, in a typical REST API, you may want to retrieve customer data and either need to make a second request to get their address details or always get the address details in the response, even when they’re not always required..
Unlike most REST APIs, a GraphQL API exposes a single endpoint you can query for any data you need. The endpoint can define several operations, all of which are self-documenting through a schema. A schema provides a comprehensive and queryable description of the API structure, types, and available operations. The schema also defines types and relationships between them, ensuring clients know exactly what data is available and receive helpful error messages when things go wrong.
There are a few concepts to keep in mind throughout this post:
- Queries – These are used to fetch data from the server, similar to GET requests in REST, but much more powerful thanks to their flexibility in fetching nested and related data in a single request.
- Subscriptions – These are used to subscribe to data from the server, and return new data or updates from the server when new data is available.
- Mutations – Used to modify data on the server (e.g., creating, updating, deleting records), similar to
POST
,PUT
, orDELETE
inREST
. - Schema – defines the types and relationships in your API, specifying what queries and mutations are available. It ensures both the client and the server agree on the data structure and types.
One more thing to cover: when implementing a GraphQL API, you have two transport protocols to consider for data exchange: HTTP and WebSockets. HTTP is typically used for traditional query and mutation operations, where clients send HTTP GET
and POST
requests to the GraphQL server and receive responses containing the requested data. This method is straightforward and works well for most cases where data changes are infrequent or do not need to be immediately reflected on the client.
On the other hand, WebSockets are used for real-time applications requiring instant updates, such as live chat, online gaming, or real-time dashboards. GraphQL subscriptions use WebSockets to maintain a persistent connection between the client and the server, enabling the server to push updates to the client as soon as data changes occur. This reduces latency and provides a more responsive user experience compared to polling mechanisms often used with HTTP.
Choosing between HTTP and WebSockets depends on your application’s specific requirements. For most standard operations, HTTP is sufficient and simpler to implement. However, for real-time data updates, integrating WebSockets with GraphQL subscriptions can significantly enhance your application’s interactivity and responsiveness. Also, both transports can be used in conjunction when building solutions. In this post, we’ll use an HTTP endpoint as our example to work with.
Note: If you want to learn more about how REST, GraphQL, and gRPC compare, check out this talk from JetBrains .NET Days Online by Poornima Nayar.
What is StrawberryShake?
Now, let’s talk about StrawberryShake. This open-source .NET library simplifies building GraphQL clients by generating strongly-typed client-side code from your GraphQL schema and queries. This means less boilerplate and more focus on what matters – your application logic.
It uses source generators to generate all the client-side code based on the GraphQL schema you define or introspect from a GraphQL API.
Prerequisites
Let’s transition into a practical example. First, let’s make sure you have the GraphQL plugin installed in JetBrains Rider. This plugin is necessary for providing syntax highlighting, validation, and auto-completion for GraphQL schemas and queries, streamlining your development process.
Open JetBrains Rider and bring up the Settings/Preferences menu. Go to Plugins in the left-hand sidebar, and search for “GraphQL” in the Marketplace tab. Click Install to add the plugin to your Rider setup. You may need to restart Rider to activate the plugin.
We’re ready to jump into creating our project and setting everything up in JetBrains Rider. Let’s get to work!
Creating the project in Rider
Let’s look at what we’ll build first. Since we’re focusing on building a GraphQL client, we’ll need an API to interact with. We’re in luck, because there are plenty of GraphQL APIs available. In this blog post, we’ll use Trevor Blades’ Countries GraphQL API, which provides data about countries around the world. The sample application will be a page that shows information about the countries included in that API, and a way to get more details for each.
The countries API endpoint is available at https://countries.trevorblades.com/, which exposes a playground where you can test your queries against it.
Note: Michael Staib, founder of ChilliCream (the company that also builds the StrawberryShake project we use in this blog post) was a guest in one of our livestreams to talk about building a GraphQL server with ASP.NET Core and HotChocolate.
In JetBrains Rider, create a new Web App project using the Razor Pages template. Give it a name, such as the very original “Countries” I used here:
While not required, for this sample, I removed some of the default CSS in the project and included PicoCSS – a great small CSS framework that makes any web page look nice. Here’s my updated _Layout.cshtml
:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="color-scheme" content="light dark"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@@picocss/pico@2/css/pico.min.css" /> <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> <title>@ViewData["Title"] - Countries</title> </head> <body> <main class="container"> <header> <nav> <ul> <li><strong>Countries</strong></li> </ul> <ul> <li><a href="/">Index</a></li> </ul> </nav> </header> @RenderBody() </main> <script src="~/js/site.js" asp-append-version="true"></script> @await RenderSectionAsync("Scripts", required: false) <script type="module" defer> import { polyfillCountryFlagEmojis } from "https://cdn.skypack.dev/country-flag-emoji-polyfill"; polyfillCountryFlagEmojis(); </script> </body> </html>
You can also clone a copy of this project from GitHub, and explore individual commits if you want to follow along.
Working with GraphQL in Rider
Time for GraphQL! In your C# project, use the context menu to add a new GraphQL Config file and paste the following contents into it:
schema: schema.graphql extensions: endpoints: production: url: https://countries.trevorblades.com/graphql/ headers: Authorization: Bearer ${TOKEN} documents: '**/*.graphql'
This graphql.config.yml
file will be used by Rider to access the schema for the countries API, and generate queries that can be executed inside the IDE. This configuration file also defines one or more endpoints, and potential headers required to access an endpoint, such as authorization headers. The countries API doesn’t require authentication, but we’ll see in a second why I added it here anyways.
In the left-hand gutter, right next to the production endpoint, you’ll see a Run icon:
Click it to run an introspection of the remote schema. When you do, you’ll see a popup that lets you enter an authorization token. You can skip it, but this is why I added the ${TOKEN}
placeholder earlier: to show you how to add variables into the GraphQL configuration.
When finished, a schema.graphql
file is automatically added to your project, listing all the types, fields, and operations exposed by the countries API. Note the toolbar at the top of the editor: you can switch to a different endpoint if you have e.g. a staging and production GraphQL API to work with, and there’s a Refresh button to re-run schema introspection and update the types, fields, and operations in the API.
The toolbar also contains a Run button, which is not very useful yet. Let’s change that! In our application, I’d like to show a list of all countries with their country code, name, and flag image. Add a new file called listcountries.graphql
, and paste the following query into it:
query ListCountries { countries { code name emoji } }
This snippet defines a GraphQL query (named “ListCountries”) that retrieves information from a collection on the API endpoint called “countries”. This query returns an array of Country elements, and each contains the code, name, and emoji. Note that you can Cmd+Click or Ctrl+Click on the name to navigate to the schema’s definition of available data and collections.
Now is a good time to try the Run button in the editor. You’ll see Rider opens a tool window showing the query result:
While we’re creating queries in Rider, let’s add one more file: getcountry.graphql
, and paste the following contents into it:
uery GetCountry($countryCode: ID!) { country(code: $countryCode) { code name emoji capital currency languages { code name } ...Continent } } fragment Continent on Country { continent { code name } }
This query will be used in our sample application to display details for a specific country. The query is named GetCountry
, and requires a parameter $countryCode
. It then requests data from the GraphQL API, requesting a country (by $countryCode
) and returning various fields of information from it.
Another interesting bit in this query is that we’re not only fetching the country data but also its related continent. If you do not need that data, you can keep it out of the query and get a smaller response payload size. You will also see the fields queried for a continent are defined as a fragment
. This is a way to make your GraphQL queries a bit more maintainable, as including the Continent
on any query that works with Country
now only means adding ...Continent
and not the complete list of fields. This is not a requirement, and you can also write the entire query like this:
query GetCountry($countryCode: ID!) { country(code: $countryCode) { code name emoji capital currency languages { code name } continent { code name } } }
With those two queries defined, let’s continue building our ASP.NET Core application.
Adding StrawberryShake GraphQL source generation
To start generating the C# client for the countries GraphQL schema we just queried, you will need to add a NuGet package reference to StrawberryShake.Server
. While named “server”, this package includes additional dependencies. to get you started with creating a GraphQL client.
StrawberryShake also has a command line tool that can help set up code generation, but we’re not going to use that in this blog post.
Next, add a file named .graphqlrc.json
next to your graphql.config.yml
. Paste the following contents into it:
{ "schema": "schema.graphql", "documents": "**/*.graphql", "extensions": { "strawberryShake": { "name": "CountriesClient", "namespace": "Countries.GraphQL", "url": "https://countries.trevorblades.com/graphql/", "dependencyInjection": true, "strictSchemaValidation": true, "emitGeneratedCode": true, "records": { "inputs": false, "entities": false }, "transportProfiles": [ { "default": "Http", "subscription": "WebSocket" } ] } } }
This file looks similar to the graphql.config.yml
file created earlier, but this time, it configures how StrawberryShake should generate C# code from the GraphQL API. It includes the API endpoint URL, the name of the generated client class (CountriesClient
), its namespace, and whether to generate convenience extension methods to register the GraphQL client with ASP.NET Core’s service collection.
If you now build your solution, you should see a new type ICountriesClient
you can work with, as well as an AddCountriesClient()
extension method on IServiceCollection
.
In Program.cs
, you can use this extension method to wire up the generated GraphQL client:
// ... var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddHttpClient(); builder.Services .AddCountriesClient() .ConfigureHttpClient( configureClient: client => { client.BaseAddress = new Uri("https://countries.trevorblades.com/graphql/"); // client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse($"Bearer {Constants.ApiKey}"); }); var app = builder.Build(); // ...
The call to AddCountriesClient()
will register all necessary services with ASP.NET Core. You will also need a call to ConfigureApiClient()
to configure the GraphQL API endpoint URL. I left a comment in the code here showing how you would add bearer token authorization headers for authentication.
We’re ready to consume the GraphQL queries we defined earlier! In Index.cshtml.cs
, add a constructor parameter that accepts an ICountriesClient
and assign it to a field, or use a primary constructor.
public class IndexModel(ICountriesClient countriesClient) : PageModel { // ... }
In the OnGet
method, you can query the GraphQL endpoint and retrieve a list of countries. Here’s the full code for the IndexModel
class that is used to render the page:
public class IndexModel(ICountriesClient countriesClient) : PageModel { public IReadOnlyCollection<IListCountries_Countries> Countries { get; set; } public async Task OnGet() { var countries = await countriesClient.ListCountries.ExecuteAsync(); if (countries.IsErrorResult()) { // ... } Countries = countries.Data?.Countries ?? new List<IListCountries_Countries>(0); } }
You’ll notice the countriesClient
has a ListCountries
property on which you can call ExecuteAsync()
to fetch data. StrawberryShake generates this method, and the method may accept parameters as well. If you try calling the GetCountry
method (which is generated based on the GetCountry
query we defined in the previous section of this post), you’ll see the countryCode
parameter can be passed in:
The ExecuteAsync()
method always returns an object that contains information about the response. If an error is returned, you can check for it and log or otherwise handle the exception. If the response is successful, the Data
property will contain an object representing the object defined in the *.graphql
file earlier. In this example application, you can pass the data on to the Razor view.
In the Index.cshtml
view, you can now consume the list of countries, and display each in a table:
@page @model IndexModel @{ ViewData["Title"] = "Countries"; } <h1>Countries</h1> <table> <thead> <tr> <th scope="col">Country code</th> <th scope="col">Name</th> <th scope="col">Emoji</th> </tr> </thead> <tbody> @foreach (var country in Model.Countries) { <tr> <td>@country.Code</td> <td><a asp-page="Country" asp-route-countryCode="@country.Code">@country.Name</a></td> <td>@country.Emoji</td> </tr> } </tbody> </table>
Try running it in your browser, and you should see a similar page to this:
Congratulations! You have successfully built an application that fetches data from a GraphQL API in C#. As a next step, try implementing the Details.cshtml
page yourself (or check out the GitHub repository for this sample application to look at implementation details).
Conclusion
In this post, we’ve covered how to build a GraphQL client in .NET using JetBrains Rider and StrawberryShake. We’ve covered quite a bit of ground – from setting up your development environment and understanding the fundamentals of GraphQL, to defining schemas and queries, and finally integrating everything into a sample project.
Using JetBrains Rider’s GraphQL plugin and the StrawberryShake framework, we’ve seen how to create strongly-typed GraphQL clients and how to work with them in your C# projects.
I encourage you to try this out on your projects. Experiment with different schemas and queries, and see how you can integrate GraphQL to make your applications more efficient and responsive. JetBrains Rider, with its rich set of features and plugins, is a fantastic IDE for .NET development, and combined with StrawberryShake, you have a powerful GraphQL toolkit.
Feel free to share your experiences and any cool projects you build. And if you run into any issues or have questions, let us know in the comments. Happy coding!