- Published on
A Micro Service Architecture
How to create full stack rest API and web app micro services
In this tutorial we'll create a full stack web application following the micro service architecture.
We'll use npm, express, and nodejs to build a client web app which requests data from an api server.
Both client and api will be containerized into their own service using Docker.
Lastly we'll use Docker compose to spin up both containers with volumes so that we can work quickly, without having to stop, rebuild, and start our containers.
Create client web app
Create root working directory.
mkdir microservices
cd microservices
Create client microservice directory.
mkdir client
cd client
Generate project files.
npx express-generator .
npm install
Implement looping request for an outside service's resources.
// ./client/views/layout.jade
script((type = 'text/javascript')).setInterval(() => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then((response) => response.json())
.then((json) => {
console.log('JSON:', json)
document.getElementsByTagName('p')[0].innerHTML = new Date(Date.now())
})
}, 1500)
Run application to check it works.
npm start
We should now see we've got a working web application, awesome.
Define client service
Begin containerization of client.
touch Dockerfile
Define containerization strategy.
FROM node:alpine
WORKDIR /usr/src/app
COPY package.json .
COPY package-lock.json ./
RUN yarn
COPY . .
CMD ["yarn", "start"]
Build docker image for this service.
docker build -t client .
- The
-t client
specifies that we're naming/tagging this image asclient
.
Check client image exists locally.
docker images
Run the service using the image we just created to start a new container.
docker run -dp 80:3000 client
- mapping host post
80
to container's internal3000
port. - The
-dp
flag specifies that we're running the container in detached mode
Check running containers.
docker ps
If we go to http:localhost:80
in our web browser we'll now see that our service is running and being handled by Docker, awesome.
Begin creating api service
Create api service directory.
cd ..
mkdir api
cd api
Generate api project files.
npx express-generator --no-view .
Define custom business logic.
// ./api/routes/index.js
const wizards = [
'Harry Potter',
'Hermione Granger',
'Ronald Weasley',
'Neville Longbottom',
'Mad-Eye Moody',
'Bartemius Crouch Sr.',
'Newt Scamander',
'Sirius Black',
'Kingsley Shacklebolt',
'Draco Malfoy',
'Albus Dumberdore',
'Thomas Riddle',
'Salazar Slytherin',
'Godric Gryffindor',
'Gellert Grindelwald',
]
router.get('/magic', function (req, res, next) {
const wizard = wizards[Math.floor(Math.random() * wizards.length)]
res.json(wizard)
})
Run api on different port in order to avoid port number collision.
PORT=3001 npm start
Refactor client business logic.
// ./client/views/layout.jade
script((type = 'text/javascript')).setInterval(() => {
fetch('http://localhost:3001/magic')
.then((response) => response.json())
.then((json) => {
console.log('JSON:', json)
const p = document.getElementsByTagName('p')[0]
const br = document.createElement('br')
p.appendChild(br)
p.append(new Date(Date.now()) + ' ' + json)
})
}, 1500)
Resolve CORS issue.
npm install cors
Update api service config.
// ./api/app.js
var cors = require('cors')
var app = express()
app.use(cors())
Start api service.
PORT=3001 npm start
Define api service containerization strategy.
FROM node:alpine
WORKDIR /usr/src/app
COPY package.json .
COPY package-lock.json ./
RUN yarn
COPY . .
CMD ["yarn", "start"]
Build api service docker image.
docker build -t api .
Run api service container mapping host machine port 3001
to docker container port 3000
.
docker run -dp 3001:3000 api
We should now see docker running both of our containers.
We can use a docker compose file to run multiple containers if we want.
Create docker-compose.yml
file and define two services.
version: '3.9'
services:
web-app-service:
build: ./client
ports:
- 80:3000
rest-api-service:
build: ./api
ports:
- 3001:3000
depends_on:
- web-app-service
Implement hot reloading for developer happiness
Install nodemon.
cd api
npm i nodemon
Configure api service to use nodemon when it starts.
{
"name": "responder",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "nodemon ./bin/www" // Change me
},
"dependencies": {
"cookie-parser": "~1.4.4",
"cors": "^2.8.5",
"cors-anywhere": "^0.4.4",
"debug": "~2.6.9",
"express": "~4.16.1",
"morgan": "~1.9.1",
"nodemon": "^2.0.19"
}
}
Add volumes inside of docker compose file.
version: '3.9'
services:
web-app-service:
build: ./client
ports:
- 80:3000
rest-api-service:
build: ./api
ports:
- 3001:3000
depends_on:
- web-app-service
volumes: # Add us
- ./api:/usr/src/app # Add us
volumes: # Add us
api: # Add us
You should now see that when you change the list of wizards to one person, the changes are reflected immediately, without needing to stop the container, rebuild the image, and restart it, awesome!
Github completed project source code
Could this article be improved? Please make a suggestion.
Your thoughts and comments are welcome and appreciated.