Skip to content

Introduction ​

An API endpoint that lists records usually lets the client choose what it gets:

GET /users?filter[name]=john&sort=-created_at&include=posts&fields[users]=id,name&fields[posts]=title

The package reads these parameters, checks them against what the endpoint allows, and applies them to an Eloquent query. A parameter the endpoint does not allow is rejected instead of reaching the database.

A drop-in replacement for spatie/laravel-query-builder ​

The package is API-compatible with spatie/laravel-query-builder 7.x: the same class names, method signatures, exceptions and configuration file. Switching is a namespace change, Spatie\QueryBuilder → MyProgs\QueryBuilder. Code written for spatie 5 and 6 works as well. The spatie test suite runs against this package as its compatibility contract.

Included relations are never empty ​

With spatie, a sparse fieldset combined with an include loses the related rows:

GET /users?fields[users]=name&include=posts

The users are selected without id, so Eloquent cannot match the posts to them, and every posts relation comes back empty. The same happens when fields[posts] leaves out user_id. This is spatie issue #175.

This package adds the primary and foreign keys every included relation needs to the SELECT: for belongsTo, hasMany, morphTo, belongsToMany pivots, hasManyThrough and the rest. Included relations are loaded as if the fieldset had listed the keys.

Two APIs over one core ​

  • The spatie-compatible API (QueryBuilder::for(User::class)->allowedFilters(...)) is for existing code and a drop-in switch. See Basic usage.
  • The definitions API (UserQuery::for($request)) describes in one class what a request may ask of a resource. It adds typed filters with operators (filter[price][gte]=100), sorts by relations and aggregates, virtual fields, nested definitions, built-in pagination, and reports every problem of the request in one 422 response. See Defining queries.

Both APIs parse, validate and plan the request with the same core, so the keys are added in the same way in both.

Requirements ​

  • PHP 8.3 or newer
  • Laravel 12 or 13