Skip to content

Repository files navigation

FlexQuery.NET

Dynamic filtering, sorting, paging, and projection for IQueryable in .NET.

NuGet Version NuGet Downloads .NET Documentation License

FlexQuery.NET is a dynamic query engine for .NET REST APIs. It turns query-string parameters into secure, server-side expression trees — filtering, sorting, paging, and projecting your data with a single call, without ever evaluating LINQ in memory.

Why FlexQuery.NET?

  • One endpoint, every query — clients send filter, sort, page, pageSize, select, and more; your action method stays one line.
  • 100% server-side — every operation translates to SQL via expression trees. No client evaluation, no surprise full-table loads.
  • Security first — whitelists, blacklists, and per-operation field policies are validated before a query ever reaches the database.
  • Multiple query syntaxes — the native DSL, FQL (a SQL-inspired language), and a lightweight OData-compatible syntax.
  • Provider choice — runs on Entity Framework Core, Dapper (with multi-dialect SQL generation), or any IQueryable source.

Quick Start

Install the core package plus a provider:

dotnet add package FlexQuery.NET
dotnet add package FlexQuery.NET.EntityFrameworkCore

Configure once at startup, then bind FlexQueryParameters in a controller and pass it to FlexQueryAsync:

using FlexQuery.NET;

// Program.cs — once, before any query executes
FlexQueryCore.Configure();
FlexQueryEFCore.Setup();
using FlexQuery.NET.Models;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/customers")]
public sealed class CustomersController(AppDbContext db) : ControllerBase
{
    [HttpGet]
    public async Task<IActionResult> GetCustomers(
        [FromQuery] FlexQueryParameters parameters,
        CancellationToken cancellationToken)
    {
        var result = await db.Customers
            .FlexQueryAsync(parameters, opt =>
            {
                opt.AllowedFields = ["Id", "FirstName", "LastName", "Email", "Status"];
                opt.MaxPageSize = 100;
                opt.DefaultSortField = "Id";
            }, cancellationToken);

        return Ok(result);
    }
}

That's the whole endpoint. A request like:

GET /api/customers?filter=Status:eq:Active&sort=LastName:asc&page=1&pageSize=20&select=Id,FirstName,Email

returns a paged, validated, SQL-translated result:

{
  "data": [
    { "id": 3, "firstName": "Ana", "email": "ana@example.com" }
  ],
  "totalCount": 42,
  "page": 1,
  "pageSize": 20,
  "totalPages": 3,
  "hasNextPage": true,
  "hasPreviousPage": false
}

With an explicit select, the response contains exactly the requested fields — and nothing else.

Supported Query Formats

The same endpoint can accept different filter syntaxes:

GET /api/customers?filter=Status:eq:Active            # Native DSL (default)
GET /api/customers?filter=Status = 'Active'           # FQL (SQL-inspired)
GET /api/customers?$filter=Status eq 'Active'         # MiniOData (OData-compatible)

The DSL supports the AND/OR keywords (and &/|), parentheses for grouping, and operators such as eq, neq, gt, gte, lt, lte, contains, startswith, endswith, like, in, notin, between, isnull, isnotnull, any, all, and count.

FQL and MiniOData parsers ship as separate packages and are enabled with a one-line registration at startup:

Fql.Register();        // FlexQuery.NET.Parsers.Fql
MiniOData.Register();  // FlexQuery.NET.Parsers.MiniOData

Select the syntax globally via FlexQueryOptions.DefaultQuerySyntax, or per request via the options' QuerySyntax property.

What Can It Do?

  • Filtering — composable conditions with ~20 operators, AND/OR logic, and nesting.
  • Sorting — multi-field, per-field direction, with server-defined defaults.
  • Selection & DTO projection — dynamic select with aliases, or strongly-typed results via FlexQueryAsync<TEntity, TResponse> with a convention-based mapping model (CreateMap, ForMember, ForNavigation).
  • Pagination — offset paging and high-performance keyset (cursor) pagination with cursor / NextCursorToken.
  • Include & Expand — eager-load navigations, or deep expansions where each branch can carry its own filter, sort, and take.
  • Grouping & aggregatesgroupBy, aggregate (sum, count, avg, …), having, and distinct.
  • Validation & governance — per-operation field whitelists (FilterableFields, SortableFields, SelectableFields, …), allowed-operator policies, field-depth limits, role-based field access, and secure-by-default strict validation.
  • Fluent API — compose queries in code with Query.Create() and typed filter builders.
  • OpenAPI — document FlexQuery endpoints automatically in ASP.NET Core's built-in OpenAPI generation.

