Skip to main content
RapidDev - Software Development Agency
bubble-integrationsBubble API Connector

Oracle Database

Connect Bubble to Oracle Database using Oracle REST Data Services (ORDS), which auto-generates HTTPS endpoints over your Oracle tables and PL/SQL procedures. Bubble's API Connector calls these ORDS endpoints with Basic Auth or OAuth2 Bearer credentials in a Private header — keeping database authentication server-side. No SQL*Net port 1521 connection is required or possible from Bubble; ORDS is Oracle's official REST bridge and is free to license.

What you'll learn

  • Why ORDS is the only supported Bubble-to-Oracle connection path (no SQL*Net, no JDBC from Bubble)
  • How to enable REST on an Oracle schema and specific tables using ORDS
  • How to configure Bubble's API Connector with Basic Auth or OAuth2 credentials as Private headers
  • How to handle the ORDS `items` response wrapper in Bubble's 'Use as Data' data type configuration
  • How to expose and call Oracle PL/SQL stored procedures from Bubble workflows
  • How to paginate Oracle data in Bubble Repeating Groups using ORDS limit and offset parameters
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced18 min read2 hoursDatabase & BackendLast updated July 2026RapidDev Engineering Team
TL;DR

Connect Bubble to Oracle Database using Oracle REST Data Services (ORDS), which auto-generates HTTPS endpoints over your Oracle tables and PL/SQL procedures. Bubble's API Connector calls these ORDS endpoints with Basic Auth or OAuth2 Bearer credentials in a Private header — keeping database authentication server-side. No SQL*Net port 1521 connection is required or possible from Bubble; ORDS is Oracle's official REST bridge and is free to license.

Quick facts about this guide
FactValue
ToolOracle Database
CategoryDatabase & Backend
MethodBubble API Connector
DifficultyAdvanced
Time required2 hours
Last updatedJuly 2026

ORDS: Oracle's official REST bridge that makes Bubble integration possible

Oracle Database communicates natively over SQL*Net (TNS protocol) on port 1521 — a binary, stateful protocol that requires a persistent connection and a specific driver (JDBC, OCI, or similar). Bubble's API Connector supports only HTTP/HTTPS REST calls. There is no way to connect Bubble directly to Oracle's native protocol.

Oracle REST Data Services (ORDS) solves this. ORDS is Oracle's official middleware that auto-generates REST endpoints over your Oracle schema: a REST-enabled table `EMPLOYEES` in schema `HR` becomes accessible at `GET /ords/hr/employees/` — returning JSON with pagination. PL/SQL stored procedures and packages can be exposed as POST endpoints. ORDS is free to license and runs as a standalone WAR file, a Docker container, or natively on Oracle Autonomous Database (where it comes pre-installed).

Oracle Autonomous Database Always Free tier provides two fully managed Oracle Database instances at no cost. Each comes with ORDS pre-installed, an HTTPS URL, and a browser-based SQL editor (Database Actions). This is the fastest path to testing Bubble + Oracle without any on-premises infrastructure.

For enterprise teams with existing on-premises Oracle Enterprise or Standard installations, ORDS runs as a separate middleware layer in front of the database, typically deployed on an internal server with an HTTPS reverse proxy (NGINX or Apache). The Bubble integration pattern is identical whether ORDS fronts Autonomous Database or a self-managed Oracle instance — only the base URL changes.

Bubble's API Connector proxies all calls through Bubble's servers, so your ORDS credentials in a Private header are never exposed to users' browsers. This is particularly important for Oracle environments where a single ORDS credential can access multiple schemas.

Integration method

Bubble API Connector

Call Oracle REST Data Services (ORDS) endpoints from Bubble's API Connector with Basic Auth or OAuth2 Bearer credentials in a Private header.

Prerequisites

  • An Oracle Database instance with ORDS deployed — Oracle Autonomous Database Always Free (cloud.oracle.com) or self-managed Oracle with ORDS WAR installed
  • Oracle schema access to REST-enable tables and create ORDS OAuth2 clients
  • A Bubble app on any plan (Free plan works for basic API Connector; paid plan required for API Workflows / Backend Workflows)
  • The API Connector plugin installed in your Bubble app (Plugins tab → Add plugins → search 'API Connector' by Bubble → Install)

