Skip to main content

Transform an Oracle DB as OpenData using Oracle REST Data Services

Set up Oracle REST Data Services (ORDS) in Docker to transform your Oracle DB into OpenData with a powerful REST API. Includes filtering and pagination.

· 18 min read
View raw
Christophe
Markdown, WSL and Docker lover ~ Insatiable curious.
Cet article existe aussi en français — lire la version française.

This article is over a year old but was reviewed on July 30, 2026 — the content is still accurate.

Transform an Oracle DB as OpenData using Oracle REST Data Services

⚡
TL;DR

This tutorial demonstrates how to transform your Oracle Database into a powerful RESTful API using Oracle REST Data Services (ORDS) in Docker. Building on a previously established Oracle DB container, this guide walks you through deploying the ORDS container, linking it to your database, and configuring schemas for REST access. You'll learn how to REST-enable specific database tables or views using Oracle SQL Developer and then access your data via auto-generated OpenAPI endpoints. The article also covers how to use URL parameters for advanced querying, including filtering, sorting, and pagination.

In a previous article (Running Oracle Database Server as a Docker container), we've created a Docker container called oracle-db where a Human Resources database is running.

In this article we'll partially expose the database on the web using OpenAPI so, in short, we'll allow authorized people to access, f.i., http://oursite/api/employees to get the list of all employees. That generated OpenAPI schema can be linted, by the way; see Validate your OpenAPI schema against the Belgif REST standards.

Exactly the same way we've done with PostgREST (see my Don't query your PostgreSQL db anymore, prefer PostgREST) but, this time, with an Oracle database.

And the magic will happen thanks Oracle REST Data Services aka ords.

Here's where we're headed: the same Oracle table, now reachable as plain JSON over HTTP.

Getting the list of employees as JSON using curl

In the previous article (Running Oracle Database Server as a Docker container), we've created a Docker container called oracle-db where a Human Resources database is running.

Please read that article and follow steps so, before continuing here, you've a running Docker container as described below.

Some prerequisites​

Before you begin make sure...

  • You've a running Docker container called oracle-db
  • The container is thus running on your localhost
  • The port number to access your database is 1521
  • The credentials to use is SYS (or SYSTEM) and admin for the password
  • The container contains a database called ORCLPDB1 (also called service name) which is the Human Resources sample of Oracle (refer to the mentioned article) and
  • Our database oracle-db is running on a network called oracle

Download a Docker image for Oracle REST Data Services​

Unlike the Docker Oracle Database Server image, we don't need any credentials for downloading the official Oracle ORDS image.

Simply run docker pull container-registry.oracle.com/database/ords-developer:latest to get it. It's a 2.2 GB file size image.

>user@machine: ~/yourproject

Create an Oracle ORDS container​

We'll need to proceed step-by-step.

Create a volume for the ORDS configuration​

ORDS requires two folders, one for what it calls its secrets and one for its configuration files. Let's use a Docker self managed volume for the configuration files so please run docker volume create ords_config in your console.

Configure the connection string​

In order to be able to start ORDS, the official doc states we need to create a file called conn_string.txt with our connection string. In our use case, here is the file we need to create:

>user@machine: ~/yourproject

Information

If you've a doubt about which value has to be used as service_name; start a sqlplus console (docker exec -it oracle-db sqlplus sys/admin@ ORCLPDB1 as sysdba) and run SELECT global_name FROM global_name; in SQL*Plus.

Getting service name

The official ORDS Docker image comes with APEX (which is the abbreviation for Oracle Application Express).

Based on the documentation, Oracle Application Express (APEX) is a low-code development platform that enables you to build scalable, secure enterprise apps, with world-class features, that can be deployed anywhere. See https://apex.oracle.com/ for more information.

For our needs, we just want ORDS; we don't need APEX. For this purpose, we'll initialise the IGNORE_APEX OS variable to TRUE.

Create the container​

Your values for this page
default
default
default
default
default
Applies to the commands on this pageSaved on this device only

To create our ORDS (without APEX) container; please run:

>user@machine: ~/yourproject

