Framework App to Web App: Part Six - Mocking, DRAPI and CORS
In the last part, we created for login form and added an eventHandler to call formLogin()
function. However, we didn't go into the code behind that function. That function is pretty basic, offloading the bulk of the processing:
const formLogin = () => {
let username = document.getElementById("username-input").value;
let password = document.getElementById("password-input").value;
login(username, password);
};
Mocking
That function - at least during development - will go down two different routes, depending on the username passed.
const login = (user, pwd) => {
// Set as mocking only
if (user === "John Doe") {
window.isMock = true;
statusMsg("Login successful");
loadSessionData();
toggleSPA("landing", "block");
} else {
fetch(baseUrl + urls.login, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ username: user, password: pwd }),
})
.then((response) => response.json())
.then((json) => {
if (json.hasOwnProperty("status")) {
statusError(json.message);
} else {
if (extractCredentials(json)) {
loadSessionData();
toggleSPA("landing", "block");
}
}
})
.catch((err) => {
statusError(err.message);
});
}
};
If the username is "John Doe", we set a global variable isMock
to true and continue processing. Otherwise we log into Domino REST API, extract the JWT token and continue processing.
The purpose here is that we can develop the application offline and / or with mock data. Even if we have access to Domino REST API, using the mock data means we can update local storage with dummy data and don't need to clean up the database all the time. When we're ready we can test the Domino REST API calls from the app and we can test them from bruno or Postman.
Bruno or Postman allows us to perform the relevant REST API calls to retrieve dummy data that we can test with. In this case it allows us to create local JSON files of ships, ports, countries, spots and more.
Fetch
REST API calls, whether local or to Domino REST API, are done with fetch. fetch is widely supported by browsers meaning the days of XMLHttpRequest or framework variants like dojo.xhr
, jQuery's $.get()
or the more recent Axios
are no longer required. Even if you're familiar with Axios, it's worth switching to fetch because it can handle chunked responses in a browser, which Axios cannot as Stephan Wissel showed in our Engage session.
Fetch is also readily supported in bruno and Postman code generation options, so learning how to use it is very easy. As you can see above, it takes a URL and a JSON object containing options - method, headers and body. If your code does not necessarily return a JSON object, you will need to check the response status code before trying to convert to JSON. The nice thing with Domino REST API is that if some kind of error occurs, the response always contains a "status" property.
CORS
Whenever making browser-based HTTP requests, you need to be aware of Cross-Origin Resource Sharing (CORS). Even when opening a local HTML file, the browser will block CORS requests, which is why you'll need a local HTTP server. In the list of dev tools I mentioned Visual Studio Code's Live Server extension. This is a good way round it if you don't want to install anything additional. Most developers have Node.js installed now and so my preference has become NodeJS's http-server via npx.
This works fine for the mock data calls. But as soon as you call Domino REST API, you may also get hit by CORS problems. Here, Domino REST API is only allowing requests from specified hostnames. Domino REST API automatically accepts "localhost", but if you're using Live Server extension you'll be connecting from 127.0.0.1. The good news is Domino REST API's documentation covers allows you to configure this. We'll need to create keepconfig.d
directory in Domino data directory, if it doesn't exist, and add a config.json file. In the "CORS" JSON object, we'll need to add "127.0.0.1": true
and restart Domino REST API. Now, we can connect from our local app.
NodeJS's http-server allows you to use http://localhost
rather than http://127.0.0.1
. This also gives a URL on your local network (192.168.1.*), but that will not be in DRAPI's CORS setting either, so will fail. Of course this will work for testing the application in a mock session, which may cover most initial scenarios. However, using ngrok will proxy through your local machine, so will work around this problem.
The key here is understanding what CORS means, that the server we're connecting to is not accepting the request because it doesn't come from a hostname it has been configured to accept.
LocalStorage and SessionStorage
Whether mocking or calling Domino REST API, I'm storing content locally in the browser. Again with modern browsers this can be done on desktop browsers or mobile browsers. There are two options - localStorage and sessionStorage. Most of the local data I'm storing in sessionStorage, although we'll see a specific use case for localStorage in the next part of the series.
The important differences are that sessionStorage is automatically cleared when the user closes the tab and localStorage is shared across tabs for the domain and protocol whereas sessionStorage is only shared if the user uses the browser "duplicate tab" functionality.
The key thing to bear in mind with both localStorage and sessionStorage is that both the key and the value are always UTF-16 strings. This means booleans need converting or (probably easier) compared as strings, as we'll see in the next part. More typically, the values stored will be JSON, which means calling JSON.stringify()
before writing and JSON.parse()
after reading.
Wrap Up
In the next session we'll add some styling to the application, which will demonstrate how far CSS has come in recent years.
Table of Contents
- Introduction
- Dev Tools
- Frameworks
- DRAPI
- Home Page
- Mocking, Fetch, DRAPI and CORS
- CSS
- Landing Page Web Component
- Services
- Ship Form Actions
- Ship Search and Save