Step-by-step guide

1

Enable ORDS on your Oracle schema and REST-enable specific tables

Log into your Oracle Database via SQL Developer Web (Autonomous DB) or SQL Developer desktop. Connect to the schema you want to expose to Bubble. Run the ORDS REST-enablement commands for your schema and the specific tables. For Autonomous Database, these commands run in Database Actions (SQL worksheet). For self-managed Oracle with ORDS installed, run them in a connected SQL session. REST-enabling a schema creates the URL namespace (`/ords/{schema}/`). REST-enabling a table auto-generates four endpoints: GET (list all rows with pagination), GET by ID, POST (insert), and PUT/PATCH by ID (update). You can also REST-enable Oracle database views — this is the recommended approach for complex queries: create an Oracle view that pre-joins multiple tables and REST-enable the view as a single GET endpoint. Bubble calls one endpoint and receives the complete joined data without the complexity of multiple calls.

ords-enable-schema.sql
1-- REST-enable the schema (run as ADMIN or schema owner with ORDS_ADMIN privileges)
2BEGIN
3 ORDS.ENABLE_SCHEMA(
4 p_enabled => TRUE,
5 p_schema => 'HR',
6 p_url_mapping_type => 'BASE_PATH',
7 p_url_mapping_pattern => 'hr',
8 p_auto_rest_auth => FALSE
9 );
10 COMMIT;
11END;
12/
13
14-- REST-enable a specific table
15BEGIN
16 ORDS.ENABLE_OBJECT(
17 p_enabled => TRUE,
18 p_schema => 'HR',
19 p_object => 'EMPLOYEES',
20 p_object_type => 'TABLE',
21 p_object_alias => 'employees',
22 p_auto_rest_auth => FALSE
23 );
24 COMMIT;
25END;
26/

Pro tip: Set `p_auto_rest_auth => TRUE` if you want ORDS to require authentication by default on all endpoints in this schema. For development you may start with FALSE to test endpoints, but switch to TRUE before connecting to Bubble to ensure all calls require credentials.

Expected result: Your ORDS base URL (`https://{your-ords-host}/ords/hr/employees/`) now responds to GET requests with a JSON response containing an `items` array and pagination metadata. Test this URL in your browser — you should see your Oracle table data in JSON format.

2

Create ORDS OAuth2 credentials for Bubble

For production Bubble integrations, OAuth2 Client Credentials is more secure than Basic Auth — it issues short-lived Bearer tokens rather than sending a username/password on every request. In Oracle Database Actions (or via SQL), create an OAuth2 client for Bubble. This generates a `client_id` and `client_secret`. To get a Bearer token, exchange these credentials at the ORDS token endpoint (`/ords/{schema}/oauth/token`) using Basic Auth with client_id:client_secret. The token response includes an `access_token` (Bearer token) valid for a defined duration. Alternatively, use ORDS First Party Auth (Basic Auth) with an Oracle schema-level user — simpler but sends credentials on every request. For Basic Auth: base64-encode `username:password` and use `Authorization: Basic <encoded>` as the header value. Both approaches work with Bubble's Private header. For teams with existing Oracle DBAs, OAuth2 is the recommended enterprise approach. For small teams evaluating the integration, Basic Auth is faster to set up.

ords-oauth2-setup.sql
1-- Create an OAuth2 Client in ORDS (run in schema that owns the REST endpoints)
2BEGIN
3 OAUTH.CREATE_CLIENT(
4 p_name => 'bubble-app',
5 p_grant_type => 'client_credentials',
6 p_description => 'Bubble web app integration',
7 p_privilege_names => NULL
8 );
9 COMMIT;
10END;
11/
12
13-- Retrieve client_id and client_secret
14SELECT NAME, CLIENT_ID, CLIENT_SECRET FROM USER_ORDS_CLIENTS;
15
16-- Token endpoint (POST from Bubble API Connector to get Bearer token)
17-- POST https://{ords-host}/ords/{schema}/oauth/token
18-- Header: Authorization: Basic base64(client_id:client_secret)
19-- Body: grant_type=client_credentials

