Connect to Oracle Database with JavaScript
Introduction
Running a local Oracle database and querying it from Node.js is just as easy as doing the same with any other database technology.
In this post we’ll:
- Start Oracle AI Database 26ai locally with a single
docker-compose.yml, using the freegvenzl/oracle-freeimage - Auto-seed it on startup with a sample
countries/cities/currenciesdataset via an init script - Poke around the schema with SQLcl, using its built-in aliases instead of hand-written catalog queries
- Set up a small TypeScript project and install the official
oracledbnpm module - Write a
countries.tsscript that connects using credentials from a.envfile and streams theCOUNTRIEStable back with aResultSet
By the end you’ll have a disposable, fully scripted Oracle setup and a minimal TypeScript querying pattern you can reuse in real projects.
Setting Up an Oracle Database with Docker Compose
Docker Compose Configuration
First, let’s create a Oracle Database server to work with:
cat docker-compose.yml
services:
oracle:
image: gvenzl/oracle-free:slim-faststart
container_name: db
hostname: db
ports:
- "1521:1521"
environment:
ORACLE_PASSWORD: Welcome12345
APP_USER: dev
APP_USER_PASSWORD: dev
volumes:
- ./init_scripts:/container-entrypoint-initdb.d
We have mounted a init_scripts directory where it’s content will be run after database startup This is what will happen:
- Downloads the install.sql file using curl
- Set TWO_TASK=//localhost:1521/FREEPDB1 (1521 is default and can be left out). TWO_TASK is an old friend I always set so I easily can switch between accounts (user/pass) without specifying the host:[port]/SERVICE_NAME part.
- Logs in to the database with the dev account specified in the docker-compose.yml file
- Runs the install.sql script
- Cleans up (deletes install.sql)
tree init_scripts
init_scripts
└── 01_install.sh
cat init_scripts/01_install.sh
curl -LJO https://raw.githubusercontent.com/gvenzl/sample-data/master/countries-cities-currencies/install.sql
TWO_TASK=//localhost/FREEPDB1 sqlplus -s dev/dev @install.sql
rm install.sql
Start the database container in detached mode:
$ docker compose up -d
Verify:
docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
db gvenzl/oracle-free:slim-faststart "container-entrypoin…" oracle 11 minutes ago Up 11 minutes 0.0.0.0:1521->1521/tcp, [::]:1521->1521/tcp
Let’s make sure the tables have been created
- Set TWO_TASK
- Log in using the SQLcl client (
brew install sqlcl. Uses GraalVM’s JavaScript engine to power the fancy prompt). - List tables using a SQLcl alias. SQLcl has an alias for everything, so there’s no need to query the catalog by hand.
export TWO_TASK=localhost/FREEPDB1
sql dev/dev
SQLcl: Release 26.2 Production on Tue Jul 21 08:56:11 2026
Copyright (c) 1982, 2026, Oracle. All rights reserved.
Connected to:
Oracle AI Database 26ai Free Release 23.26.2.0.0 - Develop, Learn, and Run for Free
Version 23.26.2.0.0
DEV@FREEPDB1🙊😎 > alias groups
apex default my nucleo ords psql report system
DEV@FREEPDB1🙊😎 > alias default
functions indexes locks mview_logs mviews oraversion packages procedures queues schedules sequences
sessions synonyms tables triggers types u_jobs u_mle_modules views
DEV@FREEPDB1🙊😎 > tables
TABLES
--------------------------------------------------------------------------------------------------------------------------------
CITIES
COUNTRIES
CURRENCIES
CURRENCIES_COUNTRIES
REGIONS
Querying the Oracle Database from JavaScript
Create a typescript project
# one-liner to configure a modern typescript project
npm init -y && jq '. + {type: "module"}' package.json > tmp.json && mv -fv tmp.json package.json && npm install -D typescript @types/node && npx tsc --init
# we will load the environment variables from a .env file
npm i dotenv
Install the Oracle Database JavaScript module
npm search oracledb
oracledb
A Node.js module for Oracle Database access from JavaScript and TypeScript
Version 7.0.1 published 2026-07-15 by sharadchan87
Maintainers: krismohan anthony.tuininga sharadchan87
Keywords: Oracle Database official DB SQL JSON PL/SQL SODA OCI API client library driver add-on extension binding interface adapter module
https://npm.im/oracledb
npm i oracledb
Configure the environment
cat .env
DB_USERNAME=dev
DB_PASSWORD=dev
DB_CONNECTION_STRING=localhost/FREEPDB1
Query the COUNTRIES table
node countries.ts
◇ injected env (3) from .env // tip: ◈ secrets for agents [www.dotenvx.com]
Successfully connected to Oracle Database
AD Andorra Principality of Andorra 86000
AE United Arab Emirates United Arab Emirates 9701000
AF Afghanistan Islamic Republic of Afghanistan 34941000
AG Antigua and Barbuda Antigua and Barbuda 96000
AL Albania Republic of Albania 3057000
AM Armenia Republic of Armenia 3038000
AO Angola Republic of Angola 30356000
etc ...
The countries.ts script
import oracledb, { Connection } from 'oracledb';
import dotenv from 'dotenv';
dotenv.config();
interface CountryRow {
COUNTRY_CODE: string;
NAME: string;
OFFICIAL_NAME: string;
POPULATION: number;
}
async function runApp(): Promise<void> {
let connection: Connection | undefined;
try {
connection = await oracledb.getConnection({
user: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
connectionString: process.env.DB_CONNECTION_STRING
});
console.log("Successfully connected to Oracle Database");
const queryResult = await connection.execute<CountryRow>(
`select country_code, name, official_name, population from countries`,
[],
{ resultSet: true, outFormat: oracledb.OUT_FORMAT_OBJECT }
);
const rs = queryResult.resultSet!;
let row: CountryRow | undefined;
while ((row = await rs.getRow())) {
if (row.OFFICIAL_NAME === null) {
console.log(row.COUNTRY_CODE, row.NAME, row.NAME, row.POPULATION);
}
else {
console.log(row.COUNTRY_CODE, row.NAME, row.OFFICIAL_NAME, row.POPULATION);
}
}
await rs.close();
} catch (err) {
console.error(err);
} finally {
if (connection) {
try {
await connection.close();
} catch (err) {
console.error(err);
}
}
}
}
runApp();
Conclusion
With a single Docker Compose file and the official oracledb npm module, you now have a fully disposable Oracle AI Database 26ai environment and a working TypeScript script that connects to it, runs a query, and streams the results back row by row using a ResultSet.
From here, the same pattern — getConnection, execute with resultSet: true, and OUT_FORMAT_OBJECT — scales to bind parameters, transactions, and PL/SQL calls, so you can build on this foundation for real application code.