The Divio platform makes it possible to get all the benefits of adopting a decoupled architecture without the increased overhead of app management and deployment.
Thomas Bailey
Marketing
In this article, we'll be going through how to build and run a decoupled web application on the Divio platform.
A common way to build a modern web app is to separate the backend and client side into separate projects. The backend exposes APIs that a frontend can query and consume, allowing multiple frontends specialised across different devices and platforms. This is commonly known as headless or decoupled architecture and brings with it a number of important benefits.
Decoupling the front end from the back end allows for greater flexibility in design and development. Different teams can work on each component with greater autonomy, mixing technologies and frameworks that are best suited for their respective tasks. This flexibility enables faster iteration and deployment cycles.
As frameworks and platforms evolve, a decoupled architecture gives the ability to adopt or evaluate new technologies without the need to rework the entire application. Front-end frameworks are especially susceptible to change and potentially breaking changes.
A decoupled architecture also allows for easier scaling. Rather than increasing the resources allocated to a monolithic app and hoping for an overall performance lift, the front-end and back-end can be provisioned separately. Backend architectures typically comprise multiple components or services, which gives more opportunities to improve the performance of specific high-demand areas.
We will create a simple Node.js-based backend application that will expose an API that will be consumed in a frontend application.
Before you begin, set up your local working environment with the Divio CLI – a powerful command line tool that allows us to work locally and quickly deploy to the Divio cloud.
Divio structures projects under organisations which comprise collaborators. This is especially useful when working with decoupled applications where we can create a team structure accordingly.
Assuming you have already logged in, create a suitable organisation that would typically be working on the backend.
We can choose a blank Node.js template project which we can use as a placeholder to implement an API.
With our app created, we can grab the project locally by running divio app setup backend-server
– assuming the app name should reflect what you have called your app.
Since we chose a blank project, we will start fresh with our minimal implementation.
We can install the dependencies we will be using for the project.
npm install axios cors express
In order to give us some quick data to work with, the example uses the DummyJSON resource.
We can change the index.js accordingly to give us a very basic API to query using /deal as an endpoint.
const express = require('express');
const cors = require('cors');
const axios = require('axios');
// use da express framework
const app = express();
// enabling CORS for all requests
app.use(cors());
// responds with the random products
app.get('/deal', (req, res) => {
axios.get('https://dummyjson.com/products')
.then(function (response) {
let product = response.data.products[Math.floor(Math.random() * response.data.limit)]
res.json({ id: product.id, title: product.title, description: product.description, discount: product.discountPercentage})
})
});
// start the server
app.listen(3000, () => {
console.log('listening on port 3000');
});
Since it's an API and typically web servers reside at port 80, we will adjust the port to set it to 3000 and adjust the Dockerfile accordingly.
FROM node:18.16.0
# for caching optimisations
COPY package*.json /
RUN npm install
COPY . /app
WORKDIR /app
# noop files for non python projects and local development
#RUN echo "#!/bin/bash" > /app/migrate.sh && chmod +x /app/migrate.sh
#RUN echo "#!/bin/bash" > /usr/local/bin/start && chmod +x /usr/local/bin/start
ENV PATH=/node_modules/.bin:$PATH
EXPOSE 3000
CMD ["npm", "start"]
Finally, we can deploy our API by adding our changes to our Divio Git repo.
git add index.js package.json Dockerfile
git commit -m "Added basic functionality"
Git push
We can deploy our changes to the Divio cloud from the Control Panel. By default, the Divio platform provisions test and live environments automatically, making it easy to test and prototype before committing changes to a live environment.
Once deployed, we can use our API directly. Postman is a popular cross-platform tool to visually create and inspect APIs, and we can use it to form a request.
Going back to the root of your Divio account in the Control Panel, we can create a new organisation to contain a frontend team and create another Node.js app, this time with Express, to query our backend server.
We can grab the app locally and start working on it directly divio app setup frontend-web
Our app will simply query the API and echo the result and we can change the skeleton implementation accordingly. Install the packages we can use to make the query and present the result.
npm install axios ejs
From the Divio Control Panel, add the backend server URL as an environmental variable to the frontend project environmental variables.
Finally, we can change the existing logic to query our backend server. We will use EJS as our template engine, so adjust the app.js accordingly.
app.set("view engine", "ejs");
We can change the index.js, the default route, to query our backend server.
var express = require('express');
var router = express.Router();
var axios = require('axios');
router.get('/', async( req, res, next) => {
const serverResponse = await axios.get(process.env.BACKEND_SERVER + '/deal');
res.render("index", {
id: serverResponse.data.id,
title: serverResponse.data.title,
description: serverResponse.data.description,
discount: serverResponse.data.discount
});
})
module.exports = router;
Upon calling our front end in the browser, it queries the separate backend service to return back a random product selection.
We can also create a simple iOS app to query the backend and act as another frontend to our service.
Create a SwiftUI-based app in the usual way with a suitable name, following the default setup in Xcode.
We can use Alamofire as a popular HTTP networking library to simplify making the query to our backend service on the Divio platform.
Initiate a new Podfile in the project directory.
pod init
Add Alamofire as a dependency to the newly created Podfile and run pod install
to create our workspace.
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'Divio example' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
pod 'Alamofire'
# Pods for Divio example
end
We can use the template and modify it accordingly to call and present the product data.
import SwiftUI
struct Product: Decodable {
let id: Int
let title: String
let description: String
let discount: Decimal
}
struct ContentView: View {
@State var product:Product
var body: some View {
VStack(alignment: .leading) {
Text(product.title)
.font(.headline)
Text(product.description)
}
.task {
do {
let url = URL(string: "https://backend-server-stage.us.aldryn.io/deal")!
let (data, _) = try await URLSession.shared.data(from: url)
product = try JSONDecoder().decode(Product.self, from: data)
} catch {
print(error)
}
}
.padding(
)
}
By separating our front and backend services, we can easily scale them up from the Divio Control Panel.
The Metrics feature provides an easy overview of how your resources are being consumed, and you can use it to determine when to add resources to your respective projects or make design decisions – such as caching on a frontend application.
Decoupled web applications are a popular way to develop modern web apps, supporting the agile team ethos and allowing for more autonomy across teams.
The Divio platform addresses the shortfalls in decoupled architecture by simplifying app management and providing tools to scale up separate projects to meet usage demands easily.
Contact us to learn more! You can also keep up with all things Divio over at our LinkedIn and X/Twitter.