Pro tip: ORDS OAuth2 Bearer tokens expire. Add a Bubble workflow that refreshes the token when it is within a defined time of expiry (check the `expires_in` field from the token response). Store the token expiry timestamp in a Bubble App State variable and compare it on page load before making ORDS data calls.

Expected result: You have either an OAuth2 client_id/client_secret pair or ORDS First Party Auth credentials (username/password). You can exchange OAuth2 credentials for a Bearer token at the ORDS `/oauth/token` endpoint. Either credential set is ready to configure in Bubble's API Connector.

3

Configure Bubble's API Connector with ORDS credentials

In your Bubble editor, go to the Plugins tab. If 'API Connector' by Bubble is not installed, click 'Add plugins', search 'API Connector', and install it. Click 'API Connector', then 'Add another API'. Name it 'Oracle ORDS'. In the 'Root URL' field, enter your ORDS base URL including the schema path: `https://{your-ords-host}/ords/hr` (no trailing slash). In 'Shared headers', click 'Add a shared header'. For Basic Auth: Key `Authorization`, Value `Basic ` followed by the base64-encoded `username:password` string. For OAuth2: Key `Authorization`, Value `Bearer ` followed by your access token (you will need to refresh this; see the tip below). Check the 'Private' checkbox for the Authorization header — mandatory. This ensures Bubble processes the header server-side and never sends raw Oracle credentials to any browser. Add a second shared header: Key `Content-Type`, Value `application/json` (not private). If your ORDS instance requires a specific Accept header, add `Accept: application/json` as well.

oracle-ords-connector-config.json
1{
2 "root_url": "https://{your-ords-host}/ords/hr",
3 "shared_headers": [
4 {
5 "key": "Authorization",
6 "value": "Basic <base64-encoded-username:password>",
7 "private": true
8 },
9 {
10 "key": "Content-Type",
11 "value": "application/json",
12 "private": false
13 }
14 ]
15}

Pro tip: For OAuth2 Bearer tokens: add a separate Bubble API Connector group (not the ORDS group) configured to call the `/oauth/token` endpoint with Basic Auth (client_id:client_secret). Store the returned `access_token` in a Bubble App State variable. Use a Dynamic `Authorization: Bearer {{app_state.ordsToken}}` header in the ORDS API group, marked Private. Run the token exchange call on 'Page is loaded' and refresh before expiry.

Expected result: The 'Oracle ORDS' API group appears in your API Connector plugin with the correct root URL and Authorization header marked Private. You are ready to add individual ORDS endpoint calls.

4

Create a GetRows call and initialize it using 'Use as Data'

Inside the 'Oracle ORDS' API group, click 'Add another call'. Name it 'GetEmployees' (or the appropriate table name). Set method to GET. Set endpoint to `/employees/` (the REST-enabled table alias). ORDS supports two useful query parameters: `limit` (number of rows per page, default 25) and `offset` (starting row for pagination). Add these as query parameters in the call with default values `25` and `0`. ORDS also supports `?q=` for JSON Query-by-Example filtering: add an optional `q` parameter with a default empty value `{}` — Bubble passes Oracle Query-by-Example JSON to filter rows (e.g., `{"DEPARTMENT_ID":90}` returns only department 90 employees). Click 'Initialize the call'. Bubble fetches your Oracle data via ORDS. The response follows the ORDS envelope format: `{"items":[...], "hasMore":true, "limit":25, "offset":0, "count":25}`. In the 'Use as Data' dialog, Bubble may detect the outer wrapper object as the data type. You need to configure Bubble to use the inner `items` array as the list type — set the response as a List of the row type by mapping `items[*]` in the response configuration. Name this data type after your Oracle table (e.g., 'OracleEmployee').

ords-get-employees-response.json
1// ORDS GET endpoint — table query with pagination and filter
2GET /employees/?limit=25&offset=0&q={}
3
4// ORDS response envelope format
5{
6 "items": [
7 {
8 "EMPLOYEE_ID": 100,
9 "FIRST_NAME": "Steven",
10 "LAST_NAME": "King",
11 "EMAIL": "SKING",
12 "DEPARTMENT_ID": 90,
13 "SALARY": 24000
14 }
15 ],
16 "hasMore": true,
17 "limit": 25,
18 "offset": 0,
19 "count": 25,
20 "links": [
21 { "rel": "self", "href": "https://{ords-host}/ords/hr/employees/" },
22 { "rel": "next", "href": "https://{ords-host}/ords/hr/employees/?offset=25" }
23 ]
24}

