ATS Global

Building Headless REST and GraphQL APIs in Liferay DXP

Learn how Liferay DXP supports Headless REST and GraphQL APIs, including request processing, service-layer architecture, authentication, data access, and API testing.

LIFERAY DXPPublished: OCT 22, 202610 min readBy Jyothi N Share
Building Headless REST and GraphQL APIs in Liferay DXP
Building Headless REST and GraphQL APIs in Liferay DXP

Introduction

Modern applications often need to access backend data without depending on a platform's user interface. APIs make this possible by allowing external applications to retrieve information, create records, and interact with backend functionality through defined interfaces.

Liferay DXP supports Headless REST APIs and GraphQL for exposing application functionality to external clients. These approaches provide different ways to request data and perform operations while keeping backend implementation details separate from the consuming application.

Building a reliable API involves more than exposing an endpoint. It requires a clear API contract, appropriate service logic, secure access, reliable data persistence, and testing to verify the complete request flow.

This article explains how Headless REST and GraphQL work in Liferay DXP, how they interact with the service and persistence layers, and what to consider when implementing and testing them. A Student management example is used to illustrate the concepts.

1. Understanding the Architecture

Liferay's API architecture separates the client, API processing, business logic, and data persistence into distinct responsibilities.

The main components are:

  • External client: Sends API requests using applications such as Postman or Swagger.
  • Liferay DXP: Receives requests and applies the configured authentication and authorization mechanisms.
  • REST resource implementation: Processes supported REST operations defined by the API contract.
  • GraphQL schema and resolvers: Define and process the GraphQL operations exposed by the application.
  • Service layer: Executes the relevant business logic.
  • Persistence layer: Reads and writes application data.
  • Database: Stores the application records.

Logical architecture

       External Application      (Postman / Swagger / Client)                  |                  v             Liferay DXP                  |       Authentication & Permissions                  |           +------+------+           |             |           v             v        REST API      GraphQL API           |             |           v             v      REST Resource   Schema / Resolver           |             |           +------+------+                  |                  v             Service Layer                  |                  v           Persistence Layer                  |                  v               Database

The diagram represents a logical architecture. REST resources and GraphQL resolvers follow separate processing paths. They can use shared service logic when the implementation connects them to the same services.

This separation is important because API processing and business logic have different responsibilities. REST resources and GraphQL resolvers handle their respective interfaces, while the service layer can centralize business rules and data operations.

2. How Headless REST APIs Work

Headless REST APIs expose resources through URLs and use HTTP methods to perform supported operations.

Common methods include:

  • GET: Retrieves data.
  • POST: Creates a resource or submits data for processing.
  • PUT or PATCH: Updates a resource, depending on the API contract.
  • DELETE: Removes a resource, where supported.

The exact operations and request formats depend on the API being used.

GET request flow

A GET request retrieves existing data.

For example, a client might request student records from a custom API:

GET /o/student-headlessAPI/v1.0/students

The request follows this general sequence:

  1. The client sends an HTTP GET request.
  2. Liferay processes the configured authentication and permission checks.
  3. The REST resource implementation processes the requested operation.
  4. The implementation invokes the relevant service logic.
  5. The service retrieves the required data through the persistence layer.
  6. The result is returned through the REST API to the client.

A successful retrieval commonly returns HTTP 200 OK. The response body and content type depend on the API contract and implementation.

POST request flow

A POST request submits data to create a resource or perform another supported operation.

For example, a student registration request might contain:

{  "name": "Rahul Kumar",  "email": "rahul@example.com",  "department": "Computer Science",  "active": true}

The general processing sequence is:

  1. The client sends a POST request with the request body.
  2. Liferay processes authentication and applicable permissions.
  3. The REST resource implementation validates and processes the request.
  4. The relevant service logic executes the operation.
  5. The persistence layer saves the record when creation succeeds.
  6. The API returns the result according to its configured response contract.

