AroraQL 1.0.0

dotnet add package AroraQL --version 1.0.0
                    
NuGet\Install-Package AroraQL -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AroraQL" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AroraQL" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="AroraQL" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AroraQL --version 1.0.0
                    
#r "nuget: AroraQL, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AroraQL@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AroraQL&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=AroraQL&version=1.0.0
                    
Install as a Cake Tool

AroraQL

AroraQL turns plain JSON payloads into LINQ queries. A client describes what it wants — filter, sort, project, page — in a single JSON body, and one dynamic generic endpoint executes it against any data source using expression trees. No per-entity filter endpoints, no query-string gymnastics.

Features

  • Dynamic generic endpoints — a single POST /query endpoint serves every data source; the payload's from field selects the target at runtime.
  • JSON-first query modelJsonQuery binds straight from the request body via minimal API model binding.
  • Expression-tree executionwhere and orderBy compile to real LINQ expressions (Where, OrderBy/OrderByDescending), including nested paths like Department.Name.
  • Field projectionselect returns lightweight dictionaries containing only the requested fields, nested dot-paths included.
  • Scheme discovery — mark models with IJsonQueryable<T> and MapJsonQuery() auto-registers a POST /{typename} scheme endpoint for each.

Installation

dotnet add package AroraQL

Requires .NET 10 and the Microsoft.AspNetCore.App shared framework (i.e. an ASP.NET Core app).

Quick start: one endpoint, every source

1. Define your models

using AroraQL;

public sealed class Employee : IJsonQueryable<Employee>
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public int Age { get; set; }
    public string Country { get; set; } = "";
    public Department Department { get; set; } = new();
}

public sealed class Department : IJsonQueryable<Department>
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
}

2. Map the dynamic generic endpoint

This is the core pattern: one endpoint, dispatched dynamically on the payload's from field. The switch is your explicit, type-safe routing table — adding a new queryable source is one line.

app.MapPost("/query",
    (JsonQuery query) => query.From?.ToLowerInvariant() switch
    {
        "books" => Results.Ok(JsonQueryExtensions.Execute(books, query)),
        "authors" => Results.Ok(JsonQueryExtensions.Execute(authors, query)),
        "employees" => Results.Ok(JsonQueryExtensions.Execute(employees, query)),
        _ => Results.BadRequest($"Unknown source '{query.From}'.")
    })
    .WithTags("AroraQL");

Execute itself is source-agnostic — it never looks at from. Routing the payload to the right collection (or repository, or EF DbSet) is deliberately the host's job, which keeps the library free of reflection-based source lookup and gives you full control over what is exposed.

3. Query it

POST /query
Content-Type: application/json
{
  "from": "Employees",
  "select": ["Id", "Name", "Department.Name"],
  "where": [
    { "field": "Age", "op": ">", "value": 18 },
    { "field": "Country", "op": "=", "value": "Egypt" }
  ],
  "orderBy": [{ "field": "Name", "dir": "asc" }],
  "take": 50
}

Response:

[
  { "Id": 1, "Name": "Ahmed", "Department.Name": "Engineering" },
  { "Id": 2, "Name": "Mona",  "Department.Name": "HR" },
  { "Id": 4, "Name": "Sara",  "Department.Name": "Engineering" }
]

Filtering, ordering, paging, and projection all happen server-side from that single payload. When select is empty the full entities are returned; otherwise each row is a dictionary containing exactly the requested fields.

You can also pin an endpoint to a single source when you don't want dynamic dispatch:

app.MapPost("/query/books",
    (JsonQuery query) => JsonQueryExtensions.Execute(books, query))
    .WithTags("AroraQL");

Scheme discovery endpoints

MapJsonQuery() scans loaded assemblies for IJsonQueryable<T> implementations and maps a POST /{typename} scheme endpoint per model (handy for exercising model shapes from Scalar/OpenAPI):

app.MapJsonQuery();          // all IJsonQueryable<T> models
app.MapType<Employee>();     // or one explicit type

Query payload reference

JsonQuery

JSON field Type Description
from string Source name used by your dispatch switch.
select string[] Fields to project; dot notation for nested paths.
where WhereClause[] Filter clauses (combined with AND).
orderBy OrderByClause[] Sort clauses, applied in order.
skip int? Number of items to skip.
take int? Maximum number of items to return.
page int? Page number (reserved).
pageSize int? Page size (reserved).
distinct bool Distinct flag (reserved).
groupBy string[] Grouping fields (reserved).
include IncludeClause[] Related data to include (reserved).

where clause

{ "field": "Age", "op": ">", "value": 18 }
op Meaning Executed
= Equal
!= Not equal
> Greater than
>= Greater or equal
< Less than
<= Less or equal
like Pattern match planned
in Set membership planned
not in Set exclusion planned
between Range planned

value is any JSON literal; it is converted to the target property's CLR type at execution time. Unsupported operators throw NotSupportedException.

orderBy clause

{ "field": "Name", "dir": "asc" }

dir is "asc" (default) or "desc". Nested paths like Department.Name are supported in field, select, where, and orderBy alike.

Executing outside HTTP

Execute works against any IEnumerable<T>, so the same payload can drive background jobs, tests, or gRPC handlers:

var result = JsonQueryExtensions.Execute(employees, query);

A fluent QueryBuilder is also available for composing a JsonQuery in C# when the caller isn't sending JSON.

Sample

A runnable sample (minimal API + Scalar UI, in-memory data) lives in samples/AroraQL.Sample:

dotnet run --project samples/AroraQL.Sample --launch-profile http
# then open http://localhost:5199 (redirects to /scalar/v1)

Building from source

dotnet build
dotnet pack src/AroraQL/AroraQL.csproj -c Release

The NuGet package is produced automatically on build (GeneratePackageOnBuild).

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

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
1.0.0 114 7/4/2026