Pro tip: If Bubble's 'Use as Data' maps the ORDS wrapper object (with `items`, `hasMore`, `limit` fields) instead of the inner row type, you may need to bind your Repeating Group to the detected `items` subtype. In the Repeating Group data source, use 'Get data from external API → GetEmployees' and then access `result's items` as the list. This extra step is specific to ORDS's wrapped response format.

Expected result: The GetEmployees call initializes successfully and Bubble detects the response structure. An 'OracleEmployee' data type is created with fields matching your Oracle table columns. ORDS returns Oracle column names in uppercase (EMPLOYEE_ID, FIRST_NAME) — these become the Bubble field names.

5

Create InsertRow, UpdateRow, and CallProcedure calls

For inserting data, add a call named 'InsertEmployee'. Set method to POST, endpoint to `/employees/`. The request body is a JSON object with your Oracle table column names as keys (ORDS uses Oracle column names, uppercase). Initialize with a test payload and confirm a new row appears in your Oracle table. Set 'Use as' to 'Action'. For updating, add 'UpdateEmployee'. Set method to PUT, endpoint to `/employees/{{EMPLOYEE_ID}}` — ORDS auto-generates PUT endpoints that update a row by its primary key. The path parameter is the Oracle primary key value. Body contains only the fields to update. Set 'Use as' to 'Action'. For PL/SQL procedures exposed as ORDS REST handlers: in Oracle, create a REST handler module for your procedure (`ORDS.DEFINE_MODULE`, `ORDS.DEFINE_TEMPLATE`, `ORDS.DEFINE_HANDLER`). The procedure's IN parameters become JSON body fields in the POST call; OUT parameters return in the JSON response. Add a 'CallProcedure' call in Bubble: POST to the procedure handler endpoint, body matches the IN parameter names. This is one of the most powerful patterns — years of Oracle business logic becomes callable from a Bubble button click without rewriting any PL/SQL.

ords-write-calls.sql
1// InsertEmployee — POST body (Oracle column names, uppercase)
2{
3 "FIRST_NAME": "{{firstName}}",
4 "LAST_NAME": "{{lastName}}",
5 "EMAIL": "{{email}}",
6 "DEPARTMENT_ID": "{{departmentId}}",
7 "HIRE_DATE": "{{hireDate}}",
8 "JOB_ID": "{{jobId}}"
9}
10
11// UpdateEmployee — PUT endpoint
12// PUT /employees/{{EMPLOYEE_ID}}
13{
14 "SALARY": "{{newSalary}}",
15 "DEPARTMENT_ID": "{{newDepartment}}"
16}
17
18// PL/SQL procedure ORDS handler registration
19BEGIN
20 ORDS.DEFINE_MODULE(
21 p_module_name => 'hr_procedures',
22 p_base_path => '/procedures/'
23 );
24 ORDS.DEFINE_TEMPLATE(
25 p_module_name => 'hr_procedures',
26 p_pattern => 'approve_expense'
27 );
28 ORDS.DEFINE_HANDLER(
29 p_module_name => 'hr_procedures',
30 p_pattern => 'approve_expense',
31 p_method => 'POST',
32 p_source_type => ORDS.SOURCE_TYPE_PLSQL,
33 p_source => '
34 DECLARE
35 v_result VARCHAR2(100);
36 BEGIN
37 approve_expense_pkg.process(
38 p_expense_id => :expense_id,
39 p_approver => :approver,
40 p_result => v_result
41 );
42 :result := v_result;
43 END;'
44 );
45 COMMIT;
46END;
47/

Pro tip: For PL/SQL procedures exposed via ORDS, Bubble cannot display Oracle DBMS_OUTPUT — procedures must return data via bind variables in the handler source (`:result`) or write to a table that Bubble subsequently queries. Design procedures with explicit return parameters for any values Bubble needs to display.

Expected result: You have four API Connector calls: GetEmployees (Data), InsertEmployee (Action), UpdateEmployee (Action), and CallProcedure (Action). Each is initialized with a real Oracle response. You can test each call from the API Connector editor before building Bubble UI.

6

Build Bubble Repeating Groups with ORDS pagination and connect write workflows

Add a Repeating Group to your Bubble page. Set its data source to 'Get data from external API' → GetEmployees. Pass `limit` as `25` and `offset` as `(currentPage - 1) × 25` where `currentPage` is a custom state initialized to `1`. Access the OracleEmployee fields on each cell: `current cell's OracleEmployee's FIRST_NAME`, etc. Note Oracle column names are uppercase — your Bubble field names follow this convention. Add 'Previous Page' and 'Next Page' buttons that decrement and increment `currentPage`. Disable 'Previous Page' when `currentPage = 1` and disable 'Next Page' when the ORDS response's `hasMore` field is `false`. For the insert form: create a 'Submit' button workflow that calls InsertEmployee with each form input mapped to the corresponding Oracle column parameter. After a successful insert, set `currentPage` to `1` and trigger a Repeating Group refresh. For updates: store the selected row's primary key (`EMPLOYEE_ID`) in a custom state when a user clicks an edit row button. Pass this stored ID as the `{{EMPLOYEE_ID}}` path parameter in UpdateEmployee. Set up Privacy rules under Data tab → Privacy for any Bubble data types you create from ORDS data. For RapidDev's enterprise clients, this Oracle + Bubble pattern has unlocked significant productivity gains — existing Oracle infrastructure becomes accessible to non-technical teams through a polished Bubble front-end. If you want to scope a similar project, reach out at rapidevelopers.com/contact.

Pro tip: For complex Oracle data filtering, use the ORDS `?q=` parameter with Oracle Query-by-Example JSON syntax. In Bubble, build the filter JSON as a text string using the expression builder and URL-encode it before passing as the `q` query parameter. Example: filtering by department ID — construct `{"DEPARTMENT_ID":{{selectedDeptId}}}` as a dynamic text value.

Expected result: Your Bubble page loads Oracle data via ORDS in a paginated Repeating Group. Previous/Next navigation changes the `offset` parameter and refreshes the data. Insert and update workflows write to Oracle tables. All Oracle credentials remain in Private headers and never appear in browser network traffic.

Common use cases

Read-only front-end over existing Oracle ERP data

Enterprise organizations often need non-technical teams to view Oracle ERP data — purchase orders, inventory levels, customer records — without granting them direct database access. REST-enable the relevant Oracle views (read-only, pre-joined), configure Bubble's API Connector to call the ORDS GET endpoints, and build a custom dashboard in Bubble. Users see the data they need in a modern UI without touching the underlying Oracle system.

Bubble Prompt

Build a Bubble page that loads the top 50 open purchase orders from an Oracle ORDS endpoint. Show order number, vendor name, amount, and status in a Repeating Group. Add a filter dropdown for status (Open, Pending, Approved) that passes a `?q=` JSON filter to the ORDS GET endpoint.

Copy this prompt to try it in Bubble

PL/SQL procedure execution from Bubble workflows

Your Oracle database contains years of business logic in PL/SQL stored procedures — approval workflows, complex calculations, data validation routines. Rather than rewriting this logic in Bubble, expose the procedures as ORDS REST handlers and call them from Bubble workflows via POST. Input parameters pass as JSON body fields; output parameters return in the response. Bubble triggers the procedure on user action (form submit, button click) and displays the result.

Bubble Prompt

Create a Bubble workflow triggered by an 'Approve' button click that calls an Oracle PL/SQL procedure via an ORDS POST endpoint. Pass the record ID and current user's Oracle username as body parameters. Display the procedure's return value (approval confirmation code) in a popup on the Bubble page.

Copy this prompt to try it in Bubble

Data entry form writing to Oracle tables

Allow Bubble users to submit data that inserts rows into Oracle tables via ORDS POST endpoints. Use cases include field service workers submitting inspection reports, HR staff entering employee records, or finance teams logging expense items — all writing directly to Oracle through a Bubble form without needing Oracle client software or VPN access.

Bubble Prompt

Set up a Bubble form where users fill in employee details (name, department, hire date, salary). On submit, call the ORDS POST endpoint for the EMPLOYEES table to insert a new row. After a successful response, show a confirmation message and clear the form. Set up a Bubble Privacy Rule on the form page to restrict write access to users with the HR role.

Copy this prompt to try it in Bubble

Troubleshooting

Initialize call returns 'There was an issue setting up your call' or 401 Unauthorized

Cause: The Authorization header format is incorrect, the credentials are wrong, or ORDS has authentication enabled on the schema but the credentials provided do not match. For Basic Auth, the base64 encoding of `username:password` must be exact. For OAuth2, the Bearer token may have expired.

Solution: Test your ORDS endpoint directly in a browser or with curl first: `curl -H 'Authorization: Basic <encoded>' https://{ords-host}/ords/hr/employees/`. If you get data, the issue is in how Bubble's API Connector sends the header. Verify there is exactly one space between 'Basic' and the base64 string. For OAuth2 Bearer tokens, re-exchange credentials at the `/oauth/token` endpoint and update the header value in Bubble. Remember the token expiry.

