Deploying a Single Page Application using the Domino REST API
The Domino REST API not only provides secure access to "jsonified" Domino data,
but also comes with capabilities to ease integration. This enables one to quickly cater to the long tail of applications, giving them a home instead of loosing them to the shadow IT.
Once you know the steps, you can deploy new Single Purpose Applications (I modified the meaning of SPA a little) in no time.
No CORS, no headache
DRAPI allows to host static applications in the keepweb.d
directory. "Static" might be a little misnomer (it only relates to the source files, not the interaction) since a JS file can make your experience quite interactive. Since you run on the same Domain and port as the API, you don't need to worry about CORS
Preparation
Your SPA will live in a sub directory of keepweb.d
, so think about a name, we shall use demo42
here. Add a suitable icon (e.g. 72x72 px png), name it demo42.png
and you are ready to roll. Let's assume our Domino API is running on https://api.demo.io
Work faster with vitejs
viteJS is one of the fronteand tools you want to learn. It features "hot module reload" to speed up development and, when done, packages your application nice and tidy.
It is easy to get started. You need a current version (22.x at time of writing) of nodeJS installed as development tooling.
npm create vite@latest demo42 -- --template vanilla
cd demo42
npm install
This will create the demo42 directory and scaffold the project for you. Before we get started with athe development, let's adjust the environment. In the root of the project create a file vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
base: '/keepweb/demo42/',
server: {
proxy: {
'/api': {
target: 'https://api.demo.io',
changeOrigin: true
}
}
}
});
This allows you to develop in the comfort of your local machine's hot module reload which refreshes your app on svae automagically. It also fixes the path matching to its final destination. Next create in public
the file manifest.json
. This file defines the tile layout for the landing page.
{
"short_name": "Demo 42",
"name": "The final answer to all Demos",
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#aacccc",
"icon": "vite.svg"
}
You can play with colors and icons as you deem fit. Now we are ready to run the application:
npm run dev
Your application will be available on http://localhost:5173/keepweb/demo42. As you can see, the html source code isn't polluted with JavaScript. The vanilla JS example is a little overengineered, the html in main.js
could very well live in index.html
, so I replace the content of main.js
with this:
/* (C) 2025 - copyright holder, License */
// Constants
/* Login goes here */
const formLogin = () => {
let username = document.getElementById('username-input').value;
let password = document.getElementById('password-input').value;
login(username, password);
};
const login = (un, pwd) => {
fetch('/api/v1/auth', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username: un, password: pwd })
})
.then((response) => response.json())
.then((json) => extractCredentials(json))
.catch((err) => console.error(err));
};
const extractCredentials = (json) => {
if (json.bearer) {
let bearer = json.bearer;
window.bearer = bearer;
statusMsg('Login successful');
document.getElementById('credentials').style.display = 'none';
document.getElementById('someid').style.display = 'block';
doSomeWork();
} else {
statusMsg('Login failed');
}
};
const doSomeWork = () => {
statusMsg('Here shall be dragons');
};
const statusMsg = (statusText) => {
document.getElementById('message').innerHTML = statusText;
};
/* Bootstraping */
const bootstrap = () => {
const login_button = document.getElementById('login-btn');
if (login_button) {
login_button.addEventListener('click', formLogin);
} else {
statusMsg('No Login avilable');
}
};
// Keep this at the bottom, so all functions are loaded
if (document.readyState != 'loading') {
bootstrap();
} else {
document.addEventListener('DOMContentLoaded', bootstrap);
}
Of corse, when you plan multiple small apps, you would extract common functions into a module.
My HTML looks like this:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo 42</title>
<link rel="stylesheet" href="src/index.css" />
</head>
<body>
<div class="container">
<header>
<h1>Demo 42 Application</h1>
</header>
<main>
<section class="outline" id="credentials">
<form id="credentials-form">
<p>
<label for="username-input">User Name</label>
<input type="text" id="username-input" />
</p>
<p>
<label for="password-input">Password</label>
<input type="password" id="password-input" />
<button id="login-btn" type="button">Login</button>
</p>
</form>
</section>
<section class="outline" id="someid">
<!-- Application specific code goes here -->
</section>
<section>
<form id="message-box">
<div disabled class="flex3" id="message">Status</div>
</form>
</section>
</main>
<footer>© 2025 Copyright holder</footer>
</div>
<script type="module" src="src/index.js"></script>
</body>
</html>
That's all it takes (We get to API calls in another blog entry) to get started. Once you are happy with the results,
it is timme for deployment.
# create a dist directory with production ready code
npm run build
# Move it to Domino, might differ for your environment
scp -R dist/* domino@api.demo.io/local/notesdata/keepweb.d/demo42/
As usual YMMV
Posted by Stephan H Wissel on 17 March 2025 | Comments (0) | categories: Domino DRAPI WebDevelopment