Creating Your First Liferay Object: A Step-by-Step Tutorial
Published on July 8, 2026 by Wasim
What You'll Build
This tutorial walks through creating a working Contact Object from scratch — a simple business contact management model. By the end you'll have a custom data model with six fields, an auto-generated admin UI, live REST and GraphQL APIs, basic role permissions, and one real record created and queried — all without writing code, using only the Liferay Control Panel.
Prerequisites: Liferay DXP or Portal 7.4+ (or the equivalent 2024.Q4+ / 2025.Qx / 2026.Qx cloud release), plus the Administrator or Object Administrator role. Check with your site administrator if you're unsure which role you have — Object creation requires "Object Definitions" permission.
Step 1: Navigate to Objects
Log in to Liferay, open the Global Menu (top-left grid/hamburger icon), and under Control Panel click Objects:
Global Menu → Control Panel → Objects
If this is your first Object, the list will be empty aside from any system-defined Objects Liferay ships with. See the official Creating and Managing Objects guide for the full reference.
Step 2: Create the Object Definition
Click Create Object Definition and fill in the basic details:
| Field | Value | Notes |
|---|---|---|
| Label | Contact |
Singular, human-readable name |
| Plural Label | Contacts |
Used in menus and API URLs |
| Name | Contact |
Auto-fills from Label; internal system name |
| Scope | Site |
Or "Instance" for availability across all sites |
| Storage Type | Default (Liferay's database) |
Alternative: an external table, if configured |
You'll also see three optional toggles — Enable Workflow (approval steps before a record goes live), Enable Comments (discussion threads on records), and Enable Categorization (tag records with categories/tags). Leave all three off for this tutorial; you can turn them on later without losing data.
Click Save. Liferay creates the base Object shell and takes you to its detail page, ready for fields.
Step 3: Add Fields
Click Add Field for each piece of data the Contact needs.
Full Name — Type: Text, Required: Yes, Indexed: Yes (enables search/filter).
Email — Type: Text, Required: Yes, Unique: Yes. Setting Unique: Yes means Liferay rejects any new record whose email already exists elsewhere in the Object — the simplest way to prevent duplicate contacts.
Phone — Type: Text, Required: No, Indexed: No.
Status — Type: Picklist, Required: Yes. A Picklist is a predefined dropdown; when you select it, Liferay asks you to create a new options list or reuse an existing one. Create a new list called Contact Status with the options Active, Inactive, Pending, and set Active as the default.
Date Added — Type: Date, Required: No.
Notes — Type: Long Text, Required: No. Use Long Text for anything that might run more than a sentence or two — Liferay renders it as a larger text area in the generated form.
Your finished field list:
| Field | Type | Required | Unique |
|---|---|---|---|
| Full Name | Text | Yes | No |
| Text | Yes | Yes | |
| Phone | Text | No | No |
| Status | Picklist | Yes | No |
| Date Added | Date | No | No |
| Notes | Long Text | No | No |
Step 4: Review Field Order
Most Object editors let you drag and drop fields into a logical order, which determines how they appear in the auto-generated form. A sensible order here is simply Full Name → Email → Phone → Status → Date Added → Notes.
Step 5: Publish the Object
Click Publish. Liferay validates the field configuration and, once it checks out, activates the Object. This single click generates:
- A database table behind the scenes
- REST API endpoints at
/o/c/contacts - An updated GraphQL schema
- An admin UI (list view plus a create/edit form)
- Default permissions, initially restricted to Administrators
You can safely add new fields after publishing at any time. Changing a field's type once it holds data is riskier — Liferay will warn you if a change could affect existing records.
Step 6: Explore the Auto-Generated Admin UI
Go back to Global Menu → Control Panel → Objects → Contact (or, if the Object is site-scoped, look for a new Contacts menu item under that site's Content section). You'll find a list/table view of all records (empty for now), a New Contact button, column headers matching your fields, and basic sort/filter controls — all generated without any UI work on your part.
Step 7: Create Your First Record
Click New Contact. Liferay presents a form built directly from your field definitions. Fill in sample data:
Full Name: Jane Rivera
Email: jane.rivera@example.com
Phone: 555-0142
Status: Active
Date Added: 2026-07-08
Notes: Initial contact via conference lead.
Click Save, and Jane Rivera's record appears in the list view immediately.
Step 8: Verify the Auto-Generated API
The API is live the moment you published in Step 5 — no separate deployment step. Open the API Explorer at http://your-instance:8080/o/api and look for Contact under REST Applications; you'll see the full OpenAPI spec with all six CRUD endpoints already generated.
Test it directly:
curl -X GET 'http://localhost:8080/o/c/contacts' \
-H 'Authorization: Bearer YOUR_TOKEN'
{
"items": [
{
"id": 40001,
"fullName": "Jane Rivera",
"email": "jane.rivera@example.com",
"phone": "555-0142",
"status": "Active",
"dateAdded": "2026-07-08",
"notes": "Initial contact via conference lead.",
"dateCreated": "2026-07-08T11:20:00Z",
"dateModified": "2026-07-08T11:20:00Z"
}
],
"totalCount": 1,
"pageNumber": 1,
"pageSize": 20
}
Seeing your record come back as JSON confirms the data model, admin UI, and API all came from the single configuration you just did. For the full range of what these endpoints support — filtering, sorting, relationships, authentication — see REST APIs Auto-Generated by Liferay Objects.
Step 9: Set Basic Permissions
New Objects are restricted to Administrators by default. To open Contacts up to another role, go to the Object's Permissions tab, choose a role (e.g., a custom Sales Team role), and assign granular permissions — View, Add, Update, Delete, and Permissions (who else can manage access).
A common real-world setup for a sales role:
| Permission | Sales Team |
|---|---|
| View | ✓ |
| Add | ✓ |
| Update | ✓ |
| Delete | – |
| Manage Permissions | – |
This lets salespeople create and edit contacts without being able to delete records or change who else has access. Click Save.
Step 10: Confirm Everything Works End-to-End
Run through this quick checklist before calling the Object done:
- Object appears in Control Panel → Objects
- Fields display correctly in the auto-generated form
- A new record can be created via the UI and shows up in the list view
GET /o/c/contactsreturns the record via the API- A
POSTrequest can create a record via the API - The role you assigned can access Contacts according to the permissions you configured
If all of those hold, your first Object is production-ready.
Common Beginner Mistakes to Avoid
Forgetting to mark required fields. If a field like Email isn't marked Required: Yes, users can save incomplete records — set it on anything your business logic depends on.
Skipping unique constraints. Without Unique: Yes on Email, duplicate contacts with the same address will be allowed. Decide up front which fields must be unique.
Overcomplicating the first version. It's tempting to add twenty fields immediately. Start with 5–8 essential fields, publish, test, and expand later — adding fields afterward is easy, but changing types or removing fields on populated data is riskier.
Skipping permission configuration. An Object left accessible only to Administrators means no one else can actually use it. Assign role permissions before considering it "done."
Ignoring picklist reuse. If you build a similar Object later (say, an Application Object with its own status field), check whether you can reuse an existing picklist definition instead of creating a duplicate list of the same options.
What Gets Created Behind the Scenes
For reference, here's everything Liferay generates automatically the moment you publish — none of it requires manual setup:
| Component | Auto-Generated |
|---|---|
| Database table | ✓ |
| REST endpoints (GET, POST, PUT, PATCH, DELETE) | ✓ |
| GraphQL schema | ✓ |
| OpenAPI/Swagger documentation | ✓ |
| Admin list view | ✓ |
| Admin create/edit form | ✓ |
| Field validation (required, unique) | ✓ |
| Default permission structure | ✓ |
| Search indexing (for indexed fields) | ✓ |
Next Steps
Once your first Object is working, natural extensions include adding relationships (linking Contact to a Company Object), adding Object Actions to trigger automated logic like a notification on status change, customizing the UI layout, enabling Workflow so records require approval, or building a custom frontend that consumes the REST/GraphQL API directly. Each builds on the foundation from this tutorial — there's no need to start over.
Summary
You created an Object Definition (Contact), added six fields spanning Text, Picklist, Date, and Long Text types, published it, created a record through the auto-generated UI, verified the REST API returns live data, and configured role-based permissions — all through the Control Panel, with no custom code, manual database work, or separate API development.
This is the core loop you'll repeat every time you model new business data in Liferay: define fields, publish, and the UI, API, and permissions scaffolding all come with it.
Related Reading
- Liferay Objects Explained: Building Custom Data Models Without Code — the concepts behind what you just built.
- REST APIs Auto-Generated by Liferay Objects — filtering, sorting, relationships, and authentication for the API you tested in Step 8.
- Liferay Low-Code: Build Business Apps Without the Hand-Coding — how Objects fit alongside Workflows and interface design.
- Browse all Liferay tools — generators and reference utilities for Liferay developers.