The `docker run` explained

  • -d: the ORDS will run as a daemon service,
  • --rm: once the service is terminated, the Docker container will be removed,
  • --network: we should run ORDS on the same network than our database; i.e. oracle
  • -p 8181:8181: we've to map the intern port 8181 used by ORDS on our host and, for simplicity, we'll expose the port 8181 also,
  • -e IGNORE_APEX=TRUE: as mentioned, we want ORDS; not APEX so tells Docker to not install APEX,
  • -v ./ords_secrets/:/opt/oracle/variables: as stated in the documentation, we need to provide a file called conn_string.txt and to map that file in the /opt/oracle/variables inside the container and, finally,
  • -v ords_config:/etc/ords/config/: we'll use a self-managed Docker volume to keep the configuration files of ORDS.

That command will add the ORDS layer in your database (based on the connection string; which is ORCLPDB1 for us).

Installation of ORDS

On subsequent runs, we shouldn't provide connection string anymore.

Once ORDS has been installed as done here above, if you need to rerun the ORDS container, we should no more provide the secret so we have to remove the -v ./ords_secrets/:/opt/oracle/variables flag.

If you need to run the container once more; here is the command line:

>user@machine: ~/yourproject

We need to create our user in our database​

Before being able to use ORDS and access objects using an HTTP request, we should create a new user in our database and give it some rights.

Run docker exec -it oracle-db sqlplus sys/admin@ ORCLPDB1 as sysdba to start a SQL*Plus console and connect to the PDB database.

We need to create a user for ORDS; let's say user hr. Please run this statement: CREATE USER hr IDENTIFIED BY admin;

Then grant permissions: GRANT CONNECT, RESOURCE, UNLIMITED TABLESPACE TO hr;

Create the ORDS user

Once created, we need to exit the console (started using the sys user) and reconnect with our user.

Type thus exit to quit the sqlplus console and run docker exec -it oracle-db sqlplus hr/admin@ ORCLPDB1.

Once back in the sqlplus console (logged in as user hr), please run : EXECUTE ORDS.ENABLE_SCHEMA;.

Enabling the ORDS Schema

(read ORDS 101: Enabling Oracle Schemas for HTTPS/REST if you want deeper info)

Caution

If you get an error at this level, it means ORDS wasn't installed in your database. Please read again the Create the container chapter.

Start ORDS web interface​

At this stage, we've installed ORDS, configured our database to use it and we've created a user called hr. We can surf to http://localhost: 8181/ords and connect to the ORDS dashboard:

http://localhost:8181/ords/_/landing
ORDS Welcome page

Note

APEX wasn't installed and thus disabled

Use hr and admin, our custom user, for the login page:

http://localhost:8181/ords/_/landing
ORDS login page

Right now, we can ask ORDS to see the list of objects already accessible; simply surf to http://localhost: 8181/ords/hr/open-api-catalog/ to get ... an empty list.

This is normal since we should specify which object (a table, a view, a stored procedure) can be accessible or not. But, yes, by accessing http://localhost: 8181/ords/hr/open-api-catalog/ and getting a JSON answer; we can confirm ORDS is running fine.

The http://localhost: 8181/ords/hr/open-api-catalog/ page is called the Schema Metadata (documentation).

REST enable database objects​

So, we need to enable each object one by one. The easier way is to use Oracle SQL Developer. If you don't have it, just download it for free.

Running Oracle SQL Developer

Create a new connection with these settings:

Name
Human Resources - ORDS
Username
hr
Role
default
Password
admin
Hostname
127.0.0.1
Port
1521
Service name
orclpdb1

SQL Developer - Database connection

Click on the Save button then on the Connect one.

Just after the login, by clicking on the list of tables or views, there is nothing.

This is normal, we've just created our hr user and the associated hr schema and we don't have objects in that schema.

Right now, there is nothing to REST enable

Adding an employee view in our hr schema​

Let's connect to our database and display the list of employees: please run docker exec -it oracle-db sqlplus sys/admin@ ORCLPDB1 as sysdba; then SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL FROM SYSTEM.EMPLOYEES;.

Getting the list of employees

Now, we're sure we've access to the table, let's create a view in our hr schema to display the table content:

  • run GRANT SELECT ON SYSTEM.EMPLOYEES TO hr; and
  • create the view by running CREATE VIEW HR.EMPLOYEES AS SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY, COMMISSION_PCT, MANAGER_ID, DEPARTMENT_ID FROM SYSTEM.EMPLOYEES;.

