Introducing GraphQLinq - Strongly Typed GraphQL Queries with LINQ to GraphQL.

Consuming a GraphQL api in C# is straightforward with either using HttpClient directly or using a client library such as GraphQL.Client but both suffer from the same problems: The GraphQL queries do not go through any compile-time checking, and any mistake in the query isn’t discovered until you execute the query at runtime. For example, to query SpaceX GraphQL API for all missions where the manufacturer is Orbital ATK you need to run the following query:

{
  missions (find: {manufacturer: "Orbital ATK"}) {
    id
    name
    description
    twitter
    website
    wikipedia
    manufacturers
  }
}

.Net solved the same problem for SQL with LINQ: LINQ queries are first-class language objects and are strongly typed. This way, there are no hardcoded queries in the project, and the LINQ queries are type-checked by the compiler.

In this article, I will show how GraphQLinq - a LINQ to GraphQL library solves this problem.

Getting Started with GraphQLinq

GraphQLinq consists of two parts: GraphQLinq.Scaffolding for generating strongly typed classes from the GrapQL endpoint and client library, GraphQLinq.Client, for writing and executing LINQ to GraphQL queries.

GraphQLinq.Scaffolding is a .NET tool, so run the following command to install it:

dotnet tool install --global --version 1.0.0-beta GraphQLinq.Scaffolding

This will install the tool globally and make it available for all projects.

Next, navigate to your project folder and run the graphql-scaffold tool to scaffold the client-side code:

graphqlinq-scaffold https://api.spacex.land/graphql -o SpaceX -n SpaceX

This will generate the code in the SpaceX folder (specified by the o option) and use SpaceX as a namespace too.

Scaffolding GraphQL client with GraphQLinq.Scaffolding

Now, install the GraphQLinq.Client package in your project to write some LINQ to GraphQL queries:

dotnet add package GraphQLinq.Client --version 1.0.0-beta

Running GraphQL Queries with LINQ

The scaffolding tool generates QueryContext class that serves as an entry point for writing and running LINQ queries. For example, you can write the above graphQL query like this:

var spaceXContext = new QueryContext();

var missionsQuery = spaceXContext.Missions(new MissionsFind { Manufacturer = "Orbital ATK" }, null, null)
                                 .Include(mission => mission.Manufacturers);
var missions = missionsQuery.ToList();

This LINQ query will generate a graphQL query that includes all primitive properties of the Mission class and also includes the list of manufacturers in the query.

Here are a couple of more LINQ to GraphQL queries:

Query all Primitive Properties of a Type

Quering for all primitive properties is very straightforward:

var spaceXContext = new QueryContext();

var company = spaceXContext.Company().ToItem();

Query Specific Properties

var companySummary = spaceXContext.Company().Select(c => new CompanySummary
{
    Ceo = c.Ceo,
    Name = c.Name,
    Headquarters = c.Headquarters
}).ToItem();

/// Alternatively, use an anonymous object

var companySummaryAnonymous = spaceXContext.Company().Select(c => new { c.Ceo, c.Name, c.Headquarters }).ToItem();

Include Navigation Properties

var companyWithHeadquartersAndLinks = spaceXContext.Company()
                                      .Include(info => info.Headquarters)
                                      .Include(info => info.Links).ToItem();

View Generated Query

To view the generated GraphQL query, call ToString on the LINQ query itself. You will get the GraphQL query together with the variables bound to the query.

var missionsQuery = spaceXContext.Missions(new MissionsFind { Manufacturer = "Orbital ATK" }, null, null)
                                 .Include(mission => mission.Manufacturers);

var query = missionsQuery.Query;
var fullQuery = missionsQuery.ToString();

After executing the above code snippet query will be equal to:

query ($find: MissionsFind) { result: missions (find: $find) { 
  description
  id
  name
  twitter
  website
  wikipedia
  manufacturers
 }}

and fullQuery will be:

{"query":"query ($find: MissionsFind) { result: missions (find: $find) { 
  description
  id
  name
  twitter
  website
  wikipedia
  manufacturers
 }}","variables":{"find":{"manufacturer":"Orbital ATK"}}}

Roadmap

The library is in the early stages and does not support all possible GraphQL schemas and queries. Check open issues for a list of proposed features and known issues.

If you find the library useful, don’t forget to ⭐ the GitHub repository. If you have any questions or suggestions, you are welcome to submit an issue or send a PR.

Avatar
Giorgi Dalakishvili
World-Class Software Engineer

Related