Designing the Domain
As highlighted earlier in the Getting Started guide, designing your domain model is one of the most important steps - and typically the first one - when building APIs with JsonApi4j. A well-structured domain design ensures clear resource boundaries, consistent data representation, and smoother integration with the JSON:API specification.
There are a few extension points that are important to understand when working with JsonApi4j. In most cases, you’ll simply implement one or more predefined interfaces that allow the framework to recognize and apply your domain configuration automatically.
All domain-related interfaces are located in the jsonapi4j-core module under the pro.api4.jsonapi4j.domain package.
Here are the most essential ones:
Resource<RESOURCE_DTO>- implement this interface to declare a new JSON:API resourceToOneRelationship<RELATIONSHIP_DTO>- implement this interface to declare a new JSON:API to-one relationshipToManyRelationship<RELATIONSHIP_DTO>- implement this interface to declare a new JSON:API to-many relationship
Resource<RESOURCE_DTO>
This is the primary interface for defining a JSON:API resource. It describes how your internal model is going to be represented by JSON:API documents.
Think about resources as of vertices (or nodes) in a graph.
Type parameter:
RESOURCE_DTO- the internal data object or DTO from your domain or persistence layer (UserDbEntity,DownstreamCountry, etc.).
Annotation
Every resource must be annotated with @JsonApiResource to register it in the framework:
@JsonApiResource(resourceType = "users")
public class UserResource implements Resource<UserDbEntity> {
// ...
}
The resourceType attribute defines the unique type name for this resource. Each resource in your API must have a distinct type.
Mandatory
resolveResourceId(RESOURCE_DTO dataSourceDto)- returns the unique identifier for this resource (“id” member). Must be unique across all resources of this type.
Optional Capabilities
Attributes:
| Method | Default | Description |
|---|---|---|
resolveAttributes(RESOURCE_DTO dataSourceDto) |
null |
Maps internal objects to API-facing attributes (“attributes” member). Most resources should define this as it represents the core domain information. |
Top-level document links and meta — override these methods to customize the top-level “links” and “meta” members of the JSON:API document. The defaults are sufficient for most cases:
| Method | Default | Description |
|---|---|---|
resolveTopLevelLinksForSingleResourceDoc(request, dataSourceDto) |
“self” link | Links for single-resource documents (e.g., GET /users/5). By default, generates a “self” link. |
resolveTopLevelLinksForMultiResourcesDoc(request, dataSourceDtos, paginationContext) |
“self” + “next” links | Links for multi-resource documents (e.g., GET /users). By default, generates “self” and “next” links where applicable. |
resolveTopLevelMetaForSingleResourceDoc(request, dataSourceDto) |
null |
Meta for single-resource documents |
resolveTopLevelMetaForMultiResourcesDoc(request, dataSourceDtos) |
null |
Meta for multi-resource documents |
Resource-level links and meta — customize the “links” and “meta” members inside each resource object:
| Method | Default | Description |
|---|---|---|
resolveResourceLinks(request, dataSourceDto) |
“self” link | Links within each resource object |
resolveResourceMeta(request, dataSourceDto) |
null |
Meta within each resource object |
ToOneRelationship<RELATIONSHIP_DTO>
This interface is used to define a To-One relationship between a JSON:API resource and another related resource. It allows the framework to map and expose single-valued relationships in a JSON:API-compliant response.
Think of this relationship as a 1-to-1 edge in a graph, where one parent resource can reference a single related resource.
Type parameter:
RELATIONSHIP_DTO- the internal data object or DTO representing the related resource (e.g.,DownstreamCountry).
Annotation
Every relationship must be annotated with @JsonApiRelationship to register it in the framework:
@JsonApiRelationship(relationshipName = "placeOfBirth", parentResource = UserResource.class)
public class UserPlaceOfBirthRelationship implements ToOneRelationship<DownstreamCountry> {
// ...
}
The relationshipName attribute defines the name of the relationship field in the JSON:API document. The parentResource attribute identifies which resource this relationship belongs to.
Mandatory
These methods are inherited from the Relationship base interface:
resolveResourceIdentifierType(RELATIONSHIP_DTO relationshipDto)- returns the type of the related resource (“type” member of the resource linkage object). Can return different types if the relationship is polymorphic — for example, auserPropertyrelationship might return"apartments","cars", or"yachts"depending on the DTO.resolveResourceIdentifierId(RELATIONSHIP_DTO relationshipDto)- returns the unique identifier of the related resource (“id” member of the resource linkage object).
Optional Capabilities
| Method | Default | Description |
|---|---|---|
resolveRelationshipLinks(request, relationshipDto) |
“self” + “related” links | Customize the “links” member of the relationship object |
resolveRelationshipMeta(request, relationshipDto) |
null |
Customize the “meta” member of the relationship object |
resolveResourceIdentifierMeta(request, relationshipDto) |
null |
Customize the “meta” member of the resource identifier object within the relationship’s “data” member |
Notes:
- A To-One relationship always resolves to a single resource identifier object (or
null) in the JSON:API response. - Multiple relationships can be defined for the same resource by implementing multiple
ToOneRelationshipinstances.
ToManyRelationship<RELATIONSHIP_DTO>
This interface is used to define a To-Many relationship between a JSON:API resource and another related resource. It allows the framework to map and expose multivalued relationships in a JSON:API-compliant response.
Think of this relationship as a 1-to-N edge in a graph, where one parent resource can reference multiple related resources.
Type parameter:
RELATIONSHIP_DTO- the internal data object or DTO representing each related resource (e.g.,DownstreamCountry).
Annotation
Same as ToOneRelationship — annotate with @JsonApiRelationship:
@JsonApiRelationship(relationshipName = "citizenships", parentResource = UserResource.class)
public class UserCitizenshipsRelationship implements ToManyRelationship<DownstreamCountry> {
// ...
}
Mandatory
Same as ToOneRelationship — inherited from the Relationship base interface:
resolveResourceIdentifierType(RELATIONSHIP_DTO relationshipDto)- returns the type of each related resource.resolveResourceIdentifierId(RELATIONSHIP_DTO relationshipDto)- returns the unique identifier of each related resource.
Optional Capabilities
The key difference from ToOneRelationship: resolveRelationshipLinks and resolveRelationshipMeta receive a List<RELATIONSHIP_DTO> (all items in the relationship) instead of a single DTO. resolveRelationshipLinks also receives a PaginationContext since to-many relationships support pagination.
| Method | Default | Description |
|---|---|---|
resolveRelationshipLinks(request, relationshipDtos, paginationContext) |
“self” + “related” links | Customize the “links” member. Receives the full list of relationship DTOs and pagination context for generating “next” links. |
resolveRelationshipMeta(request, relationshipDtos) |
null |
Customize the “meta” member. Receives the full list of relationship DTOs. |
resolveResourceIdentifierMeta(request, relationshipDto) |
null |
Customize the “meta” member of each individual resource identifier object within the “data” array. Called per item. |
Notes:
- A To-Many relationship resolves to an array of resource identifier objects in the JSON:API response.
- Multiple relationships can be defined for the same resource by implementing multiple
ToManyRelationshipinstances.