A realworld app micro-services implementation, using the following tech stack:
- NX.dev Monorepo
- Nest.js for BE services
- Mongoose using
@nestjs/mongoose
module for persistence - GraphQL using
@nestjs/graphql
module for gateway GraphQL API - Auth with passport-jwt using
@nestjs/jwt
and@nestjs/passport
modules - Global config using
@nestjs/config
module - Swagger OpenAPI route documentation using
@nestjs/swagger
module - TypeScript
- MongoDB - for service persistence layer running in a docker container
- Redis - Queue implementation over Bull and Redis as a service message bus using
@nestjs/bull
- Run
docker-compose up -d
- Create the mongo db: SSH into the mongo container:
docker exec -it realworld-mongo sh
Once SSHed into it:
mongo
use realworld
quit()
- Change
.env
file values if required. - Run all services using
yarn start:all
- Use PostMan exported collection
realworld.postman_collection.json
for calling APIs
feature-user
is responsible for/api/user
functionality and endpoints.feature-tag
is responsible for/api/tags
functionality and endpoints.feature-profile
is responsible for/api/profie
functionality and endpoints.feature-article
is responsible for/api/articles
functionality and endpoints.
All feature services expose OpenApi Swagger definitions under /api
route.
realworld-examples-app
is the GraphQL gateway for the above services. Code-First GraphQL autogenerated schema is saved underapps/realworld-example-app/schema.graphql
.
The playground is accessible via localhost:3330/graphql
.
For example, getting the current logged-in user details, along with her feed and the list of articles she wrote (with comments for each article returned in feed/articles if they exist):
fragment userFragment on User {
_id
email
username
}
fragment articleFragment on Article {
_id
body
description
favoritesCount
tagList
title
comments {
_id
body
author {
...userFragment
}
}
author {
...userFragment
}
}
query {
me {
...userFragment
feed {
...articleFragment
}
articles {
...articleFragment
}
}
}
For using the GraphQL gateway with authenticaed routes:
- First call the login mutation with
username
(email) andpassword
-> get theaccess_token
. - Add
{"token": <JWT token>}
to the headers.
See data-access
lib for predefined Queries, Mutations and Fragments and their respective autogenerated Types and React hooks.
Feature services and the GraphQL gateway use the following libs:
auth
is responsible for user login and issueing the JWT token, as well as exporting theJwtAuthGuard
used on protected routes in all feature services.models
exposes DTOs and ENUMs used by feature services.shared
exposes reusable services used by feature services.data-access
exposes auto-generated types and react hooks based on schema types and queries/mutations. Generated code is the result of runningyarn codegen
oryarn codegen:watch
. Codegen config iscodegen.yaml
.
We use Redis as the queue persistence layer and Bull & @nestjs/bull
for the queue implementation.
The purpose of this is for feature services to be able to notify something happened in the system, and other feature services can react and complete the flow.
One flow that uses this is:
- User creates an article, with
tagList
that includes some new tags and some existing tags feature-article
service publishes a message with thetagList
on theEvaluateTags
topic to theTags
queue and saves the new article.feature-tag
implements a consumer that processes messages under theEvaluateTags
topic on theTags
queue and creates the new tags in DB.
Another exaple is:
- User is created or updated
feature-user
service publishesuserUpdated
event on theUsers
queue and saves the new/updated user.feature-profile
services implements aUserConsumer
consumer that processes messages underuserUpdated
topic on theUsers
queue and creates/updates the relevant profile in DB.
See Real World App spec here.