The server can generate identifiers through the service layer rather than requiring clients to supply primary keys.

After creation, a separate GET request can be used to verify that the new record is available.

3. How GraphQL Works in Liferay DXP

GraphQL provides a schema-based interface for requesting data and performing supported operations.

Unlike REST, which commonly exposes resource-specific URLs, GraphQL allows clients to submit queries and mutations through a shared endpoint.

In Liferay DXP, the GraphQL endpoint is commonly:

/o/graphql

The exact operations available depend on the GraphQL schema exposed by the deployed application and Liferay configuration.

GraphQL query flow

       GraphQL Client              |              v         /o/graphql              |              v       GraphQL Schema              |              v       Query Resolver              |              v         Service Layer              |              v       Persistence Layer              |              v           Database              |              v       Resolver Result              |              v       GraphQL Response

A client submits a query describing the data it needs. The GraphQL processing layer validates the operation against the schema and invokes the configured resolver. The resolver obtains the required data through the implementation, which may call the service layer.

The result is returned in a GraphQL response, commonly containing a data property. Execution errors may also be reported in an errors property.

Example query

The following illustrates a query using a custom API namespace:

{  studentHeadlessAPI_v1_0 {    students  }}

The namespace and field names are specific to the deployed schema. They are not universal names for every Liferay installation.

GraphQL mutations

Queries retrieve data, while mutations are used for supported operations that change data.

For example, a mutation might create a record through a configured resolver. The resolver executes the relevant operation and returns the result according to the schema.

GraphQL does not automatically expose every service method or invoke a REST resource implementation. The available operations depend on the schema and resolver implementations.

4. REST vs. GraphQL

Both approaches expose backend functionality, but they differ in how clients describe requests.

FeatureREST HeadlessGraphQL
Request modelResources and HTTP methodsQueries and mutations
Endpoint patternResource-specific URLsShared GraphQL endpoint
API contractOpenAPI specification, where providedGraphQL schema
Data retrievalDefined by the resource operationDefined by the query and schema
Write operationsPOST, PUT, PATCH, or DELETE where supportedMutations where supported
Testing toolsPostman and SwaggerGraphiQL and Postman

REST is useful when an application interacts with clearly defined resources and operations. GraphQL can be useful when clients need to request specific data through a schema designed for their use cases.

Neither approach is automatically better in every situation. The appropriate choice depends on the application requirements, available operations, security configuration, and client needs.

5. Authentication and Authorization

APIs must control who can access them and which operations they can perform.

Liferay API access depends on the endpoint's security configuration. Depending on the setup, clients may use supported authentication mechanisms such as Basic Authentication or OAuth 2.0 bearer tokens.

A bearer-token request commonly includes this header:

Authorization: Bearer <access_token>

Authentication and authorization have different responsibilities:

  • Authentication establishes the identity of the caller.
  • Authorization determines whether that caller is permitted to perform the requested operation.

Typical HTTP outcomes include:

ScenarioTypical response
Request succeeds200 a successful HTTP status
Authentication is missing or invalid401 Unauthorized
Caller lacks the required permission403 Forbidden

Actual behavior depends on the endpoint and configuration.

OAuth 2.0 can allow an application to access protected APIs using an access token rather than supplying a user's password with every request. The application must still manage credentials, token expiration, scopes, and permissions securely.

A complete security test should verify both successful access and rejected requests. Obtaining a valid token alone does not prove that authorization rules are correctly enforced.

6. The Role of Service Builder and REST Builder

Liferay provides development tools that help implement data models and expose API functionality.

Service Builder

Service Builder generates model, persistence, and service infrastructure from entity definitions.

For example, a Student entity might contain an identifier, name, email address, department, and active status.

The service implementation can contain business rules and use the generated persistence components to interact with stored records.

This separation makes the backend easier to maintain because business logic does not need to be duplicated across multiple API interfaces.

