Bundling node-oracledb JavaScript apps with Webpack

Christopher Jones
Oracle Developers
Published in
7 min readMay 24, 2023

The Webpack bundler lets you easily package Node.js JavaScript applications so you can quickly deploy them to be run wherever Node.js is installed. It combines and minifies JavaScript files, and enables you to package dependencies.

Photo by Mohan Murugesan on Unsplash

When your apps use the node-oracledb 6.0 driver’s new, pure JavaScript ‘Thin’ mode that talks directly to Oracle Database, you can efficiently and portably distribute your database applications without needing your end users to install platform-specific binary modules or Oracle Client libraries, and without your users having environment configuration complexities.

Using Webpack with node-oracledb Thin Mode

‘Thin’ mode is the default runtime behavior of node-oracledb 6.0. Let’s look at an example Node.js app and how to bundle it.

Create a Node.js project:

$ npm init -y
$ npm install oracledb --save

Create your application, index.js. This simple demo creates a table, inserts some rows, and queries them back from Oracle Database:

// index.js

const oracledb = require('oracledb');

if (process.argv.length != 5) {
console.error('Usage: node ' + process.argv[1] + ' username password connect_string');
process.exit(1);
}

const dbConfig = { user: process.argv[2], password: process.argv[3], connectString: process.argv[4] };

async function run() {

const connection = await oracledb.getConnection(dbConfig);

const stmts = [
`BEGIN
EXECUTE IMMEDIATE 'DROP TABLE no_example PURGE';
EXCEPTION WHEN OTHERS THEN IF SQLCODE <> -942 THEN RAISE; END IF;
END;`,
`CREATE TABLE no_example (id NUMBER, data VARCHAR2(20))`
];

for (const s of stmts) {
await connection.execute(s);
}

let result = await connection.executeMany(
`INSERT INTO no_example VALUES (:1, :2)`,
[
[101, "Alpha" ],
[102, "Beta" ],
[103, "Gamma" ]
],
{
bindDefs: [
{ type: oracledb.NUMBER },
{ type: oracledb.STRING, maxSize: 20 }
]
});

console.log(`Inserted ${result.rowsAffected} rows`);

result = await connection.execute(
`SELECT * FROM no_example`,
[],
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
);

console.log("Query results: ");
console.dir(result.rows, { depth: null });

await connection.close();
}

run();

You can run it by passing your database credentials:

$ node index.js cj mypw localhost/orclpdb1
Inserted 3 rows
Query results:
[
{ ID: 101, DATA: 'Alpha' },
{ ID: 102, DATA: 'Beta' },
{ ID: 103, DATA: 'Gamma' }
]

No other install, e.g. of Oracle Instant Client, or external configuration was needed to run this. The only thing required was to have a database running and know the connection credentials.

To package up this application, install the Webpack dependencies:

$ npm install webpack webpack-cli clean-webpack-plugin --save-dev

Edit package.json and change the “scripts” start target to run Webpack:

"scripts": {
"start": "webpack --config webpack.config.js"
},

Your package.json will be similar to:

{
"name": "webpack-example-thin",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"oracledb": "^6.0.0"
},
"devDependencies": {
"clean-webpack-plugin": "^4.0.0",
"webpack": "^5.81.0",
"webpack-cli": "^5.0.2"
}
}

In the same directory as index.js and package.json, create a file webpack.config.js:

const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

const config = {
entry: './index.js',
target: 'node',
output: {
path: path.join(__dirname, 'dist'),
filename: 'myapp.js',
},
mode: 'production',
watch: false,
plugins: [
new CleanWebpackPlugin(),
],
};

module.exports = config;

I use the clean-webpack-plugin to make re-running the build easier.

Run the Webpack build:

$ npm start

The output will be like:

> webpack-example-thin@1.0.0 start
> webpack --config webpack.config.js

