This post demonstrates how to use node-express with Multilingual Engine(MLE)/JavaScript. It has been created as part of contribution to an open-source project and it might interesting in case you try to do something similar along these lines. However, there is a Big Caveat:
CAVEAT: the node-express combination is very well established in the JavaScript community: it’s simple, lightweight, and easy to use. However, you are probably better off using Oracle REST Data Services (ORDS) instead of express for more serious work if you don’t want to reinvent the wheel. ORDS provides a lot more than express out of the box, including, but not limited to, authorisation and authentication.
Database Setup
This little application requires an Oracle Database (Free) instance. This example features Gerald Venzl’s image, and a compose file to get you up to speed quickly. If you prefer your own setup, please make sure not to use the *-slim image flavours. These don’t include MLE, and that would defeat the purpose of this article.
Here’s an example compose file you can use as the starting point. The file caters for rootless podman/podman-compose; adapt as necessary for other container runtimes like Docker(-compose). You may also want to bump the version tag as newer releases become available.
# THIS IS NOT A PRODUCTION SETUP - LAB USE ONLY!
services:
oracle:
image: docker.io/gvenzl/oracle-free:23.6
ports:
- 1521:1521
environment:
- ORACLE_PASSWORD_FILE=/run/secrets/oracle-passwd
volumes:
- oradata-vol:/opt/oracle/oradata
networks:
- backend
healthcheck:
test: [ "CMD", "healthcheck.sh" ]
interval: 10s
timeout: 5s
retries: 10
start_period: 5s
secrets:
- oracle-passwd
volumes:
oradata-vol:
networks:
backend:
secrets:
oracle-passwd:
external: true
The above compose file worked brilliantly with podman-compose 1.0.6-1 and podman 4.9.3. It requires a Podman secret, named oracle-password to run. You can read more about Podman Secrets in the official documentation.
Application User
Once the database is up and running (visible in the output of podman logs <container>) you need to create an application user in FREEPDB1, the default (Pluggable) Database created by the container image. The easiest way to do that is via SQLcl. Either install it locally or use the official image. The latter is preferable if you don’t want to deal with SQLcl’s dependencies such as the Java Runtime. The container image has been used when writing the article, as shown:
-
Get required connection information
Before starting you need to know
- the name of the database container to connect to
- the network it used
Use the following snippet to get the container name
sh $ podman ps --format "{{.Image}} --> use {{.Names}}"
docker.io/gvenzl/oracle-free:23.6 --> use dbfree_oracle_1Similarly, use
podman network lsto get the network. Let’s assume the network name isdbfree_backend. -
Start SQLcl
Launch the SQLcl container instance. Don’t forget to update the network name!
sh cd express-mle-javascript/src/
podman run -it --rm \
--volume ./database/:/opt/oracle/sql_scripts:Z \
--network ${compose_network:-"dbfree_backend"} \
container-registry.oracle.com/database/sqlcl:latest /nologThis will launch SQLcl.
-
Connect to the SYSTEM account using the container name you worked out earlier
If you created the database using the compose file shown above, the following command will work without a change.
sql connect system@dbfree_oracle_1/freepdb1Enter the password to connected to the SYSTEM account, one of the default DBA accounts in the database.
-
Switch to the default Pluggable Database and create the user
Let’s create the user; please update the password when you do so 🙂
sql alter session set container = FREEPDB1;
create user app_user identified by "someSecretPasswordOfYourLiking"
default tablespace users quota 1g on users;
-- these are required for MLE/JavaScript
grant execute on javascript to app_user;
grant execute dynamic MLE to app_user;
grant db_developer_role to app_user;
grant soda_app to app_user;
alter user app_user default role all;
This concludes the database setup.
Code Deployment
Don’t close the container session, you need it next to deploy the application code. Connect as the newly created user and run the deploy.sql script:
connect app_user@dbfree_oracle_1/freepdb1
start deploy.sql
You should see the deployment on your screen:
SQL> start deploy
MLE Module demo_module created
Table DEMO_TABLE created.
Procedure PROCESS_DATA compiled
Function GET_DB_DETAILS compiled
Function GET_MESSAGE_BY_ID compiled
The stage is now set! You can find the deploy.sql script (all the code actually) on GitHub.
Start the Application
Open a new terminal and start the express application as follows:
cd ./express-mle-javascript
# install required packages
npm i
# start the application
npm run dev
You can now point cURL at port 3000 and run some tests.
Have fun
Feel free to experiment with the code. The entry point in src/server/app.mjs defines all the application’s verbs:
- get
- post
- delete
The database logic is written in MLE/JavaScript (mle-server-side-code.mjs) exposed to the database in src/database/deploy.sql. The latter defines the PL/SQL call specifications.
Test the application by querying session info:
curl --silent http://localhost:3000/api/info | jq
You can post a message to the database:
curl --silent --json '{ "message": "this message has been provided via curl" }' \
http://localhost:3000/api/messages/
Retrieve messages from the database:
curl --silent http://localhost:3000/api/messages | jq
You can also get specific messages by their ID. Here’s how to retrieve the last message inserted:
lastMessage=$(curl --silent http://localhost:3000/api/messages | jq 'map(.ID) | max')
curl --silent http://localhost:3000/api/messages/${lastMessage:-1} | jq
And finally, you can delete messages, too
lastMessage=$(curl --silent http://localhost:3000/api/messages | jq 'map(.ID) | max')
curl --request DELETE --silent http://localhost:3000/api/messages/${lastMessage:-1}
To test if these operations are available, you can run the unit test suite defined in Mocha:
$ npm run test
> test
> npx mocha ./test
All Unit Tests
Session Info tests
✔ should print session info (105ms)
Posting a message
✔ should post a message to the database successfully (39ms)
✔ should fail posting a message to the database
Retrieval of all messages
✔ should retrieve one or more messages from the database
Retrieval of a single message
✔ should retrieve the last message posted from the database
✔ should fail to retrieve a message due to an invalid parameter type
✔ should fail to retrieve a message due to a negative ID
Deleting messages
✔ should delete the last message inserted
✔ should fail to delete a message due to an invalid parameter type
✔ should fail to delete a message due to a negative ID
10 passing (304ms)
Unit tests are the only way to gain confidence that your code doesn’t break/experience regressions, provided you follow DevOps principles (“work in small batches”, and “integrate often” principle spring to mind).
