The Technical Breakdown: One Brain, Two Bodies
The core challenge was efficiency. I didn't want to log into two different systems to update my portfolio and my retro gaming blog. The solution Antigravity and I arrived at was a "Headless" architecture.
In this setup, Strapi (hosted on a DigitalOcean Droplet) acts as the single source of truth—the "brain." It holds all the images, blog posts, and project details. My two websites (anthonymicallef.ca and antonretro.com) act as the "bodies." They don't store content; they just ask the brain for it.
Here is how we connected the pipes:
The Strapi Setup (The Backend)
On the DigitalOcean Droplet, we configured Strapi to run permanently using a process manager called PM2. We created distinct "Collections" for different types of data:
- Project: For the portfolio site (title, description, tech stack, screenshots).
- Content Types: Segregated collections for News, Reviews, and Guides to keep the retro gaming content organized.
We configured the API permissions to act like open gates, allowing my frontend websites to read the data freely without giving them permission to delete or change anything.
The Fetch Logic (The Frontend)
Since I prefer working with Express.js and EJS, I needed a way for my Node server to grab the data before the page loads. Antigravity helped me write a reusable service function to handle this.
Instead of hardcoding database queries, the Express app makes an HTTP GET request to the DigitalOcean Droplet IP.
Rendering the View
Once the data is fetched, Express passes it into the EJS render engine. This was the "aha!" moment. To the end user visiting the site, it looks like a standard static website. They have no idea the content is actually living on a separate server entirely
app.get('/', async (req, res) => {
try {
const articles = await fetchArticles();
// Injecting the Strapi data into the EJS template
res.render('index', { articles: articles });
} catch (error) {
res.status(500).send('Error connecting to CMS');
}
});
The Traffic Cop: Nginx
Pointing a domain name directly to a Node.js application (like Strapi) is risky and inefficient. Node.js is great at logic, but not great at handling raw internet traffic. This is where Nginx comes in.
We installed Nginx on the DigitalOcean Droplet to act as a Reverse Proxy.
Think of Strapi as a VIP living in a penthouse (Port 1337) that doesn't answer the front door. Nginx is the doorman standing at the main entrance (Port 80/443).
The Request: A user visits admin.anthonymicallef.ca.
The Doorman: Nginx checks its guest list (the configuration file). It sees that this domain is allowed.
The Handoff: Nginx transparently forwards the request upstairs to Strapi on localhost:1337.
The Response: Strapi sends the data back to Nginx, which hands it to the user.
This setup also allowed us to use Certbot to automatically install free SSL certificates, so the connection is secure (HTTPS) without us having to write a single line of encryption code in Strapi itself.
Why DigitalOcean?
I chose DigitalOcean for the CMS specifically because Strapi requires a persistent server (it's not "serverless" by nature). By isolating the CMS on its own Droplet, we gained total control over the environment, automated backups, and avoided the "cold start" delays common with serverless functions.