Repeating Group shows the ORDS wrapper object (items, hasMore, limit) instead of actual Oracle row data

Cause: Bubble's 'Use as Data' detected the ORDS envelope object as the data type rather than the inner `items` array. The Repeating Group is bound to the outer wrapper instead of the list of rows.

Solution: In the Repeating Group data source, after selecting GetEmployees from 'Get data from external API', access the `items` field: the expression should read like 'Get data from external API (GetEmployees)'s items. Set the Repeating Group's type of content to the OracleEmployee type and the data source to the `items` list from the API response. If the detected type did not correctly identify `items` as a list, re-initialize the call with a response that contains at least 2 rows so Bubble correctly identifies it as an array.

ORDS GET endpoint returns 200 but with an empty items array despite data existing in Oracle

Cause: The ORDS `?q=` filter parameter syntax is incorrect, the table alias does not match the REST-enabled alias defined in ORDS, or the Oracle schema user credentials do not have SELECT privileges on the table.

Solution: Test the endpoint without any `?q=` filter first. Verify the table alias in your Bubble endpoint path matches what was set in `ORDS.ENABLE_OBJECT`'s `p_object_alias` parameter (not necessarily the table name). Check that the ORDS-authenticating user has SELECT privilege: `GRANT SELECT ON HR.EMPLOYEES TO ords_public_user;` (or the equivalent in your ORDS configuration).

