Why I built beanquery: a whitelist-first query API for JPA
Almost every admin panel I build needs the same thing: a table with filters, sorting and paging over some JPA entity. And almost every time the first version is either a pile of hand-written endpoints, or a generic "filter anything" endpoint that quietly exposes more than anyone intended.
The problem with "filter anything"
If an endpoint can filter on any field of an entity, then every column, including the ones you never meant to show, becomes searchable. Even if you never return the value, a filter on it leaks information. Blacklists do not fix this: the day someone adds a column, it is public until somebody remembers to hide it.
Whitelist first
beanquery inverts the default. A field is invisible until you annotate it with @QueryableField, and each field declares what it allows: selectable, filterable, sortable. For every annotated entity the starter exposes two endpoints:
GET /api/bq/{entity}/metadatadescribes fields, types and allowed operators, so a UI can build itselfPOST /api/bq/{entity}/queryruns a query withselect,filters,sortandpage
Filters are a tree, so real-world conditions are expressible:
{
"logic": "and",
"children": [
{ "field": "status", "op": "EQ", "value": "ACTIVE" },
{
"logic": "or",
"children": [
{ "field": "name", "op": "EQ", "value": "Acme" },
{ "field": "city", "op": "EQ", "value": "Gorlice" }
]
}
]
}
Safety by construction
Operators are allow-listed per field type, queries are built with JPA Criteria rather than strings, transactions are read-only, and page sizes are capped. A QueryAuthorizer hook lets the host application add mandatory predicates (for multi-tenancy, say) and hide fields per caller.
Try it
beanquery is Apache 2.0 and needs JDK 17–21 and Spring Boot 3.1+ with JPA and Spring MVC:
<dependency>
<groupId>io.github.mszajner</groupId>
<artifactId>beanquery-starter</artifactId>
<version>0.1.0</version>
</dependency>
The source is on GitHub. Issues and pull requests are welcome.