Bob is a traveler of time and space who uses this travel blog to tell the world about his epic quests and journeys to foreign planets, galaxies and dimensions.
- normal visitor
- read blogs, blogposts and comments
- register as user or blogger
- users
- write comments on blogposts
- delete own comments
- bloggers
- create, edit and delete own blogs
- create, edit and delete own blogposts
1. Set mongoDB credentials as environment variables:
For security reasons the database credentials and the secret used to sign the json web tokens have to be provided via environment variables.
# PowerShell:
$env:DB_USER="username"
$env:DB_PASS="password"
$env:JWT_SECRET="random_string" # you can set anything you wnat here.
# Bash:
export DB_USER=user
export DB_PASS=password
export JWT_SECRET=random_string # you can set anything you wnat here.
2. Run the app:
# Start microservices, envoy proxy and frontend:
docker-compose up
Troubleshooting
Build with the --no-cache
option to make sure the latest changes are included in the images.
docker-compose build --no-cache
If it still doesn't work, just delete everything.
docker rm -f (docker ps -aq)
docker rmi (docker images -q)
docker-compose up
If you feel slightly masochistic today.
Run frontend and services
after setting the environment variables, each service can be started like this:
cd ./frontend # or user-service, comment-service, blog-service
npm start
# Build and run envoy proxy.
# No need to expose port 9090, 9091 or 9092
# since the envoy proxy can access those ports via host.docker.internal
docker build -t travelbob/envoy -f .\docker\envoy.Dockerfile --no-cache .
docker run -d -p 8080:8080 travelbob/envoy
Importand: On Linux change host.docker.internal
in ./envoy/envoy.yaml
to localhost
.
gRPC uses some features of HTTP/2 that are not yet supported by modern browsers, that's why we need the envoy proxy.
The proxy listens on port 8080
for the frontend to send HTTP/1.1 gRPC calls. Those calls are then translated to HTTP/2 gRPC calls and forwarded to the correct service (as configured in the envoy.yaml config file).
This project uses a free instance of an Atlas mongoDB cluster. The connection string (excluding the user credentials) is hardcoded in the DatabaseAccess.ts
file of each service. Each service has his own database in the cluster.
- Exposes blog API on port
9090
. - It uses the comment-service to delete comments when a blogpost or a full blog is deleted. It also uses the user-service to check if users are allowed to do certain operations like removing a blog etc.
- Exposes comment API on port
9091
. - Makes use of the user-service to check if the current user is allowed to delete or write a comment.
- Exposes users API on port
9092
. - Passwords are saved in the database as hashes.
- It uses jason web tokens to authenticate users. Other services can send a JWT to this service and the service checks if the token is valid.
This service requires the environment veriable JWT_SECRET
. This variable is used to sign the jason web tokens. It can be anything but should be set to a secure string.
- Download protoc.exe Get protoc releases here
- Download protoc-gen-grpc-web.exe Get protoc-gen-grpc releases here
- Make sure that protoc-gen-grpc-web.exe does not have any version info in its name. if it does, remove the version info by renaming the .exe file:
- ✔️ protoc-gen-grpc-web.exe
- ❌ protoc-gen-grpc-web-1.0.6-windows-x86_64.exe
- Add protoc.exe and protoc-gen-grpc-web.exe to your PATH environment variable
- Navigate to folder ../travel-bob/ (project root folder)
- Execute command:
protoc -I="./protos" ./protos/blogposts.proto --js_out=import_style=commonjs:./api/grpc-web-ts --grpc-web_out=import_style=typescript,mode=grpcwebtext:./api/grpc-web-ts
- Navigate to folder ../travel-bob/ (project root folder)
- Execute:
npm i
npm i -g grpc-tools
grpc_tools_node_protoc -I="./protos" ./protos/blogposts.proto --plugin=protoc-gen-ts=$($(Get-Location).ToString())/node_modules/.bin/protoc-gen-ts.cmd --grpc_out=./api/grpc-ts --js_out=import_style=commonjs:./api/grpc-ts --ts_out=./api/grpc-ts
// import requests, replys and custom data types
import { Blog, Timestamp, AllBlogsRequest, AllBlogsReply } from '../../../protos/blogposts_pb';
// import client stub
import { BlogsClient } from '../../../protos/BlogpostsServiceClientPb';
getAllBlogsCallback(err: Error | null, response: AllBlogsReply) {
this.blogs = response.getBlogsList();
}
constructor() {
const grpcClient: BlogsClient = new BlogsClient('127.0.0.1:53001');
grpcClient.getAllBlogs(new AllBlogsRequest(), null, this.getAllBlogsCallback);
}
see blog-service implementation or comment-service implementation for examples.