REST Builder

REST Builder uses an OpenAPI specification to define the structure of a REST API, including its operations, request models, and response models.

It generates the API structure and resource classes. Custom implementation classes provide the behavior for the operations.

For example, a custom API might expose GET and POST operations for student records under a versioned path such as:

/o/student-headlessAPI/v1.0/students

The exact base path depends on the deployed API.

REST Builder and Service Builder serve different purposes: Service Builder supports the data and service layer, while REST Builder supports the REST API contract and implementation structure.

7. Understanding Data Relationships

Data modeling becomes increasingly important as applications grow.

Consider a system that manages students and courses. A simple implementation might store a course name directly in each student record. However, when many students belong to the same course, this approach can introduce duplication and inconsistent values.

A separate Course entity provides a more structured model.

For example:

Course

  • courseId
  • courseName
  • duration

Student

  • studentId
  • name
  • email
  • courseId

A one-to-many relationship would allow one Course to be associated with multiple Student records.

The relationship must be implemented explicitly in the data model and service logic. The backend should also validate referenced identifiers to prevent records from pointing to nonexistent entities.

This principle applies beyond student management: whenever one entity references another, the relationship and its validation rules should be part of the application design.

8. Testing API Operations

API testing verifies that the defined contract, service logic, security controls, and persistence behavior work together.

Tools such as Postman, Swagger, and GraphiQL can be used to test supported operations independently of the application's user interface.

A practical testing process includes:

  1. Retrieve data: Execute a GET request or GraphQL query and inspect the result.
  2. Create data: Submit a valid POST request or supported GraphQL mutation.
  3. Verify persistence: Retrieve the record again and confirm that the operation produced the expected result.
  4. Test authentication: Repeat requests with valid credentials, missing credentials, and invalid tokens where applicable.
  5. Test permissions: Verify that callers without the required access cannot perform restricted operations.
  6. Test invalid input: Submit missing fields, invalid values, or nonexistent identifiers.
  7. Check error handling: Confirm that the response accurately reflects the outcome.

For asynchronous or batch operations, a response such as HTTP 202 Accepted means the request has been accepted for processing; it does not necessarily prove that all processing has finished.

Tests should follow the documented behavior of each endpoint rather than assuming that every successful HTTP response means the same thing.

9. Production Readiness Considerations

A working proof of concept demonstrates that the main flow is possible, but additional validation is required before production use.

Consider the following areas:

  • Input validation and error handling.
  • Duplicate records and invalid identifiers.
  • Pagination and performance for larger data sets.
  • Update and delete behavior where supported.
  • Batch processing and partial failures.
  • Validation of relationships between entities.
  • Expired tokens and insufficient permissions.
  • Accurate OpenAPI and GraphQL schemas.
  • Logging, monitoring, and operational troubleshooting.

These checks help ensure that an API remains predictable, secure, and maintainable as the number of clients and records grows.

Conclusion

Headless REST APIs and GraphQL provide different ways to expose backend functionality in Liferay DXP.

REST organizes operations around resources and HTTP methods, while GraphQL uses a schema to support queries and mutations. Both require an appropriate implementation layer, clear service responsibilities, controlled access, and reliable data handling.

Service Builder helps establish the data and service infrastructure, while REST Builder supports the creation of REST API structures from an OpenAPI specification. GraphQL provides a separate interface for the operations exposed through its schema.

The key takeaway is that a reliable API is more than an endpoint that returns data. It needs a well-defined contract, appropriate security, maintainable business logic, correct persistence, and testing that verifies the complete request flow.

With these foundations, external applications can interact with Liferay DXP through well-defined APIs without depending on its user interface.

Working on something similar? See how ATS Global approaches Liferay DXP development, or browse our case studies.

Found this useful? Share it with your network.

Let’s work together

Ready to build something great?

Tell us about your project. We respond within one business day and can provide a customised quote for your requirements.