Windows containers [Two tier application]: Using Nodejs and Postgres

This is one of the project, i did it for my client. In this blog i am using sample code from github.

Note: No Code has used from vendor ends, only sharing manifests files

  1. Open Powershell, Create a node app
mkdir docker-template
cd docker-template
# setup npm, download the package on windows
https://nodejs.org/dist/v18.20.2/node-v18.20.2-win-x64.zip
#Extract the zip
.\npm init
.\npm install express

2. Create a Server.js file (using notepad command)

const express = require("express");
const app = express();
const port = 8080;
app.get("/", async (req, res) => {
res.setHeader("Content-Type", "text/html");
res.status(200);
res.send("<h1>Hello world</h1>");
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);

3. #Run the following command

.\node.exe server.js

4. Open in Browser

http://localhost:8080

5. Let’s containerize

1)Identify the windows OS
2)Download the nodejs module
3)Copy the required file
4)Expose the port where it will listen
5)Pass the command when it starts

6. Write Dockerfile

FROM mcr.microsoft.com/windows/servercore:1803 as installer
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';$ProgressPreference='silentlyContinue';"]
RUN Invoke-WebRequest -OutFile nodejs.zip -UseBasicParsing "https://nodejs.org/dist/v18.20.2/node-v18.20.2-win-x64.zip";expand-archive nodejs.zip
FROM mcr.microsoft.com/windows/nanoserver:1803
WORKDIR "C:\nodejs\node-v18.20.2-win-x64"
COPY --from=installer "C:\nodejs\node-v18.20.2-win-x64" .
RUN SETX PATH "C:\nodejs\node-v18.20.2-win-x64"
RUN npm config set registry https://registry.npmjs.org/
# Create app directory
# this is the location where you will be inside the container
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
# copying packages first helps take advantage of docker layers
COPY package*.json ./
RUN npm install
COPY . .
# Make this port accessible from outside the container
# Necessary for your browser to send HTTP requests to your Node app
EXPOSE 8080
# Command to run when the container is ready
# Separate arguments as separate values in the array
CMD ["node", "server.js"]

7.Build image

Rename-Item .\Dockerfile.txt .\Dockerfile
docker build -t myapp .

8.  Run the container

docker run -d --name=mynode -p 8080:8080 myapp
docker ps
#open in browser http://localhost:8080

9. Identify the Image

Setup postgres same way we setup nodejs
Ref docker file:  Postgres dockerfile
Create a table:database-seed.sql

CREATE TABLE employees
(
id SERIAL,
name text,
title text,
CONSTRAINT employees_pkey PRIMARY KEY (id)
);
INSERT INTO employees(name, title) VALUES
('Meadow Crystalfreak ', 'Head of Operations'),
('Buddy-Ray Perceptor', 'DevRel'),
('Prince Flitterbell', 'Marketing Guru');

10.Docker file for db:dbdockerfile

FROM stellirin/postgres-windows
WORKDIR docker-entrypoint-initdb.d
COPY "database-seed.sql" ./

11. Run the dockerbuild command

docker build -t dbpostgres -f .\dbdockerfile .

12. Modify the server.js file

const { Client } = require("pg");
const express = require("express");
const app = express();
const port = 8080;
const dbConfig = {
password: "root",
user: "root",
host: "postgres",
};
app.use(express.static("public"));
app.get("/employees", async (req, res) => {
try {
const client = new Client(dbConfig); // Create a new client for each connection
await client.connect();
const results = await client.query("SELECT * FROM employees");
res.setHeader("Content-Type", "application/json");
res.status(200);
res.send(JSON.stringify(results.rows));
} catch (error) {
console.error("Query failed:", error);
res.status(500).send("Query failed");
}
});
(async () => {
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
})();

13. Create frontend files for the app

mkdir public
cd public
notepad

14. Copy the below content, and save as index.html

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>My Docker Template</title> <script src="script.js"></script> <link rel="stylesheet" href="styles.css" /> </head> <body> <template> <div class="card"> <img src="https://res.cloudinary.com/dqse2txyi/image/upload/v1639943067/blogs/docker-node/profile-picture_eav2ff.png" alt="Avatar" width="240px" /> <div class="container"> <h4>Placeholder</h4> <p>Placeholder</p> </div> </div> </template> </body> </html>

15.public/styles.css

body {
padding: 12px;
display: flex;
flex-direction: row;
column-gap: 24px;
}
.card {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
border-radius: 5px;
transition: 0.3s;
}
.card:hover {
transform: scale(1.03);
}
.container {
padding: 0 12px;
}
img {
border-radius: 5px 5px 0 0;
}

public/script

fetch("/employees")
.then((response) => response.json())
.then((data) => {
data.forEach((employee) => {
// Select the <template> we created in index.html
const cardTemplate = document.querySelector('template');
// Clone a copy of the template we can insert in the DOM as a real visible node
const card = cardTemplate.content.cloneNode(true);
// Update the content of the cloned template with the employee data we queried from the backend
card.querySelector('h4').innerText = employee.name;
card.querySelector('p').innerText = employee.title;
// Append the card as a child with the employee data to the <body> element on our page
document.body.appendChild(card);
});
});
#Come out from public folder
cd ..

16.Now build the app again
Docker file changes

FROM mcr.microsoft.com/windows/servercore:1803 as installer
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';$ProgressPreference='silentlyContinue';"]
RUN Invoke-WebRequest -OutFile nodejs.zip -UseBasicParsing "https://nodejs.org/dist/v18.20.2/node-v18.20.2-win-x64.zip";expand-archive nodejs.zip
FROM mcr.microsoft.com/windows/nanoserver:1803
WORKDIR "C:\nodejs\node-v18.20.2-win-x64"
COPY --from=installer "C:\nodejs\node-v18.20.2-win-x64" .
#RUN icacls . /grant Everyone:(OI)(CI)F /T
RUN SETX PATH "C:\nodejs\node-v18.20.2-win-x64"
RUN npm config set registry https://registry.npmjs.org/
# Create app directory
# this is the location where you will be inside the container
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
# copying packages first helps take advantage of docker layers
COPY package*.json ./
RUN npm install
RUN npm install pg
# If you are building your code for production
# RUN npm ci --only=production
COPY . .
# Make this port accessible from outside the container
# Necessary for your browser to send HTTP requests to your Node app
EXPOSE 8080
#CMD cmd.exe
# Command to run when the container is ready
CMD ["node", "server.js"]

17,

docker build -t mynode .
docker run -d --name=myapp -p 8080:8080 mynode
Start the container for db first
docker run -d --name=postgres -e POSTGRES_USER=root -e POSTGRES_PASSWORD=root -e POSTGRES_DB=root -p 5432:5432 dbpostgres

18. Open in browser: http://localhost:8080