The Sparse Fieldsets plugin implements JSON:API Sparse Fieldsets, allowing clients to request only the fields they need using the fields[TYPE]=field1,field2 query parameter. This reduces payload size and improves response efficiency.

The plugin works automatically — no code changes are needed. Once enabled, the framework filters attributes on the server before serialization. This applies to both primary resources and included resources resolved via Compound Documents — each resource type can have its own set of requested fields.

To enable the plugin, add the following dependency:

<dependency>
  <groupId>pro.api4</groupId>
  <artifactId>jsonapi4j-sf-plugin</artifactId>
  <version>${jsonapi4j.version}</version>
</dependency>

Usage

Use fields[TYPE]=field1,field2 to request only specific fields for a given resource type. When a response contains multiple resource types — for example, primary users and included countries via a relationship — you can control fields independently for each type:

GET /users?include=citizenships&fields[users]=email,lastName&fields[countries]=name

This returns users with only email and lastName in their attributes, and included countries with only name — all other fields are excluded on the server:

{
  "data": [
    {
      "id": "1",
      "type": "users",
      "attributes": {
        "email": "john@doe.com",
        "lastName": "Doe"
      },
      "relationships": {
        "citizenships": {
          "data": [{ "id": "NO", "type": "countries" }]
        }
      }
    }
  ],
  "included": [
    {
      "id": "NO",
      "type": "countries",
      "attributes": {
        "name": "Norway"
      }
    }
  ]
}

Edge Cases

Scenario Behavior
No fields[TYPE] parameter All fields are returned for that resource type
Empty value: fields[users]= No fields are returned for that resource type
Non-existing type or field Ignored
Nested path where parent exists but leaf doesn’t (e.g. a.b.c where a.b exists but c doesn’t) Entire path is ignored
Path into a collection, array or map (e.g. fields[users]=addresses.zip) Names a field of every element — zip is kept in each address, other fields of each address are dropped. Elements themselves are never removed.
Path through a field that is null on this resource Still a valid path. Whether it exists is decided by the type, not by the data.
Map keys Not addressable. Only values are narrowed.
All requested fields don’t exist Treated as empty fields (no fields returned) by default. Configurable via requestedFieldsDontExistMode.
Primitive-typed fields (e.g. int) Cannot be excluded if other fields at the same level are requested, because primitives can’t be set to null. Use object types (e.g. Integer) for full sparse fieldsets support.
Multi-type responses (e.g. users + included countries) Use separate fields[TYPE] parameters per resource type
Attributes class cannot be copied (e.g. a dynamic proxy) All of its fields are returned, and a warning is logged. Sparse fieldsets is a convenience over the response shape, never a security control, so it does not fail the request.

How Fields Are Excluded

Excluding a field never modifies your object. The plugin builds a copy of the attributes object without the excluded fields and puts the copy in the response, so the instance your Resource returned comes back untouched — an application is free to return a cached or shared attributes object. Objects are copied only along a path to an excluded field, and only when something is actually excluded.

This matches how the Access Control plugin hides fields.

Available Properties

Property name Default value Description
jsonapi4j.sf.enabled true Enables/disables Sparse Fieldsets plugin
jsonapi4j.sf.requestedFieldsDontExistMode SPARSE_ALL_FIELDS Behavior when all requested fields don’t exist. Options: SPARSE_ALL_FIELDS (return no fields), RETURN_ALL_FIELDS (return all fields)