Packages

Package Purpose README
FlexQuery.NET Core query engine — parsing, filtering, sorting, paging, projection, grouping, validation, fluent API docs
FlexQuery.NET.EntityFrameworkCore Async execution, filtered includes, and typed DTO projection for EF Core docs
FlexQuery.NET.Dapper Direct SQL generation and execution for Dapper (SQL Server, PostgreSQL, MySQL, MariaDB, SQLite, Oracle) docs
FlexQuery.NET.AspNetCore ASP.NET Core integration with [FieldAccess] security attributes and result-shape JSON docs
FlexQuery.NET.OpenApi OpenAPI/Swagger documentation for FlexQuery endpoints (.NET 9/10) docs
FlexQuery.NET.Diagnostics Execution diagnostics, timing, and observability docs
FlexQuery.NET.Adapters.AgGrid AG Grid Server-Side Row Model request/response adapter docs
FlexQuery.NET.Adapters.Kendo Kendo UI DataSource request adapter docs
FlexQuery.NET.Parsers.Fql FQL (SQL-inspired) syntax parser docs
FlexQuery.NET.Parsers.MiniOData Lightweight OData-compatible syntax parser docs

Every package ships with its own README — the same document included in its NuGet package — covering installation, quick-start snippets, and package-specific features. The links in the table above go to those READMEs; each one also cross-references the related packages.

All packages depend on the core package only; mix and match what you need:

graph TD
    Core["FlexQuery.NET"]
    Core --> EF["EntityFrameworkCore"]
    Core --> Dapper["Dapper"]
    Core --> AspNet["AspNetCore"]
    Core --> OpenApi["OpenApi"]
    Core --> Diag["Diagnostics"]
    Core --> AgGrid["Adapters.AgGrid"]
    Core --> Kendo["Adapters.Kendo"]
    Core --> Fql["Parsers.Fql"]
    Core --> OData["Parsers.MiniOData"]
Loading

ASP.NET Core Integration

The ASP.NET Core package adds declarative, attribute-based security on top of the governance options:

[FieldAccess(Allowed = ["Id", "FirstName", "Email"], Sortable = ["Id", "FirstName"])]
[HttpGet]
public async Task<IActionResult> Get(
    [FromQuery] FlexQueryParameters parameters,
    CancellationToken cancellationToken)
{
    ...
}

Wire it up alongside global configuration:

builder.Services.AddControllersWithViews()
    .AddFlexQuerySecurity();   // [FieldAccess] attributes + result-shape JSON

builder.Services.AddFlexQuery(options =>
{
    options.MaxPageSize = 100;
    options.StrictFieldValidation = true;
});

Providers

Entity Framework CoreFlexQueryAsync (and the typed FlexQueryAsync<TEntity, TResponse> overload) executes the full pipeline — filter → sort → paging → includes → projection — as EF-translated expression trees, with CancellationToken support and no-tracking by default for read endpoints.

Dapper — generates and executes SQL directly against your DbConnection, with automatic dialect detection, an entity-mapping model builder, and SQL execution logging with copy-paste-ready DECLARE scripts:

var result = await connection.FlexQueryAsync<Customer>(parameters, cancellationToken: cancellationToken);

Grid Integrations

The AG Grid and Kendo adapters translate grid requests into QueryOptions and convert results back, so a server-side grid endpoint is just a few lines:

var options = agGridRequest.ToQueryOptions();       // AG Grid SSRM request
var result = await db.Customers.FlexQueryAsync(options, cancellationToken: cancellationToken);
return Ok(result.ToAgGridServerSideResponse(agGridRequest));

Documentation

Full documentation for v4 lives at flexquery.vercel.app:

Migration

Coming from v3? v4 is a breaking release: packages were renamed (the JQL parser is now FQL), option classes were restructured, legacy query syntaxes were removed, and new capabilities such as typed DTO projection, expand, and keyset pagination were added. See the v3 → v4 migration guide.

Samples

A runnable sample Web API demonstrating EF Core, Dapper, AG Grid SSRM, and Kendo UI integrations is available in the samples folder.

License

MIT License. See LICENSE.

Releases

Packages

Contributors

Languages