Liferay REST Module
This guide focuses on manually creating Liferay REST modules (without REST Builder) for full control over JAX-RS implementation. We’ll cover module creation, endpoint development, security, and testing.
1. Setting Up a REST Module
Section titled “1. Setting Up a REST Module”Prerequisites
Section titled “Prerequisites”- Liferay DXP/Portal 7.4+
- Liferay Workspace (with Blade CLI installed)
- Java 11+
Install Blade CLI
Section titled “Install Blade CLI”Verify Installation
Section titled “Verify Installation”blade versionCreate Module Using Blade CLI
Section titled “Create Module Using Blade CLI”blade create -t rest -p com.company.rest -c MyCustomRest my-custom-restProject Structure
Section titled “Project Structure”graph TD
A[my-custom-rest] --> B[src/main/java]
B --> C[com/company/rest/MyCustomRestApplication.java]
B --> D[com/company/rest/resource/v1_0/MyResource.java]
A --> E[src/main/resources]
E --> F[configuration.properties]
2. Core Components
Section titled “2. Core Components”1. JAX-RS Application Class
Section titled “1. JAX-RS Application Class”@Component( immediate = true, property = { "jaxrs.application=true", "auth.verifier.guest.allowed=true" }, service = Application.class)public class MyCustomRestApplication extends Application {
@Override public Set<Object> getSingletons() { return Collections.singleton(this); }
@Override public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<>(); classes.add(MyResource.class); return classes; }}2. Resource Class (Endpoint)
Section titled “2. Resource Class (Endpoint)”@Path("/my-api/v1.0")@Produces(MediaType.APPLICATION_JSON)@Consumes(MediaType.APPLICATION_JSON)public class MyResource {
@GET @Path("/items/{id}") public Response getItem(@PathParam("id") long id) { return Response.ok( Map.of("id", id, "name", "Item " + id) ).build(); }
@POST @Path("/items") public Response createItem(ItemDTO itemDTO) { return Response.status(Response.Status.CREATED) .entity(itemDTO) .build(); }}3. DTO Class
Section titled “3. DTO Class”public class ItemDTO implements Serializable {
private long id; private String name; private String description;
// Getters & Setters // toString(), equals(), hashCode()}3. Advanced Features
Section titled “3. Advanced Features”Pagination & Sorting
Section titled “Pagination & Sorting”@GET@Path("/items")public Page<ItemDTO> getItems( @Context Pagination pagination, @Context Sort[] sorts) { List<ItemDTO> items = fetchItems(pagination, sorts); return Page.of(items, pagination, items.size());}Permission Checking
Section titled “Permission Checking”@DELETE@Path("/items/{id}")public Response deleteItem( @PathParam("id") long id, @Context PermissionChecker permissionChecker) { if (!permissionChecker.hasPermission( "DELETE_ITEM")) { throw new ForbiddenException(); } deleteItem(id); return Response.noContent().build();}Exception Handling
Section titled “Exception Handling”@Providerpublic class CustomExceptionMapper implements ExceptionMapper<Exception> {
@Override public Response toResponse(Exception e) { return Response.status(500) .entity(Map.of( "error", e.getMessage(), "code", 500 )) .build(); }}4. Build & Deploy
Section titled “4. Build & Deploy”Gradle Build (build.gradle)
Section titled “Gradle Build (build.gradle)”dependencies { compileOnly group: "com.liferay.portal", name: "release.portal.api" compileOnly group: "javax.ws.rs", name: "javax.ws.rs-api" compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations"}Build Command
Section titled “Build Command”./gradlew buildDeploy Command
Section titled “Deploy Command”blade deploy5. Testing REST Endpoints
Section titled “5. Testing REST Endpoints”cURL Examples
Section titled “cURL Examples”# GET Requestcurl -X GET "http://localhost:8080/o/my-api/v1.0/items/123" \ -u test@liferay.com:test
# POST Requestcurl -X POST "http://localhost:8080/o/my-api/v1.0/items" \ -H "Content-Type: application/json" \ -d '{"name":"New Item","description":"Test"}' \ -u test@liferay.com:testPostman Collection
Section titled “Postman Collection”{ "info": { "name": "My Custom REST API", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { "name": "Get Item", "request": { "method": "GET", "header": [ { "key": "Authorization", "value": "Basic {{base64Credentials}}" } ], "url": { "raw": "http://localhost:8080/o/my-api/v1.0/items/123" } } } ]}6. Best Practices
Section titled “6. Best Practices”- Versioning: Always include version in path (
/v1.0/) - Security: Use
@Context PermissionCheckerfor auth - Documentation: Add OpenAPI annotations
- Error Handling: Custom exception mappers
- Pagination: Support
PaginationandSort
7. Troubleshooting
Section titled “7. Troubleshooting”| Issue | Solution |
|---|---|
| 404 Not Found | Check module deployment (lb in Gogo shell) |
| 403 Forbidden | Verify permissions in resource-actions |
| JSON Parsing Error | Ensure proper DTO serialization |
Conclusion
Section titled “Conclusion”This manual REST module approach gives you:
✅ Full control over JAX-RS implementation
✅ Direct Liferay service integration
✅ Flexible security configuration
✅ Custom exception handling
For rapid development, consider Liferay REST Builder, but for complex scenarios, manual REST modules provide the most flexibility.
🚀 Next Steps:
- Learn about Headless Delivery APIs
- Check Liferay’s official REST documentation