Introduction:
- Rest Builder is Liferay’s tool to build REST and GraphQL APIs.
- Rest builder is a tool that can help developers generate RESTful web services quickly and easily.
- REST Builder can be used to create RESTful web services for a wide range of applications, including web applications, mobile applications, and Iot devices.
Benefits :
The benefits of using REST Builder include:
- Development speed:
- you avoid writing JAX-RS annotations, converters, adding support for multipart or layers to organize your code. Everything is generated.
- API scaffolding:
- pagination, filtering, searching, JSON writers, XML generation, even unit, and integration tests are generated.
- GraphQL support out of the box:
- write your REST API and get a GraphQL endpoint for free.
- JSON & XML support:
- APIs return whichever format the consumer prefers.
- Flexibility:
- REST Builder allows developers to customize the generated code to meet their specific needs.
Prerequisites :
- Liferay 7.x
- Basic understanding of Liferay
- Knowledge of API
Environmental Requirements :
- Liferay
- Eclipse IDE
- Blade CLI
- Gradle
Generating APIs with REST Builder
Follow these steps to REST Builder to create a headless REST API for your apps:- Create a project.
- Open eclipse IDE. Select the desired workspace which is created for your project and then click on launch.
- Navigate to the File → New → Other.. → Liferay Workspace Project → Enter Project name → Select Build type → Select Product Version → Then click on Finish tab. Open a new project window.
- Now create a new REST Builder module project
- Navigate to the Project name → Right click on Modules → New → Liferay Module Project and Click on “Next”
- Enter Project name
- Select “Build type” as “Gradle” → Select “Project Template name” as “REST Builder” → Then click on “Next”
- Open a new window. Enter Component class name → Enter Package name → Click on “Finish”
- The necessary file structure for Rest Builder automatically gets created as below.
- In your *-impl module’s root folder, write your OpenAPI profile in “rest-openapi.yaml” file.
Writing YAML files is tricky, so you can use the swagger editor to do it, which validates the YAML file against YAML syntax and the OpenAPI specification.
// rest-openapi.yaml
info:
description: "TestRestUsers REST API"
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
title: "TestRestUsers"
version: v1.0
openapi: 3.0.1
paths:
"/get-user":
get:
operationId: getUserById
parameters:
- in: query
name: userId
required: true
schema:
type: integer
format: int64
responses:
200:
description: "Success Response"
content:
application/json:
schema:
items:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
items:
$ref: "#/components/schemas/UserObject"
tags: ["User"]
components:
schemas:
UserObject:
properties:
userId:
type: integer
format: int64
screenName:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
statusCode:
type: integer
statusMessage:
type: string
- Once you have written your OpenAPI configuration and profile, it’s time to generate your scaffolding for REST and GraphQL.
- Now Build the REST Builder module.
In the terminal, navigate to the root directory of your rest-builder project
Execute the below command to build the rest-builder module
blade gw buildREST
Or
- The necessary file structure for the rest-builder gets created as below.
It’s also generates the *ResourceImpl class where you can write your business logic for implementation of your each service.
- After implementing all the business logic for each service, execute below command to deploy your rest-builder module
blade gw deploy
Or
Now We have performed the CRUD operation for User in OpenAPI and implemented Business Logic for each service.
Write the OpenAPI profile for the user in “xxx-impl/rest-openapi.yaml” like as below:
// rest-openapi.yaml
info:
description: "TestRestUsers REST API"
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
title: "TestRestUsers"
version: v1.0
openapi: 3.0.1
paths:
"/get-user":
get:
operationId: getUserById
parameters:
- in: query
name: userId
required: true
schema:
type: integer
format: int64
responses:
200:
description: "Success Response"
content:
application/json:
schema:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
$ref: "#/components/schemas/UserObject"
tags: ["User"]
"/get-users":
get:
operationId: getUsers
parameters:
- in: query
name: page
schema:
type: integer
- in: query
name: pageSize
schema:
type: integer
responses:
200:
description: "Success Response"
content:
application/json:
schema:
items:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
items:
$ref: "#/components/schemas/UserObject"
tags: ["User"]
"/update-user":
post:
operationId: updateUser
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
$ref: "#/components/schemas/UserObject"
responses:
200:
description: "Success Response"
content:
application/json:
schema:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
$ref: "#/components/schemas/UserObject"
tags: ["User"]
"/delete-user":
delete:
operationId: deleteUserById
parameters:
- in: query
name: userId
required: true
schema:
type: integer
format: int64
responses:
200:
description: "Success Response"
content:
application/json:
schema:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
$ref: "#/components/schemas/UserObject"
tags: ["User"]
components:
schemas:
UserObject:
properties:
userId:
type: integer
format: int64
screenName:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
statusCode:
type: integer
statusMessage:
type: string
Your APIs are available at this URL:
http://[host]:[port]/o/[APPLICATION_CLASSNAME]/[OPEN_API_VERSION]/
In our case, the API is available at this URL:
http://localhost:8080/o/api
Business Logic for the User:
Write a business logic in “xxx-impl/com.*.portal.restusers.internal.resource.v1_0” file.
1) Add OR Update User
First write the OpenAPI configuration profile to add or update a new User in “rest-openapi.yaml” file
// rest-openapi.yaml
paths:
"/update-user":
post:
operationId: updateUser
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
$ref: "#/components/schemas/UserObject"
responses:
200:
description: "Success Response"
content:
application/json:
schema:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
$ref: "#/components/schemas/UserObject"
tags: ["User"]
components:
schemas:
UserObject:
properties:
userId:
type: integer
format: int64
screenName:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
statusCode:
type: integer
statusMessage:
type: string
Write implementation logic for how to add or update User in “xxx-impl/com.*.portal.restusers.internal.resource.v1_0” file.
Note: If you have passed userId as 0, a new user is created.
If you want to update an existing user then you can pass usedId of existing user for update existing user details.
// xxx-impl/com.*.portal.restusers.internal.resource.v1_0
package com.projectname.portal.restusers.internal.resource.v1_0;
import java.util.Locale;
import javax.ws.rs.core.Response;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ServiceScope;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.service.ServiceContext;
import com.liferay.portal.kernel.service.UserLocalService;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.Validator;
import com.projectname.portal.restusers.dto.v1_0.UserObject;
import com.projectname.portal.restusers.resource.v1_0.UserResource;
/**
* @author DELL
*/
@Component(properties = "OSGI-INF/liferay/rest/v1_0/user.properties", scope = ServiceScope.PROTOTYPE, service = UserResource.class)
public class UserResourceImpl extends BaseUserResourceImpl {
@Reference
private UserLocalService userLocalService;
@Override
public UserObject updateUser(UserObject userObject) throws Exception {
long userId = userObject.getUserId();
long companyId = contextCompany.getCompanyId();
Locale locale = contextAcceptLanguage.getPreferredLocale();
User user = null;
ServiceContext serviceContext = new ServiceContext();
serviceContext.setCompanyId(companyId);
serviceContext.setUserId(contextUser.getUserId());
String firstName = userObject.getFirstName();
String lastName = userObject.getLastName();
String screenName = userObject.getScreenName();
String primaryEmailAddress = userObject.getEmail();
try {
user = Validator.isNotNull(userId) ? userLocalService.updateUser(userId, StringPool.BLANK, StringPool.BLANK,
StringPool.BLANK, false, StringPool.BLANK, StringPool.BLANK, screenName, primaryEmailAddress, false,
null, StringPool.BLANK, StringPool.BLANK, StringPool.BLANK, StringPool.BLANK, firstName,
StringPool.BLANK, lastName, GetterUtil.DEFAULT_LONG, GetterUtil.DEFAULT_LONG, true, 10, 10, 1980,
StringPool.BLANK, StringPool.BLANK, StringPool.BLANK, StringPool.BLANK, StringPool.BLANK,
StringPool.BLANK, GetterUtil.DEFAULT_LONG_VALUES, GetterUtil.DEFAULT_LONG_VALUES,
GetterUtil.DEFAULT_LONG_VALUES, null, GetterUtil.DEFAULT_LONG_VALUES, serviceContext)
: userLocalService.addUser(contextUser.getUserId(), companyId, true, null, null, false, screenName,
primaryEmailAddress, locale, firstName, StringPool.BLANK, lastName, GetterUtil.DEFAULT_LONG,
GetterUtil.DEFAULT_LONG, true, 10, 10, 1980, StringPool.BLANK,
GetterUtil.DEFAULT_LONG_VALUES, GetterUtil.DEFAULT_LONG_VALUES,
GetterUtil.DEFAULT_LONG_VALUES, GetterUtil.DEFAULT_LONG_VALUES, true, serviceContext);
userId = user.getUserId();
userObject.setUserId(userId);
userObject.setStatusCode(Response.ok().build().getStatus());
userObject.setStatusMessage("Added Successfully");
} catch (Exception e) {
_log.error(e.getMessage(), e);
userObject.setStatusCode(Response.serverError().build().getStatus());
userObject.setStatusMessage(e.getMessage());
}
return userObject;
}
public static final Log _log = LogFactoryUtil.getLog(UserResourceImpl.class);
}
Add User
Note: Here you have passed userId as 0 for add new user
Update User by userId
Note: Here you have passed the userId of the existing user.
2) Get User
First write the OpenAPI configuration profile to get User in “rest-openapi.yaml” file
// rest-openapi.yaml
paths:
"/get-user":
get:
operationId: getUserById
parameters:
- in: query
name: userId
required: true
schema:
type: integer
format: int64
responses:
200:
description: "Success Response"
content:
application/json:
schema:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
$ref: "#/components/schemas/UserObject"
tags: ["User"]
components:
schemas:
UserObject:
properties:
userId:
type: integer
format: int64
screenName:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
statusCode:
type: integer
statusMessage:
type: string
Write Implementation logic for how to get User By UserId in “xxx-impl/com.*.portal.restusers.internal.resource.v1_0” file.
// xxx-impl/com.*.portal.restusers.internal.resource.v1_0
package com.projectname.portal.restusers.internal.resource.v1_0;
import javax.validation.constraints.NotNull;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ServiceScope;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.service.UserLocalService;
import com.projectname.portal.restusers.dto.v1_0.UserObject;
import com.projectname.portal.restusers.resource.v1_0.UserResource;
/**
* @author DELL
*/
@Component(properties = "OSGI-INF/liferay/rest/v1_0/user.properties", scope = ServiceScope.PROTOTYPE, service = UserResource.class)
public class UserResourceImpl extends BaseUserResourceImpl {
@Reference
private UserLocalService userLocalService;
@Override
public UserObject getUserById(@NotNull Long userId) throws Exception {
User user = userLocalService.getUser(userId);
UserObject userObject = getUserFromModel(user);
return userObject;
}
private UserObject getUserFromModel(User user) {
UserObject userObject = new UserObject();
userObject.setScreenName(user.getScreenName());
userObject.setFirstName(user.getFirstName());
userObject.setLastName(user.getLastName());
userObject.setEmail(user.getEmailAddress());
return userObject;
}
public static final Log _log = LogFactoryUtil.getLog(UserResourceImpl.class);
}
Get User By userId
3) Delete User
First write the OpenAPI configuration profile to delete existing User in “rest-openapi.yaml” file
// rest-openapi.yaml
paths:
"/delete-user":
delete:
operationId: deleteUserById
parameters:
- in: query
name: userId
required: true
schema:
type: integer
format: int64
responses:
200:
description: "Success Response"
content:
application/json:
schema:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
$ref: "#/components/schemas/UserObject"
tags: ["User"]
components:
schemas:
UserObject:
properties:
userId:
type: integer
format: int64
screenName:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
statusCode:
type: integer
statusMessage:
type: string
Write Implementation logic for how to delete User in “xxx-impl/com.*.portal.restusers.internal.resource.v1_0″ file.
// xxx-impl/com.*.portal.restusers.internal.resource.v1_0
package com.projectname.portal.restusers.internal.resource.v1_0;
import javax.validation.constraints.NotNull;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ServiceScope;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.service.UserLocalService;
import com.projectname.portal.restusers.dto.v1_0.UserObject;
import com.projectname.portal.restusers.resource.v1_0.UserResource;
/**
* @author DELL
*/
@Component(properties = "OSGI-INF/liferay/rest/v1_0/user.properties", scope = ServiceScope.PROTOTYPE, service = UserResource.class)
public class UserResourceImpl extends BaseUserResourceImpl {
@Reference
private UserLocalService userLocalService;
@Override
public UserObject deleteUserById(@NotNull Long userId) throws Exception {
UserObject userObject = new UserObject();
try {
userLocalService.deleteUser(userId);
userObject.setStatusCode(200);
userObject.setStatusMessage("Deleted Successfully");
} catch (Exception e) {
_log.error(e.getMessage(), e);
userObject.setStatusCode(500);
userObject.setStatusMessage(e.getMessage());
}
return userObject;
}
public static final Log _log = LogFactoryUtil.getLog(UserResourceImpl.class);
}
Delete User By userId
4) Get All Users
First write the OpenAPI configuration profile to get all Users in “rest-openapi.yaml” file
// rest-openapi.yaml
paths:
"/get-users":
get:
operationId: getUsers
parameters:
- in: query
name: page
schema:
type: integer
- in: query
name: pageSize
schema:
type: integer
responses:
200:
description: "Success Response"
content:
application/json:
schema:
items:
$ref: "#/components/schemas/UserObject"
application/xml:
schema:
items:
$ref: "#/components/schemas/UserObject"
tags: ["User"]
components:
schemas:
UserObject:
properties:
userId:
type: integer
format: int64
screenName:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
statusCode:
type: integer
statusMessage:
type: string
Write Implementation logic for how to get all Users in “xxx-impl/com.*.portal.restusers.internal.resource.v1_0″ file.
// xxx-impl/com.*.portal.restusers.internal.resource.v1_0
package com.projectname.portal.restusers.internal.resource.v1_0;
import java.util.ArrayList;
import java.util.List;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ServiceScope;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.service.UserLocalService;
import com.liferay.portal.vulcan.pagination.Page;
import com.liferay.portal.vulcan.pagination.Pagination;
import com.projectname.portal.restusers.dto.v1_0.UserObject;
import com.projectname.portal.restusers.resource.v1_0.UserResource;
/**
* @author DELL
*/
@Component(properties = "OSGI-INF/liferay/rest/v1_0/user.properties", scope = ServiceScope.PROTOTYPE, service = UserResource.class)
public class UserResourceImpl extends BaseUserResourceImpl {
@Reference
private UserLocalService userLocalService;
@Override
public Page getUsers(Pagination pagination) throws Exception {
List userObjects = new ArrayList<>();
List users = userLocalService.getUsers(pagination.getStartPosition(), pagination.getEndPosition());
for (User user : users) {
UserObject userObject = getUserFromModel(user);
userObjects.add(userObject);
}
return Page.of(userObjects, pagination, userObjects.size());
}
private UserObject getUserFromModel(User user) {
UserObject userObject = new UserObject();
userObject.setScreenName(user.getScreenName());
userObject.setFirstName(user.getFirstName());
userObject.setLastName(user.getLastName());
userObject.setEmail(user.getEmailAddress());
return userObject;
}
public static final Log _log = LogFactoryUtil.getLog(UserResourceImpl.class);
}
Get All Users
Note: Here you have passed page and page-size for getting users.