Bubble workflow times out when calling ORDS endpoints for large Oracle tables

Cause: The ORDS GET call has no `limit` parameter and returns thousands of rows, or the Oracle query behind a complex view runs a slow full table scan without an index.

Solution: Add `?limit=50` as a query parameter on all ORDS GET calls in Bubble. In Oracle, check the execution plan for the queries ORDS generates using `EXPLAIN PLAN FOR` — ensure the WHERE conditions used in `?q=` filters use indexed columns. For complex Oracle views, consider creating Oracle materialized views with refresh schedules to pre-aggregate data and speed up ORDS responses.

'Workflow API is not enabled' error when trying to use Bubble Backend Workflows for scheduled Oracle writes

Cause: API Workflows (Backend Workflows) in Bubble require a paid plan. This feature is not available on the Bubble Free plan.

Solution: Upgrade to at least the Bubble Starter plan ($32/mo) to enable API Workflows. Once upgraded, go to Settings → API → enable 'This app exposes a Workflow API' and backend workflow endpoints become available. For scheduled Oracle writes (e.g., daily summary inserts), use Bubble's 'Schedule API workflow' action within a Backend Workflow.

Best practices

  • Always mark ORDS credentials (Basic Auth base64 string or OAuth2 Bearer token) as Private in the Bubble API Connector shared header. Oracle database credentials in a non-private header are visible in browser developer tools network requests.
  • Use Oracle views instead of raw tables for ORDS REST endpoints wherever possible. Views let you pre-join tables, apply row-level Oracle VPD security, and limit which columns are exposed — all without changing the ORDS configuration or Bubble API calls.
  • Add `?limit=50` to all ORDS GET calls. ORDS default limit is 25 rows but unlimited queries via Bubble can timeout. Always paginate explicitly and handle the `hasMore` and `offset` fields for multi-page navigation.
  • For OAuth2 Bearer tokens, implement token refresh logic in Bubble: store `expires_in` as a Unix timestamp in App State on the token exchange, compare it on page load, and re-exchange credentials if within 5 minutes of expiry before making ORDS data calls.
  • Set `p_auto_rest_auth => TRUE` on your ORDS-enabled schema in production. This ensures every ORDS endpoint requires authentication and prevents accidentally exposing data via unauthenticated URLs if a Bubble Private header misconfiguration occurs.
  • Create Oracle database users with minimum required privileges for ORDS: `SELECT` on read-only views, `INSERT/UPDATE` on specific tables for write endpoints. Avoid connecting ORDS as `SYS`, `SYSTEM`, or a DBA-level user.
  • Use the Bubble Logs tab to debug ORDS call failures. Bubble logs the API response, which includes Oracle-specific error messages like ORA-01400 (cannot insert NULL) and ORA-00001 (unique constraint violated). These Oracle error codes appear in the ORDS JSON error response and help identify data-level issues without needing Oracle access.
  • Set up Data tab → Privacy rules in Bubble for any data types created from ORDS responses. Without privacy rules, Bubble's built-in search may expose Oracle records to unauthorized users through Bubble's own query mechanisms.

