Connect FlutterFlow to Oracle Database using Oracle REST Data Services (ORDS), which auto-generates REST endpoints over your tables and PL/SQL procedures. FlutterFlow calls these endpoints via API Calls — no custom backend required. Autonomous Database wallet credentials stay on the ORDS host, never inside FlutterFlow or compiled Dart code.
| Fact | Value |
|---|---|
| Tool | Oracle Database |
| Category | Database & Backend |
| Method | FlutterFlow API Call |
| Difficulty | Advanced |
| Time required | 60 minutes |
| Last updated | July 2026 |
Connecting FlutterFlow to Oracle Database via ORDS
Oracle Database is one of the most widely deployed enterprise database systems in the world, powering everything from banking platforms to healthcare systems. When founders with an existing Oracle backend want to build a mobile or web app with FlutterFlow, they hit an immediate architectural wall: Flutter apps run on the user's device and cannot open a raw TCP connection to Oracle's SQL*Net protocol on port 1521. Attempting to connect directly would expose your database to the public internet — a critical security risk — and is technically blocked by mobile OS networking rules in any case.
The good news is that Oracle has a first-class answer to this problem that MySQL and SQL Server lack out of the box: Oracle REST Data Services (ORDS). ORDS is a free middleware layer that sits in front of your Oracle schema and automatically generates REST endpoints for your tables, views, and PL/SQL procedures. You enable REST on a schema, point ORDS at your database, and instantly get `/ords/{schema}/{table}/` endpoints that return JSON — exactly what FlutterFlow's API Calls consume. For Oracle Autonomous Database (the cloud-managed always-free tier), ORDS is already bundled and the wallet-based mTLS connection lives on the ORDS host, invisible to FlutterFlow.
Oracle Autonomous Database has an Always Free tier that gives you two database instances at no cost, making it accessible for prototype apps. ORDS itself is free. You only need a paid Oracle plan if you're on a self-managed Oracle Enterprise installation — in which case ORDS still runs at no additional license cost. The integration with FlutterFlow follows the same API Call pattern used for any REST service: define an API Group with the base ORDS URL, add individual calls for list/create/update, map the JSON response paths, and bind to widgets. The main gotcha is ORDS's response envelope: results come nested under an `items` array with accompanying pagination metadata, so your JSON paths must account for this structure.
Integration method
FlutterFlow cannot open a raw Oracle SQL*Net socket (port 1521) from the client — a Flutter app runs on the user's device, not on a server with network access to your database. Oracle REST Data Services (ORDS) is the recommended bridge: it auto-generates REST endpoints over your Oracle schema that FlutterFlow consumes via API Call groups. For Oracle Autonomous Database, the wallet-based mTLS connection lives entirely on the ORDS server, and FlutterFlow only ever exchanges JSON.
Prerequisites
- An Oracle Database instance (self-managed or Oracle Autonomous Database — Always Free tier available)
- Oracle REST Data Services (ORDS) installed and configured against your schema, OR a custom Node/Java REST API fronting Oracle
- A FlutterFlow project (free tier is sufficient to test API Calls)
- Basic understanding of your Oracle schema — table names, column names, and any PL/SQL procedures you want to expose
- If using Autonomous Database: the wallet ZIP downloaded from Oracle Cloud console and configured on the ORDS host
Step-by-step guide
Enable Oracle REST Data Services on your schema
Before FlutterFlow can talk to Oracle, you need a REST interface in front of your database. Oracle REST Data Services (ORDS) is the recommended path because it auto-generates endpoints — you write zero API code. If you're using Oracle Autonomous Database on Oracle Cloud: ORDS is already running. Log into the Oracle Cloud Console at cloud.oracle.com, navigate to your Autonomous Database instance, and click 'Database Actions'. In the Database Actions Launchpad, open 'REST'. From here you can enable REST on specific tables: click 'Modules' then 'AutoREST' to enable automatic REST on any table. Toggle the switch next to your table name and click 'Enable'. This generates endpoints at the pattern `https://{adb-host}/ords/{schema}/{table}/`. If you're on a self-managed Oracle Database: install ORDS (download free from oracle.com/rest-data-services). Connect ORDS to your database using a configuration file where you set the JDBC URL, schema user, and password — these credentials live on the ORDS server and are never exposed to clients. Start the ORDS server on a port you choose (default 8080; put it behind an HTTPS reverse proxy like Nginx for production). Then in SQL*Plus or SQLcl, connect as your schema user and run: `BEGIN ORDS.ENABLE_SCHEMA(p_enabled => TRUE, p_schema => 'YOUR_SCHEMA', p_url_mapping_type => 'BASE_PATH', p_url_mapping_pattern => 'api', p_auto_rest_auth => FALSE); COMMIT; END;` to enable REST on the schema. Individual tables are then accessible at `/ords/api/{table}/`. Test the endpoint in your browser: `https://your-ords-host/ords/your_schema/your_table/`. You should see JSON with an `items` array. If you see a 401, ORDS authentication is enabled — configure OAuth2 in the next step before testing.
Pro tip: For a quick proof of concept, Oracle Autonomous Database Always Free tier gives you two databases at no cost, with ORDS already bundled. Create an account at cloud.oracle.com and you can be running ORDS endpoints in under 20 minutes.
Expected result: Visiting your ORDS endpoint URL in a browser returns a JSON object with an 'items' array containing your table rows and a 'hasMore' pagination field.
Configure OAuth2 authentication on ORDS
Raw ORDS endpoints with no auth are publicly accessible — anyone who knows the URL can read your data. For a production FlutterFlow app, you need to configure ORDS OAuth2 so that only your app can call the endpoints. In Oracle Database Actions (Autonomous Database) or via SQL, run the following to create an OAuth2 client. This creates a client ID and secret that your FlutterFlow app will exchange for a bearer token. The client secret must be stored on your backend or in a secure location — not in FlutterFlow directly. In Database Actions, navigate to REST → Security → OAuth Clients, then click 'New Client'. Set the Name (e.g. 'FlutterFlowClient'), Owner Email, Support URI, and select the 'Client Credentials' grant type. Copy the generated Client ID and Client Secret immediately — the secret won't be shown again. To get a bearer token, your FlutterFlow app will call the ORDS token endpoint: `POST https://{ords-host}/ords/{schema}/oauth/token` with `grant_type=client_credentials` and the client credentials as Basic Auth. This token exchange should happen via a small backend function (see Step 3 note) or you can hard-code a long-lived token for initial testing only. For simpler setups, ORDS also supports HTTP Basic authentication at the ORDS level. In FlutterFlow, this means adding an `Authorization: Basic {base64(user:pass)}` header to your API Call — but be aware this encodes the credentials in the compiled app, which is acceptable only if the ORDS user has limited, read-only permissions and you rotate credentials regularly. For a production app with write access, use OAuth2 bearer tokens from a backend endpoint. If your team already uses a Firebase project with FlutterFlow, the cleanest production path is a Firebase Cloud Function that holds the ORDS client secret, exchanges it for a bearer token, and proxies the ORDS calls — FlutterFlow calls the Firebase Function, the Function calls ORDS. This keeps all Oracle credentials off the client entirely.
1// Firebase Cloud Function proxy (Node.js) — keeps ORDS client secret server-side2const { onRequest } = require('firebase-functions/v2/https');3const { defineSecret } = require('firebase-functions/params');4const fetch = require('node-fetch');56const ORDS_CLIENT_ID = defineSecret('ORDS_CLIENT_ID');7const ORDS_CLIENT_SECRET = defineSecret('ORDS_CLIENT_SECRET');89exports.queryOracle = onRequest({ secrets: [ORDS_CLIENT_ID, ORDS_CLIENT_SECRET] }, async (req, res) => {10 res.set('Access-Control-Allow-Origin', '*');11 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }1213 // Step 1: Get ORDS bearer token14 const credentials = Buffer.from(`${ORDS_CLIENT_ID.value()}:${ORDS_CLIENT_SECRET.value()}`).toString('base64');15 const tokenResp = await fetch('https://your-ords-host/ords/myschema/oauth/token', {16 method: 'POST',17 headers: { 'Authorization': `Basic ${credentials}`, 'Content-Type': 'application/x-www-form-urlencoded' },18 body: 'grant_type=client_credentials'19 });20 const { access_token } = await tokenResp.json();2122 // Step 2: Call ORDS endpoint23 const ordsResp = await fetch(`https://your-ords-host/ords/myschema/${req.query.table}/`, {24 headers: { 'Authorization': `Bearer ${access_token}` }25 });26 const data = await ordsResp.json();27 res.json(data);28});Pro tip: For initial testing, you can use ORDS's built-in schema user authentication (Basic Auth) with a read-only DB user. Once confirmed working, upgrade to OAuth2 client credentials before going to production.
Expected result: You can successfully call your ORDS endpoint with an Authorization header and receive a valid JSON response — either via curl/Postman or through the Firebase Function proxy.
Create an API Call group in FlutterFlow pointing at your ORDS base URL
Now that ORDS is running and authenticated, you can configure FlutterFlow to call it. Open your FlutterFlow project and click 'API Calls' in the left navigation panel. Click '+ Add' and select 'Create API Group'. This group will hold all of your Oracle-related API calls under a shared base URL. In the 'Group Name' field, enter something descriptive like 'OracleDatabase' or 'ORDSAPI'. In the 'Base URL' field, enter the root of your ORDS instance: `https://your-ords-host/ords/your_schema` — replace the placeholders with your actual host and schema name. If you're using the Firebase Cloud Function proxy from Step 2, enter the Cloud Function URL instead (e.g. `https://us-central1-your-project.cloudfunctions.net`). Next, configure authentication. If you're using a bearer token directly, click on the 'Headers' tab and add a new header: Key = `Authorization`, Value = `Bearer {{token}}`. Click the '+ Add Variable' button in the Variables tab and create a variable named `token` of type String. This variable will be passed dynamically at runtime (from a stored token or an app state variable). If using Basic Auth for a read-only setup, the Value is `Basic {{base64credentials}}` — but prefer the proxy approach for write operations. If your ORDS endpoint is behind a custom domain with HTTPS, FlutterFlow will work without issues on all platforms. If you're still on HTTP during development, note that FlutterFlow web previews will block mixed-content requests — use HTTPS from day one. Click 'Save Group' to proceed. The group now appears in your API Calls panel. You'll add individual endpoint calls inside it in the next step.
1// API Group configuration reference (JSON)2{3 "group_name": "OracleDatabase",4 "base_url": "https://your-ords-host/ords/your_schema",5 "headers": [6 {7 "key": "Authorization",8 "value": "Bearer {{token}}"9 },10 {11 "key": "Content-Type",12 "value": "application/json"13 }14 ],15 "variables": [16 { "name": "token", "type": "String" }17 ]18}Pro tip: Create separate API Groups if you have multiple Oracle schemas or if some endpoints need different auth (e.g. a public read group with no auth and a write group with a bearer token).
Expected result: The 'OracleDatabase' API Group appears in your API Calls panel in FlutterFlow, showing the base URL and the Authorization header with the token variable.
Add API Calls for list, read, insert, and update operations
Inside the OracleDatabase API Group, you'll now add individual calls for each database operation. Click the group name to expand it, then click '+ Add API Call'. For a List (GET all rows) call: Name it 'ListItems' (replace 'Items' with your table name). Set Method to GET. Set the endpoint path to `/your_table/` — ORDS appends this to the base URL. In the Variables tab, add an optional variable `q` (type String) for ORDS filtering, which you'd append as a query parameter: endpoint becomes `/your_table/?q={{q}}`. Under the 'Response & Test' tab, click 'Test API Call'. If your base URL, headers, and auth are correct, you'll see the raw JSON response. Click 'Generate from Response' to auto-generate JSON Paths from the response structure. This is where the ORDS envelope matters critically: ORDS wraps all results in a top-level object with two keys: `items` (an array of your row objects) and pagination fields like `hasMore`, `limit`, `offset`, and `links`. Your JSON Paths to extract data must start with `$.items[*].column_name`. For example, to extract a column called `PRODUCT_NAME`, the JSON path is `$.items[*].PRODUCT_NAME`. Note that Oracle column names are typically uppercase — your JSON paths must match exactly. For an Insert (POST) call: Name it 'CreateItem'. Set Method to POST. Endpoint: `/your_table/`. In the Body tab, set type to JSON and add variables for each column you want to insert: `{ "COLUMN_NAME": "{{value}}" }`. ORDS accepts this format and inserts the row, returning the inserted record. For an Update (PUT) call: Name it 'UpdateItem'. Set Method to PUT. Endpoint: `/your_table/{{row_id}}` — ORDS uses the primary key in the URL path. Body: same JSON format with the updated values. For a Delete (DELETE) call: Name it 'DeleteItem'. Method DELETE. Endpoint: `/your_table/{{row_id}}`. Save each call after testing it. The test tab will show you the exact response shape so you can finalize your JSON paths before binding to widgets.
1// ORDS API Call configurations23// GET /products/ — list with optional filter4// Endpoint: /products/?q={"CATEGORY":{"$eq":"{{category}}"}}5// ORDS filter uses JSON query syntax67// Response envelope (all ORDS list endpoints):8// {9// "items": [10// { "PRODUCT_ID": 1, "PRODUCT_NAME": "Widget", "PRICE": 9.99 },11// { "PRODUCT_ID": 2, "PRODUCT_NAME": "Gadget", "PRICE": 24.99 }12// ],13// "hasMore": false,14// "limit": 25,15// "offset": 0,16// "count": 2,17// "links": [...]18// }1920// JSON Paths to map response to FlutterFlow Data Type:21// Product ID: $.items[*].PRODUCT_ID22// Product Name: $.items[*].PRODUCT_NAME23// Price: $.items[*].PRICE2425// POST /products/ — insert new row26// Body: { "PRODUCT_NAME": "{{name}}", "PRICE": {{price}}, "CATEGORY": "{{category}}" }2728// PUT /products/{{product_id}} — update row by primary key29// Body: { "PRODUCT_NAME": "{{name}}", "PRICE": {{price}} }3031// DELETE /products/{{product_id}} — delete row by primary keyPro tip: ORDS paginates list results at 25 rows by default. To fetch more, append `?limit=100&offset={{offset}}` to the endpoint. Add an `offset` variable to your API Call and increment it in FlutterFlow with a page state variable for infinite scroll.
Expected result: Each API Call in the group shows a green checkmark after a successful test. The Response & Test tab displays your Oracle rows in the ORDS items[] envelope, and JSON paths are generated and mapped to correct column names.
Map ORDS responses to Data Types and bind to widgets
Now that your API Calls are configured and tested, you need to map the response data to FlutterFlow Data Types so widgets can display it typed and structured. In the left nav, click 'Data Types' (the grid icon). Click '+ Add Data Type' and name it to match your Oracle table, e.g. 'Product'. Add a field for each Oracle column you want to display: click '+ Add Field', enter the field name (matching your ORDS JSON key, e.g. PRODUCT_NAME), and set the type (String, Integer, Double, etc.). Save the Data Type. Now go back to your API Call (API Calls → OracleDatabase → ListItems). In the 'Response & Test' tab, look at the JSON Paths section. Map each path to your Data Type fields: click the path for `$.items[*].PRODUCT_NAME`, select the 'Product' Data Type, and select the 'PRODUCT_NAME' field. Repeat for each column. FlutterFlow will now know that this API Call returns a list of Product objects. On a page in your app, add a 'ListView' widget. In the widget's properties panel on the right, look for 'Backend Query'. Click '+ Add Query', select 'API Call', and choose your 'ListItems' call from the OracleDatabase group. If the call needs an auth token, you'll need to pass it here — either from an App State variable where you stored it after login, or from a Secure Storage value via Custom Action. The ListView now has access to the list of Products from the API response. Add a child 'Container' widget inside the ListView, then add Text widgets inside it. Bind each Text widget's value to a Product field: click the Text widget → Value → Backend Data → select your API call → select the field (e.g. PRODUCT_NAME). Repeat for price, category, etc. For create/update forms, use FlutterFlow's 'Form' widget or individual TextFields. On button press, open the Action Flow Editor and add an 'API Call' action pointing to your CreateItem or UpdateItem call, mapping form field values to API variables.
Pro tip: If your ORDS returns Oracle DATE or TIMESTAMP columns, they come as strings in ISO format. Map them to FlutterFlow's 'DateTime' type and use the built-in date formatting widgets to display them cleanly.
Expected result: Your ListView populates with Oracle data at runtime. Each row shows the correct values from the items[] array. Tapping a create form and submitting it results in a new row appearing in Oracle when you refresh the list.
Expose PL/SQL procedures and test on device against Autonomous Database
One of Oracle's most powerful ORDS features is the ability to expose PL/SQL stored procedures and functions as REST endpoints — callable from FlutterFlow just like any other API Call. This lets your existing business logic (validation, complex calculations, multi-table transactions) run server-side in Oracle, called from a mobile app with no additional backend needed. To expose a PL/SQL procedure via ORDS, in SQL Developer or Database Actions SQL Worksheet, run: ```sql BEGIN ORDS.DEFINE_SERVICE( p_module_name => 'inventory', p_base_path => '/inventory/', p_pattern => 'adjust-stock/', p_method => 'POST', p_source_type => ORDS.source_type_plsql, p_source => 'BEGIN adjust_stock(:product_id, :quantity_delta); END;', p_access_control_check => TRUE ); COMMIT; END; ``` This makes `POST /ords/your_schema/inventory/adjust-stock/` callable from FlutterFlow, where the body can pass `product_id` and `quantity_delta` as JSON parameters that bind to PL/SQL parameters. In FlutterFlow, add a new API Call inside your OracleDatabase group: Name = 'AdjustStock', Method = POST, Endpoint = `/inventory/adjust-stock/`, Body = `{ "product_id": {{product_id}}, "quantity_delta": {{delta}} }`, with variables `product_id` (Integer) and `delta` (Integer). Bind this call to a stepper widget's increment/decrement button in your app. For Oracle Autonomous Database testing: the Autonomous Database wallet (cwallet.sso or tnsnames.ora files) is configured on the ORDS host at the JDBC URL level — you never interact with it from FlutterFlow. Test the full integration using FlutterFlow's 'Test Mode' first (which runs in a browser), then run a full device test via 'Local Run' on an iOS Simulator or Android Emulator to verify behavior on native. If you encounter 401 errors, your bearer token may have expired — add token refresh logic in your Cloud Function proxy.
1-- Enable ORDS on a PL/SQL stored procedure (run in SQL Developer / Database Actions)2BEGIN3 ORDS.DEFINE_MODULE(4 p_module_name => 'inventory',5 p_base_path => '/inventory/',6 p_items_per_page => 257 );89 ORDS.DEFINE_TEMPLATE(10 p_module_name => 'inventory',11 p_pattern => 'adjust-stock/'12 );1314 ORDS.DEFINE_HANDLER(15 p_module_name => 'inventory',16 p_pattern => 'adjust-stock/',17 p_method => 'POST',18 p_source_type => ORDS.source_type_plsql,19 p_source => '20 DECLARE21 v_product_id NUMBER := :product_id;22 v_quantity NUMBER := :quantity_delta;23 BEGIN24 UPDATE inventory25 SET quantity_on_hand = quantity_on_hand + v_quantity26 WHERE product_id = v_product_id;27 COMMIT;28 :status_code := 200;29 END;30 '31 );3233 COMMIT;34END;35/Pro tip: If you'd rather skip writing PL/SQL ORDS handlers yourself, RapidDev's team builds FlutterFlow + Oracle integrations like this every week — book a free scoping call at rapidevelopers.com/contact.
Expected result: Calling the PL/SQL endpoint from FlutterFlow's API Call test tab returns a 200 response and the stored procedure executes correctly. In device testing, the FlutterFlow app updates Oracle data end-to-end.
Common use cases
Enterprise inventory management mobile app
A manufacturing company has decades of product and inventory data in Oracle ERP. They want a FlutterFlow mobile app for warehouse staff to look up stock levels, log new shipments, and update quantity on hand from a tablet. ORDS exposes the inventory tables as REST endpoints that FlutterFlow reads and writes without any middleware code.
Build a FlutterFlow inventory app that lists products from an Oracle Database via ORDS, lets warehouse staff search by SKU, and submits quantity adjustments that update the stock record.
Copy this prompt to try it in FlutterFlow
Healthcare patient records lookup app
A hospital runs patient records in Oracle Database with strict PL/SQL-based business logic. A FlutterFlow app for clinical staff lets them search patients by ID, view encounter history, and trigger appointment scheduling through PL/SQL procedures exposed as ORDS REST handlers — all authenticated with OAuth2 at the ORDS layer.
Build a FlutterFlow app for hospital staff to search Oracle patient records, view appointment history, and schedule follow-ups by calling PL/SQL procedures exposed via ORDS REST endpoints.
Copy this prompt to try it in FlutterFlow
Customer portal mobile companion
A financial services firm stores customer account data in Oracle Autonomous Database. A FlutterFlow app lets customers view account balances, recent transactions, and submit support tickets. The Autonomous Database wallet and OAuth2 credentials are configured on the ORDS host; FlutterFlow authenticates users with its own auth layer and passes an app token to ORDS.
Build a FlutterFlow customer portal that reads account balances and transactions from Oracle Autonomous Database via ORDS, with OAuth2 token-based authentication at the API level.
Copy this prompt to try it in FlutterFlow
Troubleshooting
API Call returns 401 Unauthorized even though Authorization header is set
Cause: ORDS OAuth2 bearer tokens expire (typically after 60 minutes). The token stored in your app state variable has expired and ORDS is rejecting it. Alternatively, the OAuth2 client credentials are wrong or the ORDS privilege grant is missing.
Solution: Implement a token refresh flow: before making any Oracle API call, check if the stored token has expired (compare issue time + 3600s against current time). If expired, call your Cloud Function proxy to re-exchange client credentials for a new token. In FlutterFlow, use an App State variable to store both the token and its expiry timestamp. Alternatively, for a simpler setup, request tokens with a longer expiry if your ORDS configuration allows it.
API Call response shows items[] is empty or JSON paths return null, even though Oracle has data
Cause: ORDS column names are returned in uppercase (Oracle's default), but the JSON Path in FlutterFlow is referencing lowercase or mixed-case names. For example, your table column PRODUCT_NAME comes back as PRODUCT_NAME in JSON, but FlutterFlow's generated path may say $.items[*].product_name.
Solution: Open the API Call → Response & Test tab and inspect the raw response JSON carefully. Confirm the exact casing of each key in the items[] array. Update all JSON Paths to match exactly — typically all uppercase for standard Oracle schemas. If your DBA aliased columns (e.g. AS productName), the JSON key will reflect that alias. Re-generate JSON paths after confirming the correct casing.
XMLHttpRequest error on FlutterFlow web preview / published web app
Cause: ORDS does not have CORS headers configured, so the browser blocks API requests from your FlutterFlow web origin (FlutterFlow's run mode domain or your published custom domain). Native iOS/Android builds are not affected by this.
Solution: On ORDS, set the `security.cors.allowedOrigins` parameter. In a self-managed ORDS install, edit the ords configuration file and add: `security.cors.allowed_origins=https://app.flutterflow.io,https://your-published-domain.com`. For Oracle Autonomous Database, go to Database Actions → REST → Settings and enable CORS with the allowed origins. Redeploy ORDS and test again in the web preview.
Autonomous Database connection works in SQL Developer but ORDS returns 503 or connection errors
Cause: The Oracle Autonomous Database wallet (mTLS) is not correctly configured on the ORDS host. The wallet ZIP (cwallet.sso, tnsnames.ora) must be extracted to the ORDS server and the JDBC URL must reference the correct TNS alias and wallet path.
Solution: Download the wallet ZIP from Oracle Cloud Console → Autonomous Database → DB Connection → Download Wallet. Extract to a directory on the ORDS server (e.g. /opt/oracle/wallet). Update the ORDS configuration JDBC URL to: `jdbc:oracle:thin:@your_adb_service?TNS_ADMIN=/opt/oracle/wallet`. Verify the directory contains cwallet.sso, tnsnames.ora, and sqlnet.ora. Restart the ORDS service. The wallet must never be copied to a FlutterFlow project or client device.
Best practices
- Never put Oracle DB credentials, ORDS client secrets, or wallet files in FlutterFlow — all Oracle authentication lives on the ORDS server or a Cloud Function proxy, never in compiled Dart code
- Use Oracle REST Data Services (ORDS) instead of building a custom REST wrapper — ORDS auto-generates secure, paginated, filterable endpoints for every table and PL/SQL procedure at no cost
- Map ORDS JSON paths starting with $.items[*] for list endpoints — all ORDS list results are wrapped in this envelope; missing this causes blank lists in FlutterFlow
- Enable ORDS CORS headers for your FlutterFlow web domain before publishing — native iOS/Android builds work without CORS, but web builds require it
- Use OAuth2 client credentials (not Basic Auth) for write operations in production — store the client secret in a Firebase Cloud Function or backend, never in FlutterFlow app state
- Always test with Oracle Autonomous Database's Always Free tier first — it comes with ORDS pre-installed and a wallet-secured connection, giving you a production-like setup without infrastructure costs
- Implement pagination with ORDS offset/limit parameters — ORDS returns 25 rows by default; for tables with thousands of rows, use offset-based paging driven by a FlutterFlow page state variable
- Use PL/SQL ORDS handlers for complex business logic (multi-table transactions, calculations, validations) — this keeps business rules in Oracle where they belong, called cleanly from FlutterFlow API Actions
Alternatives
PostgreSQL with Supabase gives FlutterFlow a native integration panel — no ORDS setup needed; choose PostgreSQL/Supabase if you're starting fresh rather than migrating an existing Oracle schema.
SQL Server requires a hand-written REST wrapper (no ORDS equivalent), but if your stack is already on Azure and Microsoft technologies, the Azure Functions + mssql approach is the natural path.
MySQL is the LAMP-stack default and works via a simple PHP or Node REST layer; choose MySQL over Oracle if your team is less familiar with enterprise Oracle licensing and PL/SQL.
Frequently asked questions
Can FlutterFlow connect to Oracle Database without ORDS?
Not directly — a Flutter app cannot open a raw TCP connection on Oracle's SQL*Net port (1521). You must front Oracle with a REST layer. ORDS is the recommended option because it's free and auto-generates endpoints, but you can also write a custom Node.js, Java, or Python REST API that connects to Oracle via JDBC/ODBC and exposes JSON endpoints for FlutterFlow to call.
Does Oracle Autonomous Database work with FlutterFlow?
Yes. Oracle Autonomous Database has an Always Free tier with ORDS already bundled. The wallet-based mTLS connection (required for Autonomous DB) is configured entirely on the ORDS host — FlutterFlow never handles the wallet files. Once ORDS is running, FlutterFlow calls it exactly like any other REST API via API Calls.
Why do my FlutterFlow JSON paths return null even though ORDS is working?
ORDS wraps all list results in an `items` array, so your JSON path must start with `$.items[*].COLUMN_NAME` — not `$.COLUMN_NAME`. Additionally, Oracle column names are returned in uppercase by default (unless aliased in your SQL). Open the API Call's Response & Test tab, inspect the raw JSON, and confirm both the envelope structure and the exact case of each key name before mapping paths.
How do I handle Oracle PL/SQL stored procedures in FlutterFlow?
Use ORDS to expose PL/SQL procedures as REST endpoints — you define a handler with ORDS.DEFINE_HANDLER specifying the PL/SQL source and the HTTP method. Once defined, the procedure is callable from FlutterFlow as a regular POST API Call. This lets you keep complex business logic in Oracle (validation, calculations, multi-table transactions) and invoke it cleanly from your mobile app.
Is Oracle licensing expensive for a startup building a FlutterFlow app?
Oracle Autonomous Database has a genuinely free Always Free tier — two database instances with no time limit. ORDS is also free. The paid Oracle Database Enterprise Edition licensing only applies if you're running a self-managed Oracle installation on-premises or on a non-Oracle cloud VM. For most FlutterFlow founders, starting with Autonomous Database Always Free is the practical choice.
My FlutterFlow web build works but the mobile app gets connection errors to ORDS. What's wrong?
ORDS must be accessible over public HTTPS for both web and native mobile builds. If your ORDS is behind a firewall or on a private network (e.g. a corporate intranet), native mobile apps cannot reach it over the internet. Ensure ORDS is deployed on a publicly accessible server with a valid TLS certificate. For corporate Oracle installations behind a VPN, you'd need a VPN client app or a separate public-facing proxy — neither of which FlutterFlow natively supports.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation