Use a DataObjectHandler
- Last Updated: August 11, 2026
- 11 minute read
- OpenEdge
- Version 12.8
- Documentation
The Progress OpenEdge.Web.DataObjectHandler (DOH) is a
built-in WebHandler class that enables you to design your own ABL
service interface and expose ABL data objects as RESTful resources in a configurable
way. It is available via the WEB transport in Progress
Application Server for OpenEdge (PAS for OpenEdge).
If you have developed Progress ABL Data Object Services, you have already used the DOH under the hood.
How the DOH works
The DOH class implements theProgress.Web.IWebHandler interface, and is a more dynamic and
business-ready form of the OpenEdge.Web.WebHandler
class. With the DOH, you create an ABL service, set it to use the OpenEdge.Web.DataObjectHandler class, and then map custom
service endpoints to ABL classes and procedures in a JSON mapping file.At runtime, when a service endpoint is called, the DOH transforms the HTTP request into an ABL object and invokes the associated ABL class or procedure. It also transforms the output from the ABL class or procedure into an HTTP response.
Should I use a user-defined WebHandler, the DOH or a mapped REST service?
Although you can also define a custom service interface using a
mapped REST service (that uses the PAS for OpenEdge REST transport) or a
user-defined WebHandler, a DOH-based ABL service offers the
following advantages over the mapped REST service:
- It supports more content types.
- It is easier to debug, since the DOH is an ABL class. You can also readily inspect and modify its JSON mapping file.
- It is more customizable; you can extend its functionality by accessing its class events. To learn more about these class events, see the white paper on using the DataObjectHandler.
- It uses the WEB transport, which is the recommended way to
expose your ABL applications as RESTful services. This is because the WEB
transport uses the ABL
WebHandlerclass and thus provides better insight and control over your ABL service.
Overview of steps to use the DOH
- Create an ABL service — You begin by creating an ABL service that uses the DOH class.
- Create a mapping file — You then create a mapping file with the same name as the ABL service.
- Define custom service endpoints — You start describing the service interface in the mapping file by defining your custom endpoints.
- Associate HTTP verbs with operation
handlers — For each service endpoint, you associate an HTTP verb (GET,
PUT, POST, etc) with one of the following operation handlers:
void, that returns only a status codefile, that returns the contents of a fileentity, that invokes an ABL class or procedure
- (Optional) Add mapping metadata for OpenAPI output — Add descriptions, access control, and service-level schemas in the mapping file so generated OpenAPI output is clearer and more complete.
Create an ABL service
The first step is to create an ABL service that uses the DOH. Perform this task as follows:
- Right-click the root project name in the Project Explorer view in Progress Developer Studio and select .
- Select WEB in the Transport drop-down.
- Enter a name for the service, (such as
HelloService). - In the WebHandler section, choose Select existing and then browse and select OpenEdge.Web.DataObject.DataObjectHandler.
- Add a single resource URI that matches the service name
(
/HelloService). - Click Finish.
Create a mapping file
After creating a DOH-based ABL service, you describe its service
interface in JSON in a mapping file with the extension .map. The DOH uses this mapping file to execute the service's
operations. If a client application attempts to call an endpoint or service that is
not described in the mapping file, the DOH returns a 404 (Not Found) error.
Create a new text file in your ABL web app project's /PASOEContent/WEB-INF/openedge folder with the same
name as the ABL service and with the extension .map (for example, HelloService.map). The
matching filename instructs the DOH to use the mapping file for the service's
operations. Placing it in the openedge folder makes it
discoverable in the application PROPATH.
At minimum, you need to define a single service within a services object, like this:
|
The service name must match the ABL service and resource URI name
that you set while creating the ABL service. The "version" property
defines the version of the service and should follow the Semantic Versioning pattern
(for example,. "1.0.0"). The JSON snippet, as shown in this
example, provides just enough to make the service discoverable but does not do much
else. Without this minimum configuration, the server would only return an HTTP-404
response for the service.
Define custom service endpoints
In the JSON mapping file, define custom service endpoints under an
operations object. For example:
|
http://localhost/CustomerApp/web/HelloService/Greetinghttp://localhost/CustomerApp/web/HelloService/Message
Associate HTTP verbs with operation handlers
For each custom service endpoint, you associate HTTP verbs with operation handlers. You can set up operation handlers for all HTTP verbs that are supported by the WEB transport. In addition to request handling, each verb definition can include metadata such as descriptions, access control roles, and schema details, which improves generated OpenAPI Specification output.
Supported HTTP verbs are:
- GET
- PUT
- POST
- DELETE
- HEAD
- OPTIONS
- TRACE
- PATCH
Specify each verb that you want to use as a JSON object under the service endpoint in the mapping file. For example:
|
Within each verb, define the MIME content type of the data that is returned by the endpoint. For example:
|
All MIME types (text/,
image/, audio/, video/, application/, multipart/) are supported.
|
Next, map each HTTP verb with one operation handler. The DOH class provides the following operation handlers:
void, which returns only a status codefile, which returns the contents of a fileentity, which invokes an ABL class or procedure
To map an HTTP verb with an operation handler, nest the operation handler under the verb as shown in this example:
|
file and void operation handlers are simple key-value pairs whereas the
entity handler is a JSON object. This is because the entity handler requires
further information (such as the ABL class or procedure name, the method that
will be invoked, etc).Add mapping metadata for OpenAPI output
Data Object Handler mapping files can include metadata that improves the OpenAPI output generated from your service definitions. In addition to routing and handler selection, you can describe services, operations, and parameters, and provide security and schema information that the OpenAPI writer can use when it builds the specification.
The most useful properties are:
description: Adds customer-facing text for a service, operation, or parameter. Use it at the service level or within each HTTP verb object. (See the "Description property in mapping metadata" section below for more detail.)accessControl: Specifies one or more roles required to invoke an operation. Define it as an array within each HTTP verb object. (See the "Access control in mapping metadata" section below for more detail.)schemas: Defines service-level JSON Schema content that can be used to describe request and response bodies in generated OpenAPI output. (See the "Service-level JSON Schema" section below for more detail.)
Example:
|
| Class | OAS version |
|---|---|
|
3.0.x |
|
3.1.x |
|
3.2.x |
IOpenAPIServiceWriter interface.
The default writer used by the CatalogWebHandler class is
OpenAPI31ServiceWriter. Choose the writer version that matches
the tooling requirements of your API consumers.After you update the mapping file, regenerate the OpenAPI output and review it to confirm that descriptions, security expectations, and schema details match runtime behavior. For more information, see ABL application catalog service in Manage Progress Application Server (PAS) for OpenEdge.
Description property in mapping metadata
You can add a description string at multiple levels throughout the mapping file. Each description flows into the corresponding location in the generated OpenAPI specification:
| Level | Where to place it | What it describes in the OpenAPI |
|---|---|---|
| Service | Directly in the service object | The info.description for the API |
| Operation | Inside each HTTP verb object (for example, "GET": {
"description": "..." }) |
The operation summary or description |
| Entity | Inside the entity object for a verb |
A description of the underlying handler or business logic |
| Parameter | Inside each argument definition in the verb's parameter list | The description field on an OpenAPI
parameter |
| Schema element | Inside a JSON Schema property definition within the
schemas object |
The description on a schema property |
| ProDataset / Temp-Table field | In the field definitions of a dataset or temp-table schema | The description on the corresponding schema
property |
Example showing descriptions at service, operation, and parameter levels:
|
All description properties are optional and backward compatible. Omitting them has no effect on service operation.
Access control in mapping metadata
The accessControl property specifies one or more security roles
required to invoke an operation. Define it as an array of strings within each HTTP
verb object:
|
Role names should match the roles defined in your oeablSecurity.csv configuration file or your Spring Security configuration.
"acl". The
DataObjectHandler has always expected the property name
"accessControl". If you previously used "acl"
in your mapping files based on the schema definition, rename it to
"accessControl" for the property to take effect.How access control affects OpenAPI output
By default, the OpenAPI writer does not include security information in generated output. To expose access control metadata in your OpenAPI specification, you must programmatically enable it in your OpenAPI service writer configuration. This is an intentional opt-in design. Upgrading to a version that supports this feature does not change existing OpenAPI output behavior.
When security output is enabled:
- If you configure a named security scheme (such as
bearerAuthoroauth2), the writer mapsaccessControlroles to standard OpenAPI security requirement objects under that scheme. - If no security scheme is configured, the writer outputs roles using an extension
property named
x-required-roleson each operation. - Operations whose
accessControlroles indicate the requesting user does not have access are omitted from the generated specification entirely. This prevents unauthorized users from discovering endpoints they cannot call.
accessControl property in the mapping file adds a
second layer, allowing developers to express finer-grained, role-based access within
the application logic.Because exposing role information in generated OpenAPI output reveals details about your security model, the OpenAPI service writer requires this to be enabled programmatically in application code rather than toggled through a configuration file. This keeps the decision to disclose security metadata within your source-controlled and reviewed deployment pipeline.
Service-level JSON Schema
When your service operations accept or return data that is not backed by an ABL
Temp-Table or ProDataset, for example, a plain JSON request body or a custom
response structure, you can define JSON Schema objects at the service level. The
OpenAPI writer uses these definitions to generate
components/schemas entries and reference them from the
appropriate request or response bodies in the specification.
Defining schemas
You add a schemas object directly inside the service definition.
Each named entry follows the standard JSON Schema format:
|
Schema definitions can be nested to any depth. You can add a description to the schema itself and to individual properties. Both flow into the generated OpenAPI output.
Referencing schemas from operations
To associate a schema with a request or response body, set the
ablName of the body parameter to match the
schema name you defined:
|
The OpenAPI writer matches the ablName of each argument with a
field or body message element against the
service-level schemas entries. When a match is found, the generated
specification uses a $ref to the corresponding schema definition
rather than an untyped object.
ablName property was chosen as the
matching key so that multiple arguments can share the same schema definition. Any
argument whose ablName matches an entry in schemas
resolves to that schema, regardless of how many arguments reference it. Different
names point to different schemas — for example, CustomerOrder (a
single object) and CustomerOrders (an array of
CustomerOrder items) would each map to their own definition.
You can use standard JSON Schema $ref declarations within your
schema definitions to express these relationships, which OpenAPI viewers resolve and
render accordingly.body and field
message element types can be matched against service-level JSON Schema definitions.
However, path parameters, header parameters, and query parameters are always
received as simple strings and do not use schema references.How JSON Schema coexists with ABL schema types
The DOH supports three types of schema definitions in mapping files:
| Schema type | Identified by | Use case |
|---|---|---|
| ProDataset | Contains "type": "dataset" structure with nested
tables |
ABL DataSet-backed operations |
| Temp-Table | Contains "type": "table" structure with field
definitions |
ABL Temp-Table-backed operations |
| JSON Schema | Contains "type": "object" or "type":
"array" at the top level |
Non-ABL JSON structures |
The DOH inspects the structure of each schema entry to determine its type. If a definition does not match the expected format for a dataset or temp-table, it falls through to be treated as a JSON Schema and is passed through to the OpenAPI writer as-is.