Alternatives

Frequently asked questions

Is ORDS really free? Do I need to license it separately from Oracle Database?

Yes, Oracle REST Data Services is free to download, license, and use. It is available as a standalone WAR file deployable on any Java application server (Tomcat, GlassFish, Jetty) or as a Docker image. For Oracle Autonomous Database (cloud), ORDS comes pre-installed at no additional cost. You only pay for the Oracle Database license itself — ORDS is the recommended, supported REST layer on top and adds no additional licensing fees.

Can I use Bubble's free plan to connect to Oracle ORDS?

Yes, the Bubble API Connector works on the Free plan for basic read and write operations via ORDS. You need a paid Bubble plan (Starter or above) only for API Workflows — Bubble's server-side scheduled or triggered backend workflows that run autonomously. If your integration only involves user-triggered page loads (fetching data) and form submissions (writing data), the Free plan is sufficient.

Can I call Oracle PL/SQL stored procedures from Bubble?

Yes, but only if the procedure is exposed as an ORDS REST handler. You define the procedure as a POST handler using `ORDS.DEFINE_HANDLER` with `p_source_type => ORDS.SOURCE_TYPE_PLSQL`. The procedure's IN parameters become JSON body fields in the Bubble API Connector POST call. OUT parameters return in the JSON response. Note: Bubble cannot receive Oracle DBMS_OUTPUT — all procedure outputs must be declared as OUT parameters or written to a table that Bubble subsequently reads.

How do I filter Oracle data in Bubble — for example, show only employees in a specific department?

ORDS supports Oracle Query-by-Example (QBE) filtering via the `?q=` URL parameter. The value is a URL-encoded JSON object where keys are column names and values are filter criteria. For example, `?q={"DEPARTMENT_ID":90}` returns only rows where DEPARTMENT_ID equals 90. In Bubble, build this JSON string dynamically using the expression builder and pass it as the `q` query parameter in your GetEmployees API Connector call. For range filters, use ORDS QBE operators: `{"SALARY":{"$gt":5000}}` returns employees with salary above 5000.

What happens if my ORDS OAuth2 Bearer token expires while a user is using the Bubble app?

ORDS API calls made with an expired Bearer token return 401 Unauthorized. In Bubble, this causes the workflow step to fail. Implement proactive token refresh: store the token's `expires_in` value (converted to an absolute Unix timestamp) in a Bubble App State variable when the token is first fetched on page load. Before every ORDS call that matters, add a workflow step that checks the current time against the stored expiry. If within 5 minutes of expiry, re-run the token exchange workflow to get a fresh token and update the App State before proceeding with the ORDS call.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire Bubble integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.