Connect V0-generated Next.js apps to Oracle Database by using the `oracledb` npm package inside a Next.js API route. Store your Oracle connection string as a server-only environment variable in Vercel Dashboard. V0 generates the UI, and a secure API route handles all database queries — your credentials never reach the browser.
Connecting V0 Apps to Oracle Database
Many enterprise organizations run Oracle Database as their primary data store — managing customer records, inventory, financial transactions, and operational data that has accumulated over decades. When you use V0 by Vercel to build internal tools and dashboards, you often need to read and write data from these existing Oracle systems rather than migrating everything to a new database.
The integration pattern is straightforward: V0 generates the React frontend components and UI layout, while a Next.js API route acts as a secure middleware layer that queries Oracle Database using the `node-oracledb` npm package. This server-side API route runs on Vercel as a serverless function, meaning it has access to server environment variables (your Oracle credentials) without ever exposing them to the browser. The React components V0 generates simply fetch from `/api/oracle/records` and display the results.
This architecture is the right approach for enterprise Oracle setups because it respects existing security boundaries. Your Oracle connection string — which contains the hostname, port, service name, username, and password — stays entirely on the server. You can also apply Oracle-specific optimizations like connection pooling in the API route layer, improving performance for high-traffic internal tools.
Integration method
V0 generates the React UI components and dashboard layouts. A Next.js API route (`app/api/oracle/route.ts`) handles Oracle Database queries server-side using the `node-oracledb` npm package. The Oracle connection string is stored as a server-only environment variable in Vercel, keeping credentials out of the browser entirely.
Prerequisites
- A V0 by Vercel account at v0.dev with GitHub connected
- A Vercel account for deployment (v0.dev account includes this)
- Access to an Oracle Database instance with a valid connection string (hostname, port, service name, username, password)
- Basic familiarity with SQL SELECT statements
- Node.js installed locally if you plan to test the API route before deploying
Step-by-step guide
Generate the Data Display UI in V0
Generate the Data Display UI in V0
Start in V0 by generating the frontend component that will display your Oracle data. You do not need to worry about the data source yet — describe the UI layout you want and V0 will generate a working component with mock data or placeholder props that you will later connect to your API route. In the V0 chat panel, write a prompt describing your data display needs. Be specific about what columns or fields you want to show. For example, if you are building an employee directory from Oracle's HR tables, describe the table columns, search functionality, and any status indicators you need. V0 will generate a React component using Tailwind CSS and shadcn/ui components. After V0 generates the component, check the preview to make sure the layout is what you want. At this stage the data is hardcoded or empty — that is expected. The key things to confirm in the preview are: the table structure looks right, the loading state is handled (ideally with a skeleton), and the overall visual design matches your needs. Once you are satisfied, use V0's Git panel to push the project to GitHub. This creates your Next.js project with the UI component in place.
Create an inventory management table with columns for product name, SKU, current stock, reorder level, and a status badge showing In Stock, Low Stock, or Out of Stock based on whether current stock is above reorder level. Include a loading skeleton and an empty state. Fetch data from /api/oracle/inventory on component mount.
Paste this in V0 chat
Pro tip: Ask V0 to include a loading skeleton and error state in the component — this makes the integration feel polished when real API latency is introduced.
Expected result: A Next.js project on GitHub with a React component that renders a data table with loading/error states, ready to be connected to a real API route.
Install node-oracledb and Create the API Route
Install node-oracledb and Create the API Route
Clone the GitHub repository to your local machine. In the project root, run `npm install oracledb` to add the Oracle Database driver. The `oracledb` package (also known as node-oracledb) is Oracle's official Node.js driver and supports all Oracle Database versions from 11.2 onwards. It provides both Thin mode (pure JavaScript, no additional Oracle software needed) and Thick mode (requires Oracle Client libraries for advanced features). For most Vercel deployments, Thin mode is sufficient and much easier to set up. Create the API route file at `app/api/oracle/route.ts`. This file will handle GET requests from your frontend component. The route reads the Oracle connection string from `process.env.ORACLE_CONNECTION_STRING` and other credentials from individual environment variables. It initializes a connection using `oracledb.getConnection()`, executes your SQL query, closes the connection, and returns the results as JSON. The Thin mode initialization is important for Vercel deployments — add `oracledb.initOracleClient()` only if you specifically need Thick mode (which requires bundling Oracle Client libraries, complicating deployment). For reading and writing standard Oracle data, Thin mode handles everything you need. A key consideration for serverless deployments is connection management. Vercel serverless functions are stateless — they spin up and down on demand. Unlike a persistent Node.js server, you cannot use a long-lived connection pool across requests. Instead, use `oracledb.getConnection()` to open a connection at the start of each request and `connection.close()` in a finally block to release it when done. This keeps each function invocation self-contained.
1// app/api/oracle/route.ts2import { NextRequest, NextResponse } from 'next/server';3import oracledb from 'oracledb';45// Use Thin mode (no Oracle Client libraries required)6oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;78export async function GET(request: NextRequest) {9 let connection;1011 try {12 // Connect using individual credentials13 connection = await oracledb.getConnection({14 user: process.env.ORACLE_USER,15 password: process.env.ORACLE_PASSWORD,16 connectString: process.env.ORACLE_CONNECTION_STRING,17 // e.g. "myhost:1521/myservice" or an EZConnect string18 });1920 const result = await connection.execute(21 `SELECT product_name, sku, current_stock, reorder_level22 FROM inventory23 ORDER BY product_name24 FETCH FIRST 100 ROWS ONLY`,25 [],26 { outFormat: oracledb.OUT_FORMAT_OBJECT }27 );2829 const rows = result.rows as Array<{30 PRODUCT_NAME: string;31 SKU: string;32 CURRENT_STOCK: number;33 REORDER_LEVEL: number;34 }>;3536 return NextResponse.json({ data: rows });37 } catch (error) {38 console.error('Oracle query error:', error);39 return NextResponse.json(40 { error: 'Failed to query database' },41 { status: 500 }42 );43 } finally {44 if (connection) {45 try {46 await connection.close();47 } catch (closeError) {48 console.error('Error closing Oracle connection:', closeError);49 }50 }51 }52}Pro tip: Oracle column names are returned in UPPERCASE by default (e.g., PRODUCT_NAME). Account for this in your TypeScript types and when mapping results to camelCase for your React component.
Expected result: An API route file at app/api/oracle/route.ts that connects to Oracle Database, runs a query, and returns results as JSON. The route compiles without TypeScript errors.
Add Oracle Credentials as Vercel Environment Variables
Add Oracle Credentials as Vercel Environment Variables
Your Oracle connection credentials must never be committed to your GitHub repository. Instead, they are stored as server-only environment variables in Vercel Dashboard and injected into your API route at runtime. Go to your Vercel Dashboard at vercel.com. Select your project and navigate to Settings → Environment Variables. You need to add three variables: **ORACLE_USER** — your Oracle database username (e.g., `hr` or `app_user`). Set the environment to 'Production', 'Preview', and 'Development' as needed. **ORACLE_PASSWORD** — your Oracle user's password. This is the most sensitive credential — make sure you only add it to Production or the specific environments that need it. **ORACLE_CONNECTION_STRING** — the Oracle EZConnect connection string in the format `hostname:port/service_name`. For example: `db.example.com:1521/ORCL`. If you are using Oracle Autonomous Database (OAD), this will be a full connection descriptor string. After adding all three variables, click Save. These variables are NOT prefixed with `NEXT_PUBLIC_` which means they are server-only and will never be sent to the browser — this is the correct security posture for database credentials. If you are testing locally, create a `.env.local` file in the project root (make sure `.env.local` is listed in your `.gitignore`) and add the same variables there. Next.js automatically loads `.env.local` for local development, so your API route will work the same way locally as it does on Vercel. For complex integrations involving Oracle Autonomous Database with wallet-based authentication, RapidDev's team can help configure the wallet files and connection parameters for Vercel serverless deployments.
1# .env.local (never commit this file)2ORACLE_USER=your_oracle_username3ORACLE_PASSWORD=your_secure_password4ORACLE_CONNECTION_STRING=your-oracle-host:1521/your_service_namePro tip: Oracle Autonomous Database users: the wallet ZIP file approach does not work easily with Vercel serverless functions. Use the TLS connection string with mTLS instead of wallet-based auth for serverless environments.
Expected result: Three environment variables are saved in Vercel Dashboard under Settings → Environment Variables. Local .env.local file is configured for testing and is excluded from git.
Connect the V0 UI Component to the API Route
Connect the V0 UI Component to the API Route
Now update the React component V0 generated so it fetches real data from your Oracle API route instead of using mock data. Open the component file in your code editor (or use V0's Dev Mode if you are on a paid plan). The component needs to call `/api/oracle/inventory` (or whatever path you created) when it mounts, handle the loading and error states, and map the Oracle query results to the table rows. The fetch call should live in a `useEffect` hook (for client components) or use React Server Components for server-side rendering. For a data table that needs to load on page load, a Server Component approach is cleaner — it fetches data during rendering without any client-side useEffect. However, if you need real-time updates or user-triggered refreshes, a client component with useEffect is more appropriate. For the Oracle data mapping, remember that Oracle returns column names in UPPERCASE (PRODUCT_NAME, CURRENT_STOCK, etc.) unless you use column aliases with double quotes in your SQL. Map these to camelCase properties that match what V0's component expects, either in the API route (recommended) or in the component itself. After updating the component, commit and push to GitHub. Vercel will automatically redeploy. Once deployed, visit your Vercel URL and confirm the table loads Oracle data. Open the browser's Developer Tools → Network tab and check that the request to `/api/oracle/inventory` returns a 200 status with your data.
Update the inventory table component to fetch data from /api/oracle/inventory using useEffect on mount. Map the response data.data array to table rows. Show a loading skeleton while fetching and an error message if the fetch fails. Each row should show product_name, sku, current_stock, and reorder_level, with a status badge that shows 'In Stock' (green) if current_stock > reorder_level, 'Low Stock' (yellow) if current_stock <= reorder_level and > 0, or 'Out of Stock' (red) if current_stock is 0.
Paste this in V0 chat
1// app/components/inventory-table.tsx2'use client';34import { useEffect, useState } from 'react';56interface InventoryItem {7 product_name: string;8 sku: string;9 current_stock: number;10 reorder_level: number;11}1213export function InventoryTable() {14 const [data, setData] = useState<InventoryItem[]>([]);15 const [loading, setLoading] = useState(true);16 const [error, setError] = useState<string | null>(null);1718 useEffect(() => {19 fetch('/api/oracle/inventory')20 .then((res) => {21 if (!res.ok) throw new Error('Failed to fetch');22 return res.json();23 })24 .then((json) => {25 // Map Oracle UPPERCASE columns to camelCase26 const mapped = json.data.map((row: Record<string, unknown>) => ({27 product_name: row.PRODUCT_NAME,28 sku: row.SKU,29 current_stock: row.CURRENT_STOCK,30 reorder_level: row.REORDER_LEVEL,31 }));32 setData(mapped);33 })34 .catch((err) => setError(err.message))35 .finally(() => setLoading(false));36 }, []);3738 const getStatus = (item: InventoryItem) => {39 if (item.current_stock === 0) return { label: 'Out of Stock', color: 'bg-red-100 text-red-700' };40 if (item.current_stock <= item.reorder_level) return { label: 'Low Stock', color: 'bg-yellow-100 text-yellow-700' };41 return { label: 'In Stock', color: 'bg-green-100 text-green-700' };42 };4344 if (loading) return <div className="animate-pulse h-64 bg-gray-100 rounded-lg" />;45 if (error) return <div className="text-red-600 p-4">Error: {error}</div>;4647 return (48 <table className="w-full text-sm border-collapse">49 <thead>50 <tr className="border-b">51 <th className="text-left py-3 px-4">Product</th>52 <th className="text-left py-3 px-4">SKU</th>53 <th className="text-right py-3 px-4">Stock</th>54 <th className="text-right py-3 px-4">Reorder At</th>55 <th className="text-left py-3 px-4">Status</th>56 </tr>57 </thead>58 <tbody>59 {data.map((item) => {60 const status = getStatus(item);61 return (62 <tr key={item.sku} className="border-b hover:bg-gray-50">63 <td className="py-3 px-4">{item.product_name}</td>64 <td className="py-3 px-4 font-mono">{item.sku}</td>65 <td className="py-3 px-4 text-right">{item.current_stock}</td>66 <td className="py-3 px-4 text-right">{item.reorder_level}</td>67 <td className="py-3 px-4">68 <span className={`px-2 py-1 rounded-full text-xs font-medium ${status.color}`}>69 {status.label}70 </span>71 </td>72 </tr>73 );74 })}75 </tbody>76 </table>77 );78}Pro tip: Use column aliases in your Oracle SQL to return camelCase names directly: `SELECT product_name AS "productName"` — this simplifies the mapping in your React component.
Expected result: The inventory table loads real data from your Oracle Database. Rows display with correct status badges and the loading skeleton shows briefly while the API route executes the query.
Deploy to Vercel and Verify the Integration
Deploy to Vercel and Verify the Integration
With your code updated and environment variables configured in Vercel Dashboard, push your latest changes to GitHub. Vercel automatically detects the push and starts a new deployment — you can watch the build progress in Vercel Dashboard under the Deployments tab. Once the deployment is live (green checkmark in Vercel Dashboard), visit your Vercel URL and test the Oracle integration. Open your browser's Developer Tools (F12) and go to the Network tab. Reload the page and watch for the request to `/api/oracle/inventory` (or your route name). Click on that request to inspect the response — you should see the JSON payload with your Oracle data. If the request returns a 500 error, click on the failed deployment in Vercel Dashboard → View Function Logs to see the actual Oracle error message. Common issues at this stage include network connectivity (Vercel needs to reach your Oracle host — check firewall rules), incorrect connection string format, or authentication failures. For production Oracle databases, make sure your Oracle DBA has allowed inbound connections from Vercel's IP ranges. Vercel's serverless function IP addresses are not fixed (they can come from any AWS region), so the common enterprise solution is to either use Oracle's private endpoint with a VPN, or allowlist the broader AWS IP ranges for your Oracle host's region. Alternatively, consider Oracle Autonomous Database which supports public TLS endpoints without IP allowlisting.
Pro tip: In Vercel Dashboard, use the 'Redeploy' button (three dots on a deployment → Redeploy) after adding new environment variables — Vercel only injects env vars at build/deploy time, not on existing deployments.
Expected result: Your Vercel deployment is live, the inventory table displays Oracle data, and the /api/oracle/route returns 200 with correct JSON. Vercel function logs show successful Oracle connection and query execution.
Common use cases
Employee Directory from Oracle HR Schema
An HR team uses Oracle's built-in HR schema to store employee records. V0 generates a searchable employee directory UI with a data table, search input, and profile cards. The Next.js API route queries the EMPLOYEES and DEPARTMENTS tables and returns the data as JSON.
Create an employee directory page with a search input at the top and a table below showing employee name, department, job title, and hire date. The table should fetch data from /api/oracle/employees and show a loading skeleton while fetching.
Copy this prompt to try it in V0
Sales Dashboard from Oracle ERP Data
A sales team needs a real-time view of orders and revenue pulled from Oracle ERP tables. V0 generates a dashboard with KPI cards showing total revenue, order count, and average order value, plus a line chart of weekly sales trends.
Build a sales dashboard with four KPI metric cards at the top showing total revenue, order count, average order value, and top product. Below that, show a line chart of daily sales for the last 30 days. Fetch all data from /api/oracle/sales.
Copy this prompt to try it in V0
Inventory Management Interface
A warehouse team needs to view and update inventory levels stored in Oracle. V0 generates a table with inline editing where staff can update stock quantities, and the API route handles both SELECT queries and UPDATE statements.
Create an inventory management table with columns for product name, SKU, current stock, reorder level, and a status badge showing In Stock, Low Stock, or Out of Stock. Include an edit button per row that opens an inline form to update stock quantity. Fetch from /api/oracle/inventory.
Copy this prompt to try it in V0
Troubleshooting
API route returns 500 error with 'NJS-516: Oracle thin client does not support connections to older versions' or similar oracledb initialization error
Cause: The node-oracledb Thin mode has minimum Oracle Database version requirements. Thin mode requires Oracle Database 12.1 or later. Older Oracle installations require Thick mode with Oracle Client libraries installed.
Solution: Check your Oracle Database version. If it is below 12.1, you need to use Thick mode, which requires bundling Oracle Instant Client with your Vercel deployment. For most cases, upgrading to a supported Oracle version or using Thick mode via a Lambda layer is the solution. Contact your Oracle DBA to confirm the database version.
Environment variables are undefined in the API route even though they are set in Vercel Dashboard
Cause: Vercel environment variables are injected at deployment time. If you added the variables after the last deployment, the running deployment does not have them.
Solution: Go to Vercel Dashboard → Deployments → select the latest deployment → click the three dots menu → Redeploy. This triggers a new deployment that picks up the newly added environment variables. Do not use 'Promote to Production' of an old build — always redeploy from scratch.
Connection times out with 'NJS-500: connection timed out' or the API route hangs with no response
Cause: Vercel serverless functions cannot reach your Oracle Database host. This is usually a firewall or network connectivity issue — the Oracle Database host is not accessible from Vercel's AWS infrastructure.
Solution: Verify your Oracle host's firewall allows inbound TCP connections on port 1521 (or your configured port) from Vercel's IP ranges. For enterprise Oracle setups behind a corporate firewall, you may need to use Oracle Database's public endpoint feature or set up a VPN tunnel. Test connectivity by temporarily enabling access from 0.0.0.0/0 to confirm the issue is network-related.
Oracle query returns UPPERCASE column names like PRODUCT_NAME instead of expected camelCase productName
Cause: Oracle Database returns column names in UPPERCASE by default when using node-oracledb. This is standard Oracle behavior and not a bug.
Solution: Either map the columns in your API route before returning JSON, or use double-quoted lowercase aliases in your SQL query. The SQL alias approach is cleaner as it keeps the transformation at the database layer.
1// Option 1: SQL aliases (preferred)2const result = await connection.execute(3 `SELECT product_name AS "productName", sku, current_stock AS "currentStock"4 FROM inventory`5);67// Option 2: Map in API route8const mapped = result.rows.map((row: any) => ({9 productName: row.PRODUCT_NAME,10 sku: row.SKU,11 currentStock: row.CURRENT_STOCK,12}));Best practices
- Always close Oracle connections in a `finally` block — unclosed connections in serverless environments accumulate and can exhaust the database's maximum connection limit.
- Use parameterized queries with bind variables (`WHERE id = :id`) instead of string interpolation to prevent SQL injection attacks.
- Limit query results with `FETCH FIRST N ROWS ONLY` (Oracle 12c+) or `ROWNUM <= N` (older Oracle) to prevent accidentally fetching millions of rows in a single API call.
- Store the Oracle connection string, username, and password as separate Vercel environment variables — never concatenate them into a single variable, as this makes rotation harder.
- Add input validation in your API route before using query parameters in SQL — validate types, lengths, and allowed values for any user-supplied filter parameters.
- Log Oracle errors with sufficient context (query name, timestamp) in Vercel function logs to make debugging production issues easier without exposing sensitive data.
- For high-traffic applications, consider Oracle DRCP (Database Resident Connection Pooling) which is designed for stateless serverless architectures accessing Oracle Database.
Alternatives
Neon Postgres via Vercel Marketplace offers one-click setup with auto-provisioned credentials and is significantly easier to integrate with Next.js than Oracle Database.
MongoDB Atlas has a native Vercel integration and a JavaScript-native query API that requires less boilerplate than SQL-based Oracle connections.
Firebase Firestore is fully managed by Google and works without any connection string configuration, making it much simpler for teams without a dedicated DBA.
Frequently asked questions
Does node-oracledb work on Vercel serverless functions?
Yes, node-oracledb in Thin mode works on Vercel serverless functions without requiring any Oracle Client libraries to be installed. Thin mode is pure JavaScript and is included in your npm dependencies. The key consideration is that Vercel functions are stateless, so you must open and close connections per request rather than using a persistent connection pool.
Can I use Oracle Autonomous Database with V0 and Vercel?
Yes, Oracle Autonomous Database (OAD) supports TLS connections that work well with Vercel serverless functions. Use the TLS connection string from the Oracle Cloud Console instead of wallet-based authentication, as wallet files are difficult to bundle with Vercel deployments. Set the connection string in your Vercel environment variables.
How do I handle Oracle transactions (INSERT, UPDATE, DELETE) from Next.js API routes?
For write operations, use `connection.execute()` with your DML statement, then call `connection.commit()` before closing the connection. If an error occurs, call `connection.rollback()` in the catch block. Always close the connection in the finally block regardless of success or failure. Create separate API route handlers for each write operation (POST, PUT, DELETE request methods).
What is the maximum Vercel function timeout and will it affect Oracle queries?
Vercel Hobby plan functions have a 10-second timeout. Pro plan functions can be configured up to 300 seconds. Most Oracle queries return well within 10 seconds, but complex analytical queries on large tables may exceed this. Optimize slow queries with proper indexes, use FETCH FIRST to limit rows, and consider moving heavy queries to Oracle Materialized Views that precompute the results.
Do I need an Oracle DBA to set up this integration?
You need DBA-level access for two things: (1) creating the application database user with appropriate SELECT/INSERT/UPDATE privileges on the required tables, and (2) configuring network access to allow Vercel's IP ranges to reach the Oracle host. Once those are set up, the Next.js integration code can be managed by a developer without ongoing DBA involvement.
Can V0 generate SQL queries for my Oracle schema?
V0 can generate SQL query strings to put inside your API route, but it does not have knowledge of your specific Oracle schema. You need to tell V0 the table names and column names you want to query. Describe your data model in the V0 prompt: 'Create an API route that queries an Oracle table called EMPLOYEES with columns EMPLOYEE_ID, FIRST_NAME, LAST_NAME, DEPARTMENT_ID, and SALARY.'
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation