FluentGraphQL 2.0.4
dotnet add package FluentGraphQL --version 2.0.4
NuGet\Install-Package FluentGraphQL -Version 2.0.4
<PackageReference Include="FluentGraphQL" Version="2.0.4" />
<PackageVersion Include="FluentGraphQL" Version="2.0.4" />
<PackageReference Include="FluentGraphQL" />
paket add FluentGraphQL --version 2.0.4
#r "nuget: FluentGraphQL, 2.0.4"
#:package FluentGraphQL@2.0.4
#addin nuget:?package=FluentGraphQL&version=2.0.4
#tool nuget:?package=FluentGraphQL&version=2.0.4
FluentGraphQL
FluentGraphQL is a lightweight, fluent C# library for dynamically building GraphQL queries. It allows developers to construct queries using a clean, chainable syntax—perfect for strongly typed scenarios or custom query generation needs.
✨ Features
- ✅ Fluent API to build queries and mutations
- ✅ Nested field selection with arguments and aliases
- ✅ Type-safe LINQ-style filters —
.Where(x => x.City == "Paris" && x.Age >= 18)compiled to the GraphQLwhereargument - ✅ Lightweight — a single dependency (System.Text.Json)
- ✅ High performance — no per-field expression trees (see benchmarks)
🤝 Comparison
There is already a great alternative available: graphql-query-builder-dotnet by Charles Devandiere. This project is not meant to discredit or replace it.
FluentGraphQL simply explores a different architectural approach, with a focus on fluent chaining, dynamic nested field construction, and performance fine-tuning. It was born independently and out of curiosity and learning, not competition.
⚡ Performance
Benchmarked with BenchmarkDotNet against graphql-query-builder-dotnet on the same nested query (accounts → contacts → tasks). Run it yourself:
dotnet run -c Release --project src/FluentGraphQL.Benchmark
| Scenario | Library | Mean | Allocated |
|---|---|---|---|
| Field selection | FluentGraphQL | ~0.7 µs | 1.8 KB |
| Field selection | graphql-query-builder-dotnet | ~5.2 µs | 11.3 KB |
+ where filter |
FluentGraphQL — .Where(x => …) |
~2.7 µs | 5.8 KB |
+ where filter |
graphql-query-builder-dotnet — manual args | ~7.9 µs | 19.1 KB |
- ~7× faster, ~6× less memory selecting fields — FluentGraphQL reads member names via
Func+[CallerArgumentExpression]instead of allocating anExpression<Func<>>per field. - ~3× faster, ~3× less memory on a filtered query — and the filter stays type-safe:
.Where(x => x.City == "Paris" && x.Contacts.Any(c => c.FirstName == "Jo"))rather than a hand-writtenwhereobject.
<sub>Apple M1 Max, .NET 9. Absolute numbers vary by machine — the ratios are the point. .Where parses one expression tree per query (read, never .Compile()d); field selection uses none.</sub>
📦 Installation
You can install via NuGet (once published):
dotnet add package FluentGraphQL
🚀 Quick Start
public class Account
{
public Guid Id { get; set; }
public string SocietyName { get; set; }
public Adresse Adresse { get; set; }
public IEnumerable<Contact> Contacts { get; set; }
}
public class Contact
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public IEnumerable<Task> Tasks { get; set; }
}
public class Task
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime StartDate { get; set; }
public DateTime? DueDate { get; set; }
}
using static FluentGraphQL.GraphQL;
var builder = new GraphQLQueryBuilder();
var cities = new[] { "Paris", "London" };
builder
.AddVariable("firstName", "Paul")
.AddQuery(new GraphQLQueryObject<Account>("accounts")
.AddEveryFields()
.AddCollectionField(
account => account.Contacts,
contact => contact
.AddEveryFields()
.AddCollectionField(
c => c.Tasks,
task => task.AddEveryFields()
)
)
.Where(account =>
cities.Contains(account.Adresse.City)
&& account.Contacts.Any(c => c.FirstName == Var<string>("firstName"))));
var result = yourapi.Query(builder.Request);
Resulting query:
query ($firstName: String!) {
accounts(
where: {
adresse: { city: { in: ["Paris", "London"] } }
contacts: { some: { firstName: { eq: $firstName } } }
}
) {
id
societyName
contacts {
id
firstName
lastName
email
phoneNumber
tasks {
id
name
description
startDate
dueDate
}
}
}
}
🔑 Variables and literal values
Inside a .Where(...) filter, a value is treated one of two ways:
- A C# literal or captured value is literal data. It is JSON-escaped before being inlined, so quotes, backslashes and newlines from user input cannot break out of the query.
x.City == "Paris"renderscity: { eq: "Paris" };x.City == userInputis always safely escaped. Var<T>("name")is a reference to a declared variable. It renders$nameand must match a variable added withAddVariable.
using static FluentGraphQL.GraphQL;
builder
.AddVariable("city", "Paris")
.AddQuery(new GraphQLQueryObject<Account>("accounts")
.AddEveryFields()
.Where(x => x.Adresse.City == Var<string>("city")));
AddVariable(name, value) infers the GraphQL type from the value's type. Use the explicit AddVariable(name, GraphQLParameterType.X, value) overload when you need full control over the declared type.
Need an operator the fluent form doesn't cover (pagination, a custom argument)? WithArguments(new { ... }) is still available as an escape hatch and follows the same variable/literal rules.
🧪 Testing
Tests are written with xUnit and cover query generation scenarios. To run:
dotnet test
📄 License
MIT — see the LICENSE file for details.
🙌 Contribution
Feel free to open issues or submit pull requests to improve the library!
FluentGraphQL is maintained by @Nayruuu.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- System.Text.Json (>= 9.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.4 | 91 | 8/11/2026 |
| 2.0.1 | 112 | 7/12/2026 |
| 2.0.1-gfa888524e2 | 102 | 7/12/2026 |
| 2.0.1-g836d1c2f6d | 100 | 7/12/2026 |
| 1.0.2 | 224 | 7/30/2025 |