If we wish to check our view:

  • Type exit in the sqlplus console since you're connected as sys,
  • In the Linux console, run docker exec -it oracle-db sqlplus hr/admin@ ORCLPDB1 to connect as hr then
  • run SELECT * FROM HR.EMPLOYEES;.

As you can see, you'll obtain the list of employees.

So, at this stage, we've added a view called employees in our hr schema and it works.

Don't use SELECT * FROM

It's a very bad practice to use SELECT * FROM ..., always make sure to select the needed fields.

Go back to the Oracle SQL Developer interface, right-click on the Views node and choose Refresh.

You'll see the added view:

The employees view

Right-click on the EMPLOYEES view and click on Enable REST Service....

Enabling REST

Make sure to check the Enable object checkbox, give a name to your object and, for this small tutorial, uncheck the Authorization required checkbox.

REST enable the employees view

Click on the Next button and just pay attention on the SQL tab:

REST enable the employees view - DDL part

You'll get the DDL to run in a console if you want to REST enable the table by code; not by using the GUI.

The last part is to jump now in the ORDS website (http://localhost: 8181/ords/hr/_sdw/?nav=rest-workshop). Please refresh the page and you'll now see you've one object in the AUTOREST area:

http://localhost:8181/ords/hr/_sdw/?nav=rest-workshop
There is one object in the AUTOREST area

Click on that area and you'll see the object (which is a view) we'll REST enabled previously:

http://localhost:8181/ords/hr/_sdw/?nav=rest-workshop
Employees is AUTOREST enabled

Look at the Open in a new tab icon on the image above. Click on that button and you'll get your records as a JSON response:

http://localhost:8181/ords/hr/employees/
Getting the list of employees as JSON REST answer

The http://localhost: 8181/ords/hr/employees/ is called the Object Data (documentation)

Since it's nothing more than a URL, you can use it with any tools you want, f.i. using curl. The command to run is curl http://localhost:8181/ords/hr/employees/ | jq — the same output already teased at the top of this article.

And what about the OpenAPI catalog​

Please go back to your browser and access http://localhost: 8181/ords/hr/open-api-catalog/ again.

This time, you'll see that you've one item called EMPLOYEES.

If you want to get the description of that resource, the page http://localhost: 8181/ords/hr/open-api-catalog/employees/ can be visited to get f.i. the list of fields exposed.

That page is called the Object Metadata (documentation)

Let's create a new view for the fun:

  • Run docker exec -it oracle-db sqlplus sys/admin@ ORCLPDB1 as sysdba then
  • In the SQL console, please run GRANT SELECT ON SYSTEM.DEPARTMENTS TO HR; followed by
  • CREATE VIEW HR.DEPARTMENTS AS SELECT DEPARTMENT_ID, DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID FROM SYSTEM.DEPARTMENTS; and
  • Jump in the Oracle SQL Developer interface, refresh the list of view, right-click on the new departments view

Now, by refreshing http://localhost: 8181/ords/hr/open-api-catalog/ again, you'll see you've now a second item called DEPARTMENTS.

http://localhost:8181/ords/hr/open-api-catalog/
Accessing the metadata-catalog
Working with REST​

Go to http://localhost: 8181/ords/hr/_sdw/?nav=rest-workshop or, from the hamburger menu, click on the REST item.

http://localhost:8181/ords/hr/_sdw/?nav=rest-workshop
REST dashboard

Using Paging, Filtering and Ordering Options on the Querystring (optional — skip if the default endpoint is enough)​

When accessing to an endpoint (like http://localhost: 8181/ords/hr/employees/), we can manipulate the URL to add parameters like paging, filtering or ordering options.

These are called JSON QBE for JSON query-by-example.

See the official documentation: https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/adsdi/overview-soda-filter-specifications-qbes.html

Pagination​

Doc

Unless specifically configured, ORDS will use pagination, i.e. it will limit the list of records to 25 rows.

So, if you've more than 25 employees, the endpoint http://localhost: 8181/ords/hr/employees/ will only return the first 25 of them.

By accessing a "full" page like http://localhost: 8181/ords/hr/employees/, we'll in fact just receive a specific number of records (as configured in ORDS). At the end of the JSON answer, there will be navigation links:

http://localhost:8181/ords/hr/employees/
Pagination

As you can see on the image; the JSON answer will provide a list of records (in an items so-called array) then a few properties like hasMore (true/false), limit i.e. the number of rows in each call, offset is the "page" (when 0 the first 25 rows are displayed, when 1 then rows 26 till 50, ...), count is the number of rows in the answer.

So, while hasMore is true, there are still records to obtain with a subsequent request.

There are two parameters to control paging of result data: offset and limit.

So we can run multiple requests like http://localhost: 8181/ords/hr/employees/?offset=25, http://localhost: 8181/ords/hr/employees/?offset=50 and so on.

We can define the number of records by page using the limit querystring parameter: http://localhost: 8181/ords/hr/employees/?limit=100.

Note

The official documentation strongly discourages removing the limit (and thus asking for all records at once). For this reason there is no way to remove the limit. If you really wish to get the full list, try with a very high number like limit=1000000 (one million).

Filtering​

The general syntax is using /?q={"column":{"$eq":"value"}}.

Greater than​

For instance, who earn more than 20,000€ as salary? http://localhost: 8181/ords/hr/employees/?q={"salary":{"$gt":20000}} will apply a filter on salary > 20000.

http://localhost:8181/ords/hr/employees/?q={"salary":{"$gt":20000}}
Who earns more than 20k
Equal​

To find all employees called Steven: http://localhost: 8181/ords/hr/employees/?q={"first_name":{"$eq":"Steven"}}

http://localhost:8181/ords/hr/employees/?q={"first_name":{"$eq":"Steven"}}
Steven
Instring / contains / like​

Employees with the pattern alex in their first name: http://localhost: 8181/ords/hr/employees/?q={"first_name":{"$instr":"alex"}} (will match f.i. Alexander or Alexis).

http://localhost:8181/ords/hr/employees/?q={"first_name":{"$instr":"alex"}}
Contains Alex

Who is working as ICT? Here the like operator will be the one to use: http://localhost: 8181/ords/hr/employees/?q={"job_id":{"$like":"IT_%"}} (IT_% means starts with)

http://localhost:8181/ords/hr/employees/?q={"job_id":{"$like":"IT_%"}}
Who works for IT
Complex filtering​

We can also use AND like in this example: http://localhost: 8181/ords/hr/employees/?q={"first_name":{"$instr":"alex"},"salary":{"$gt":5000}} i.e. retrieve all people having alex in their first name and earning more than 5,000€

http://localhost:8181/ords/hr/employees/?q={"first_name":{"$instr":"alex"},"salary":{"$gt":5000}}
Contains Alex and earn more than 5,000

Using complex filtering

The following URL http://localhost: 8181/ords/hr/employees/?q={"job_id":{"$like":"%CLERK"},"salary":{"$gt":2899},"hire_date":{"$gt":{"$date":"2016-12-31T12:59:59Z"}}} will return every employee who:

  • working as clerk (job_id ending by the CLERK word),
  • having a salary greater than 2,899€ and
  • hired as from 1st January 2017.
http://localhost:8181/ords/hr/employees/?q={"job_id":{"$like":"%CLERK"},"salary":{"$gt":2899},"hire_date":{"$gt":{"$date":"2016-12-31T12:59:59Z"}}}
Using a combination of filters

Sorting​

The general syntax is using /?q={"$orderby":{"fieldname1":"asc","fieldname2":"desc"}}.

To sort on the first name desc and, then based on the salary (the higher first): http://localhost: 8181/ords/hr/employees/?q={"$orderby":{"first_name":"desc","salary":"desc"}}.

http://localhost:8181/ords/hr/employees/?q={"$orderby":{"first_name":"desc","salary":"desc"}}
Ordering on the firstname and salary DESC

Swagger​

ORDS can generate an openapi.json file to use with Swagger. Read more Manage & Monitor Oracle Database with REST APIs

Further reading​


🦋 Join the conversation on BlueskyRequires a free Bluesky account to like, repost or comment. This block talks to Bluesky's public API directly from your browser (no cookies, no sign-in) — that means your IP address reaches Bluesky's servers just by this section loading.

Loading comments…

Was this article helpful?
Did you try the steps in this article?