GraphQL is a query language that lets clients request precisely the data they need, offering an efficient alternative to traditional REST APIs. By batching multiple queries, GraphQL can fetch nested and related data in a single network request, optimizing bandwidth usage and improving performance.
Integrating GraphQL with RavenDB allows your application to make fewer requests and transfer only the required data, enhancing efficiency. You can easily fetch necessary data from RavenDB and other data sources by performing queries in a unified manner using GraphQL syntax.
What you’ll learn
In this article, you will learn how to use GraphQL to access data stored in RavenDB with the usage of Hot Chocolate running as a stand-alone ASP.NET Core GraphQL server.
Setup
Let’s see how to set up the ASP.NET Core project with the Hot Chocolate, an open-source GraphQL server for the Microsoft .NET platform, having data integration support for RavenDB. This integration will translate paging, filtering, sorting and projections in GraphQL syntax into native RavenDB queries.
RavenDB server
Download the RavenDB server and run it locally by following the setup wizard. For the purpose of this demo we’ll be using Unsecured mode so the server will be available at http://localhost:8080 in the default configuration.
Next, let’s create a database named Northwind. We will utilize built-in RavenDB’s Sample Data set to showcase the GraphQL queries against RavenDB. So let’s deploy it to our database directly from the RavenDB Studio via Tasks > Create Sample Data.
Please note Show C# classes option. It will give you C# classes for all entities used in the Sample Data set. You can just copy it to your web project that we’ll create in the next steps.
Create a new ASP.NET Core project
Now it’s time to start building up the web project. The below command will create a new directory called “GraphQL.RavenDB.Demo” containing your web application project’s files:
> dotnet new web -n GraphQL.RavenDB.Demo
Install Hot Chocolate GraphQL package
The following package includes everything what is needed to get your GraphQL server up and running:
> cd .\GraphQL.RavenDB.Demo\
> dotnet add package HotChocolate.AspNetCore
Install the RavenDB provider to your project
In order to be able to execute queries against RavenDB database you need add RavenDB integration package:
> dotnet add package HotChocolate.Data.Raven
Configure GraphQL server with RavenDB support
You can now open the project file in your favorite IDE. We’ll utilize the just added Hot Chocolate packages to implement GraphQL server which will be able to handle queries that will be relayed to RavenDB server under the covers.
Starting the GraphQL server
Open the Program.cs file in the root of the project. Currently, it’s set up to return a simple “Hello World!” message from the root of where the server is running (e.g. https://localhost:7196).
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
In order to add the Hot Chocolate GraphQL server to our web app’s services we need to call:
builder.Services.AddGraphQLServer();
Next let’s replace the MapGet() function with MapGraphQL(). This function adds a GraphQL endpoint to the endpoint configurations, which means our GraphQL server will be available at /graphql.
The code should looks like this:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGraphQLServer();
var app = builder.Build();
app.MapGraphQL();
app.Run();
Now you can run the project and navigate to /graphql endpoint (e.g. https://localhost:7196/graphql). You’ll get Banana Cake Pop UI. The built-in Hot Chocolate’s tool for developers.
Although you’ll see some red alerts complaining about issues with the schema. We’ll take care of that in the next steps.
Configuring Document Store
Before we define the GraphQL schema, we need to configure the GraphQL server to use RavenDB as the data source. This involves adding an IDocumentStore instance to the services in the Program.cs file, which will establish the connection to the RavenDB server.
To do this, add the following line to the Program.cs file:
builder.Services.AddSingleton<IDocumentStore>(
_ => new DocumentStore {
Urls = new[] { "http://localhost:8080" },
Database = "Northwind"
}.Initialize());
This line configures the dependency injection container to provide a singleton instance of IDocumentStore. It connects to the RavenDB server running at http://localhost:8080 and sets the default database to “Northwind”. By calling Initialize(), we ensure the connection to RavenDB is ready to use.
With this configuration in place, we can access RavenDB in subsequent steps to fetch and manipulate data through our GraphQL server.
Schema
The GraphQL server needs a schema which defines types and their fields that are available for querying using GraphQL API. The schema can be defined with the usage of few approaches:
- schema-first,
- code-first,
- annotation-based.
In this demo we’ll go with the code-first approach.
Data types
As stated above we’ll be querying the built-in RavenDB’s Northwind sample database. The Show C# classes option mentioned earlier returns definitions of all types used in that data set. Let’s copy it to the Types folder, created in the root of our project, as NorthwindTypes.cs file.
using System;
using System.Collections.Generic;
using Raven.Client.Documents.Session.TimeSeries;
namespace Orders;
public sealed class Company
{
public string Id { get; set; }
public string ExternalId { get; set; }
public string Name { get; set; }
// redacted - Get the full code from the "Show C# Classes" in the sample data windows
Query type
The Query type in GraphQL defines all the possible queries that clients can execute to fetch data from the GraphQL server. It acts as the entry point to the GraphQL schema.
public class Query
{
}
To expose data through the GraphQL server, we need to implement resolvers in this Query class. Resolvers are functions that fetch the data for specific fields in the schema. When a query is executed, GraphQL traverses the query tree and calls the corresponding resolver for each field.
Implementing Resolvers
Our resolvers will retrieve data from the RavenDB database so the implementation will use RavenDB’s session instance and simply call Query() on the relevant collections.
public class Query
{
[UsePaging]
[UseProjection]
[UseFiltering]
[UseSorting]
public IRavenQueryable<Order> GetOrders(IAsyncDocumentSession session)
=> session.Query<Order>();
[UseProjection]
public IExecutable GetEmployees(IAsyncDocumentSession session)
=> session.Query<Employee>().AsExecutable();
}
Here, attributes like [UsePaging], [UseProjection], [UseFiltering], and [UseSorting] are applied. These attributes enable additional features such as pagination, projection, filtering, and sorting. You can learn more about these attributes in the Hot Chocolate documentation.
Registering the Query Type and Configuring Attributes
Before querying RavenDB, we need to register the Query type and configure the support for the mentioned attributes in the schema. Here is the complete code in Program.cs:
using GraphQL.RavenDB.Demo.Types;
using Raven.Client.Documents;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IDocumentStore>(
_ => new DocumentStore {
Urls = new[] { "http://localhost:8080" },
Database = "Northwind"
}.Initialize());
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddRavenFiltering()
.AddRavenProjections()
.AddRavenSorting()
.AddRavenPagingProviders();
var app = builder.Build();
app.MapGraphQL();
app.Run();
Explanation
- DocumentStore configuration:
- The DocumentStore instance is added to the services. It connects to the RavenDB server at http://localhost:8080 and uses the “Northwind” database.
- GraphQL Server configuration:
- The GraphQL server is set up with the Query type.
- Additional features such as filtering, projections, sorting, and paging are configured using .AddRavenFiltering(), .AddRavenProjections(), .AddRavenSorting(), and .AddRavenPagingProviders().
- Application Setup:
- The MapGraphQL method maps the GraphQL endpoint.
- The application is then run.
This setup allows you to query data from RavenDB using GraphQL with advanced features like filtering, projections, sorting, and paging enabled.
Querying
Let’s utilize the features provided by the Hot Chocolate’s GraphQL server and its RavenDB integration, and fetch data from the RavenDB database. We’ll use Banana Cake Pop UI to send the queries in GraphQL syntax and review the results.
After the startup please make sure you’ll reload the GraphQL schema:
So you’ll be able to execute queries then:
Get a List of Orders with Paging
The following query will fetch a paginated list of orders. The fields like order ID, order date, company ID, employee ID and some shipping address details will be retrieved.
query {
orders(first: 10) {
edges {
node {
id
orderedAt
company
employee
shipTo {
city
country
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
Get Employees with Projections
In order to retrieve a list of employees, including their IDs, names and titles you can run the following query:
query {
employees {
id
firstName
lastName
title
}
}
Get Orders with nested data and apply Filtering and Sorting
The below query will return orders where the shipping country is the “USA”, including order date, customer company ID, and shipping address. Results will be sorted descending by order date.
query {
orders(
where: { shipTo: { country: { eq: "USA" } } },
order: { orderedAt: DESC }
) {
edges {
node {
id
orderedAt
company
shipTo {
city
country
}
}
}
}
}
Conclusion
Integrating GraphQL with RavenDB using Hot Chocolate is straightforward and efficient. By setting up a web project and configuring it with Hot Chocolate and RavenDB, developers can fully leverage GraphQL’s querying capabilities.
This integration allows for precise data fetching, reducing requests and data transfer, thereby enhancing performance. Attributes like paging, projection, filtering, and sorting simplify complex data operations. With this setup, developers can easily create robust, responsive applications that efficiently interact with RavenDB databases.