asset myapp.js 313 KiB [emitted] [minimized] (name: main)
runtime modules 88 bytes 1 module
modules by path ./node_modules/oracledb/lib/ 847 KiB
modules by path ./node_modules/oracledb/lib/thin/ 475 KiB 40 modules
modules by path ./node_modules/oracledb/lib/*.js 290 KiB 24 modules
modules by path ./node_modules/oracledb/lib/impl/*.js 80.6 KiB
./node_modules/oracledb/lib/impl/index.js 2.09 KiB [built] [code generated]
+ 14 modules
./node_modules/oracledb/lib/ sync 160 bytes [built] [code generated]
+ 18 modules

WARNING in ./node_modules/oracledb/lib/oracledb.js 75:100-107
Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
@ ./node_modules/oracledb/index.js 27:0-45
@ ./index.js 1:17-36

1 warning has detailed information that is not shown.
Use 'stats.errorDetails: true' resp. '--stats-error-details' to show it.

webpack 5.83.1 compiled with 1 warning in 1248 ms

The ‘Critical dependency’ warning can be ignored. It occurs because Webpack is unable to analyze the Thick mode binary that node-oracledb optionally loads with require() so Webpack can’t see if there are any further dependencies. Since the demo isn’t using Thick mode, this warning is not an issue.

The output bundle is in dist/myapp.js. This single JavaScript file contains both the node-oracledb driver and the sample application — and it’s only 316 KB!:

$ ls -lR dist
-rw-r--r-- 1 cjones wheel 320686 24 May 16:41 myapp.js

To show it contains the application and driver code, uninstall node-oracledb from the node_modules directory:

$ npm uninstall oracledb

Running the original application will fail:

$ node index.js cj mypw localhost/orclpdb1
node:internal/modules/cjs/loader:1085
throw err;
^
Error: Cannot find module 'oracledb'
. . .

But you can run the newly packaged application without a problem:

$ node dist/myapp.js cj mypw localhost/orclpdb1
Inserted 3 rows
Query results:
[
{ ID: 101, DATA: 'Alpha' },
{ ID: 102, DATA: 'Beta' },
{ ID: 103, DATA: 'Gamma' }
]

Distributing and running packaged node-oracledb applications is simple with the new, default, pure JavaScript ‘Thin’ mode of node-oracledb 6. The bundle is small and self contained. It is usable wherever Node.js is installed.

Using Webpack with node-oracledb Thick Mode

If you want to use any ‘Thick’ mode features in your application you will need to distribute node-oracledb’s Thick mode binaries along with the JavaScript bundled code. You can do this with a Webpack copy plugin. You will also need to make sure Oracle Client libraries are available wherever you deploy the application.

First start with the application code shown earlier and modify it use Thick mode. Do this by editing index.js and adding a call to initOracleClient() after the driver require() call:

const oracledb = require('oracledb');

let clientOpts = {};
if (process.platform === 'win32') { // Windows
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
}
oracledb.initOracleClient(clientOpts); // Enable node-oracledb Thick mode
console.log('Using Thick mode');

. . .

Adjust the client library paths as needed. Review the node-oracledb initialization documentation for details and alternatives.

Make sure node-oracledb and the Webpack tools are installed:

$ npm install oracledb --save
$ npm install webpack webpack-cli clean-webpack-plugin --save-dev

The package.json “scripts” should be:

"scripts": {
"start": "webpack --config webpack.config.js"
},

To make the node-oracledb binary be copied to the distribution bundle, install the Webpack copy plugin:

$ npm install copy-webpack-plugin --save-dev

Update (or create, if you didn’t do the previous example) webpack.config.js to use the copy plugin. The file should look like:

const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

const config = {
entry: './index.js',
target: 'node',
output: {
path: path.join(__dirname, 'dist'),
filename: 'myapp.js',
},
mode: 'production',
watch: false,
plugins: [
new CleanWebpackPlugin(),
new CopyPlugin({
patterns: [{
// Copy Thick-mode Oracle Database driver binaries to dist
from: path.resolve(__dirname, 'node_modules/oracledb/build'),
to: 'node_modules/oracledb/build',
}],
})
],
};

module.exports = config;

Run the Webpack build:

$ npm start

You will see the same ‘Critical dependency’ warning that you got with Thin mode. This can be ignored because the only dependencies are the node-oracledb Thick mode binary modules - which will be handled by the copy plugin.

In the dist directory you will now see the file myapp.js that bundles your application together the node-oracledb JavaScript code, and you will also see the node-oracledb ‘Thick’ mode binaries:

$ ls -lR dist
-rw-r--r-- 1 cjones wheel 320934 24 May 16:47 myapp.js
drwxr-xr-x 3 cjones wheel 96 24 May 16:47 node_modules

dist/node_modules:
drwxr-xr-x 3 cjones wheel 96 24 May 16:47 oracledb

dist/node_modules/oracledb:
drwxr-xr-x 3 cjones wheel 96 24 May 16:47 build

dist/node_modules/oracledb/build:
drwxr-xr-x 11 cjones wheel 352 24 May 16:47 Release

dist/node_modules/oracledb/build/Release:
-rw-r--r-- 1 cjones wheel 519400 24 May 16:47 oracledb-6.0.0-darwin-x64.node
-rw-r--r-- 1 cjones wheel 152 24 May 16:47 oracledb-6.0.0-darwin-x64.node-buildinfo.txt
-rw-r--r-- 1 cjones wheel 41 24 May 16:47 oracledb-6.0.0-js-buildinfo.txt
-rw-r--r-- 1 cjones wheel 612024 24 May 16:47 oracledb-6.0.0-linux-arm64.node
-rw-r--r-- 1 cjones wheel 153 24 May 16:47 oracledb-6.0.0-linux-arm64.node-buildinfo.txt
-rw-r--r-- 1 cjones wheel 589952 24 May 16:47 oracledb-6.0.0-linux-x64.node
-rw-r--r-- 1 cjones wheel 151 24 May 16:47 oracledb-6.0.0-linux-x64.node-buildinfo.txt
-rw-r--r-- 1 cjones wheel 590848 24 May 16:47 oracledb-6.0.0-win32-x64.node
-rw-r--r-- 1 cjones wheel 151 24 May 16:47 oracledb-6.0.0-win32-x64.node-buildinfo.txt

You will need to distribute all these file to your end users, optionally omitting the *.node binaries for platforms that you aren’t interested in supporting. The “buildinfo” files contain a build tag that is a useful cross-check for us as maintainers if you report driver problems, but the files can also be omitted if you like. You will separately have to ensure anyone using your application has Oracle Client libraries installed, and they are in a location the application’s initOracleClient() call can find. There are various platform-specific ways of doing this: see the node-oracledb documentation on initialization.

Summary

Webpack is a convenient way to package Node.js applications for deployment. With node-oracledb 6.0 Thin mode your packaged Oracle Database applications are small and easily deployed: without a dependency on Oracle Client libraries and without the resulting library search path complexities. In only a few hundred kilobytes you can have an efficient, fast, and functional application that can connect to Oracle Database from any supported Node.js installation.

Resources

Home page: https://oracle.github.io/node-oracledb/

Installation: https://node-oracledb.readthedocs.io/en/latest/user_guide/installation.html

Documentation: https://node-oracledb.readthedocs.io/en/latest/

Questions: https://github.com/oracle/node-oracledb/discussions/

Source code: https://github.com/oracle/node-oracledb

Npm repository: https://www.npmjs.com/package/oracledb

--

--

Christopher Jones
Oracle Developers

Oracle Database Product Manager for language drivers including Python python-oracledb, Node.js node-oracledb, PHP OCI8 and more! On Mastodon: @cjbj@phpc.social