Repository: pankod/moleculerjs-boilerplate
Branch: master
Commit: 2b7f4ee9959f
Files: 99
Total size: 95.4 KB
Directory structure:
gitextract_izw2cz6n/
├── .dockerignore
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── .travis.yml
├── Dockerfile
├── LICENSE
├── README.md
├── db.sqlite.example
├── docker-compose.env
├── docker-compose.yml
├── documentation/
│ ├── Dockerfile
│ ├── README.md
│ ├── docker-compose.yml
│ ├── docs/
│ │ ├── cli-migration-guide.md
│ │ ├── cli-overview.md
│ │ ├── cli-usage.md
│ │ ├── deployment.md
│ │ ├── eslint.md
│ │ ├── example.md
│ │ ├── features.md
│ │ ├── getting-started.md
│ │ ├── setup.md
│ │ ├── structure.md
│ │ ├── swagger.md
│ │ ├── testing.md
│ │ └── typeorm.md
│ └── website/
│ ├── config.yml
│ ├── core/
│ │ └── Footer.js
│ ├── docs.json
│ ├── i18n/
│ │ └── en.json
│ ├── package.json
│ ├── pages/
│ │ └── en/
│ │ ├── help.js
│ │ ├── index.js
│ │ └── users.js
│ ├── sidebars.json
│ ├── siteConfig.js
│ └── static/
│ └── css/
│ └── custom.css
├── moleculer.config.ts
├── package.json
├── public/
│ └── index.html
├── services/
│ ├── api.service.ts
│ ├── attack.service.ts
│ ├── index.ts
│ └── planet.service.ts
├── src/
│ ├── Entities/
│ │ ├── Connection.ts
│ │ ├── Planet.ts
│ │ ├── Weapon.ts
│ │ └── index.ts
│ ├── Interfaces/
│ │ ├── Meta/
│ │ │ ├── DamageMetaOutDto.d.ts
│ │ │ └── index.ts
│ │ ├── Repositories/
│ │ │ ├── Planet/
│ │ │ │ ├── DecreaseShieldOutDto.d.ts
│ │ │ │ └── index.ts
│ │ │ └── Weapon/
│ │ │ ├── DecreaseAmmoOutDto.d.ts
│ │ │ └── index.ts
│ │ ├── Services/
│ │ │ ├── Attack/
│ │ │ │ ├── IAttack.d.ts
│ │ │ │ └── index.ts
│ │ │ └── Planet/
│ │ │ ├── IPlanet.d.ts
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── Meta/
│ │ ├── CalculateMeta.ts
│ │ └── index.ts
│ ├── Repositories/
│ │ ├── ErrorHelpers.ts
│ │ ├── Planet.ts
│ │ ├── Shared.ts
│ │ ├── Weapon.ts
│ │ └── index.ts
│ └── ServiceHelpers/
│ ├── AttackHelper.ts
│ ├── PlanetHelper.ts
│ └── index.ts
├── swagger/
│ ├── config.js
│ ├── index.js
│ ├── package.json
│ └── swagger.json
├── swaggerConfig.json
├── test/
│ ├── Config/
│ │ ├── Connection.ts
│ │ ├── SetupDatabase.ts
│ │ └── mock.setup.ts
│ ├── Integration/
│ │ ├── attack.spec.ts
│ │ └── planet.spec.ts
│ ├── Seeder/
│ │ ├── AttackSeeder.ts
│ │ ├── PlanetSeeder.ts
│ │ └── index.ts
│ ├── Unit/
│ │ ├── Entities/
│ │ │ └── Connection.spec.ts
│ │ ├── Meta/
│ │ │ └── CalculateMeta.spec.ts
│ │ ├── MicroServices/
│ │ │ ├── attack.spec.ts
│ │ │ └── planet.spec.ts
│ │ ├── Repositories/
│ │ │ ├── ErrorHelpers.spec.ts
│ │ │ ├── Planet.spec.ts
│ │ │ └── Weapon.spec.ts
│ │ └── ServiceHelpers/
│ │ ├── AttackHelper.spec.ts
│ │ └── PlanetHelper.spec.ts
│ └── Utils/
│ ├── BrokerHelper.ts
│ ├── DummyContext.spec.ts
│ ├── DummyContext.ts
│ └── index.ts
├── tsconfig.json
└── tsconfig.production.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
node_modules
dist
project-cli
documentation
================================================
FILE: .editorconfig
================================================
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = tab
indent_size = 4
[package.json]
indent_style = space
indent_size = 2
================================================
FILE: .eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
],
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
project: './tsconfig.json',
},
rules: {
// Disabled Rules
'@typescript-eslint/camelcase': ['error', { properties: 'never' }],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-member-accessibility': 'off',
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/no-empty-interface': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-object-literal-type-assertion': 'off',
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/no-magic-numbers': 'off',
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-type-alias': 'off',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-object-literal-type-assertion': 'off',
'@typescript-eslint/prefer-interface': 'off',
// Enabled rules
'@typescript-eslint/adjacent-overload-signatures': 'error',
'@typescript-eslint/array-type': 'error',
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/class-name-casing': 'error',
'@typescript-eslint/member-ordering': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-triple-slash-reference': 'error',
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/no-unnecessary-qualifier': 'error',
'@typescript-eslint/no-parameter-properties': 'error',
'@typescript-eslint/no-misused-new': 'error',
'@typescript-eslint/no-for-in-array': 'error',
'@typescript-eslint/no-angle-bracket-type-assertion': 'error',
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/prefer-function-type': 'error',
'@typescript-eslint/promise-function-async': 'error',
'@typescript-eslint/restrict-plus-operands': 'error',
'@typescript-eslint/semi': 'error',
'@typescript-eslint/type-annotation-spacing': 'error',
'@typescript-eslint/unified-signatures': 'error',
complexity: ['error', { max: 3 }],
'max-depth': ['error', { max: 4 }],
'prefer-const': [
'error',
{
destructuring: 'all',
ignoreReadBeforeAssign: true,
},
],
},
};
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# history
.history
# sqlite
db.sqlite
# macos
.DS_Store
# builds
/project-cli/dist
dist/
/documentation/website/build
# webstorm
.idea/
*.iml
================================================
FILE: .prettierrc
================================================
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 4,
"useTabs": true
}
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- "10"
install:
- npm ci
script:
- npm run lint
- npm run test
before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
after_success:
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
================================================
FILE: Dockerfile
================================================
FROM node:10-alpine
RUN mkdir /app
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production
ENV NODE_ENV=production
CMD ["npm", "start"]
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 Pankod
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
Moleculer JS Microservice Boilerplate with Typescript, TypeORM, CLI, Service Helpers, Swagger, Jest, Docker, Eslint support and everything you will ever need to deploy rock solid projects..
## About
A microservice is a single self-contained unit which, together with many others, makes up a large application. By splitting your app into small units every part of it is independently deployable and scalable, can be written by different teams and in different programming languages and can be tested individually.
Moleculer is a fast, modern and powerful microservices framework for Node.js. It helps you to build efficient, reliable & scalable services.
This boilerplate make it easier to get started with a well-structured Node.js microservices with Typescript.
## Features
This boilerplate includes the latest powerfull tools.
* **Moleculer** - Moleculer is a fast, modern and powerful microservices framework for Node.js.
* **Typescript** - Superset of JavaScript which primarily provides optional static typing, classes and interfaces. path support(allias)
* **Built-in Project CLI**- Create services, entities, interfaces, and tests with one command by using built-in cli.
* **Docker** - A tool designed to make it easier to create, deploy, and run applications by using containers.
* **Eslint** - The pluggable linting utility.
* **Swagger** - A framework backed by a large ecosystem of tools that helps developers design, build, document, and consume RESTful Web services.
* **Jest** - Javascript testing framework , created by developers who created react
* **TypeORM** - TypeORM is specifically an ORM that converts data between JavaScript / TypeScript to a variety of databases.
* **Service Helpers** - Provides easy communication contract between services.
## Setup & Documentation
Please refer to our [setup guide](https://pankod.github.io/moleculerjs-boilerplate/docs/setup) to create a new app.
For more detailed documentation, check out https://pankod.github.io/moleculerjs-boilerplate/
## Built-in CLI
moleculerjs-boilerplate is shipped with a CLI tool to streamline the creation of new microservices. By using the CLI tool, you may easily add entities, services to your project and have all the required interfaces and imports are automatically created for you.
To start the CLI, you may run the following npm command:
```
npm run cli
```
After answering questions it generates files in miliseconds.
## License
Licensed under the MIT License, Copyright © 2019-present Pankod
================================================
FILE: docker-compose.env
================================================
NAMESPACE=
LOGGER=true
LOGLEVEL=info
SERVICEDIR=dist/services
TRANSPORTER=nats://nats-server:4222
================================================
FILE: docker-compose.yml
================================================
version: "3.0"
services:
api:
build: .
image: api
env_file: docker-compose.env
environment:
SERVICES: api
PORT: 3000
ports:
- "3000:3000"
attack:
build: .
image: attack
env_file: docker-compose.env
environment:
SERVICES: attack
planet:
build: .
image: planet
env_file: docker-compose.env
environment:
SERVICES: planet
nats-server:
image: nats:latest
ports:
- "4222:4222"
================================================
FILE: documentation/Dockerfile
================================================
FROM node:8.11.4
WORKDIR /app/website
EXPOSE 3000 35729
COPY ./docs /app/docs
COPY ./website /app/website
RUN yarn install
CMD ["yarn", "start"]
================================================
FILE: documentation/README.md
================================================
This website was created with [Docusaurus](https://docusaurus.io/).
# What's In This Document
* [Get Started in 5 Minutes](#get-started-in-5-minutes)
# Get Started in 5 Minutes
1. Go to the website folder:
```sh
# Go to the folder
$ cd website
```
2. Make sure all the dependencies for the website are installed:
```sh
# Install dependencies
$ npm i
```
3. Run your dev server:
```sh
# Start the site
$ npm start
```
================================================
FILE: documentation/docker-compose.yml
================================================
version: "3"
services:
docusaurus:
build: .
ports:
- 3000:3000
- 35729:35729
volumes:
- ./docs:/app/docs
- ./website/blog:/app/website/blog
- ./website/core:/app/website/core
- ./website/i18n:/app/website/i18n
- ./website/pages:/app/website/pages
- ./website/static:/app/website/static
- ./website/sidebars.json:/app/website/sidebars.json
- ./website/siteConfig.js:/app/website/siteConfig.js
working_dir: /app/website
================================================
FILE: documentation/docs/cli-migration-guide.md
================================================
---
id: cli-migration-guide
title: CLI Migrate Guide
sidebar_label: Migration Guide
---
Initially, moleculer's `project-cli` was inside the boilerplate repository. This makes it hard to upgrade cli version for users. We decided to move project cli to it's own package.
This guide will walk you through migrating to new cli.
If you have `@pankod/pankod-cli` in your devDependencies, you can skip this guide.
- Remove `project-cli` folder completely. (`rm -rf project-cli`)
- npm install -D @pankod/pankod-cli
- Add this to your package.json;
```
"pankod": {
"projectType": "moleculer"
},
```
- Update your `cli` script in `package.json`;
```
"cli": "pankod-cli add",
```
That's it! Now you should be able to use new cli, add Entity or Service.
================================================
FILE: documentation/docs/cli-overview.md
================================================
---
id: cli-overview
title: Overview
sidebar_label: Overview
---
moleculerjs-boilerplate is shipped with a CLI tool to streamline the creation of new microservices. By using the CLI tool, you may easily add entities, services, tests to your project and have all the required interfaces and imports are automatically created for you.
To start the CLI, you may run the following npm command:
```sh
npm run cli
```
After answering the questions, it will generate files in miliseconds.
================================================
FILE: documentation/docs/cli-usage.md
================================================
---
id: cli-usage
title: Usage
sidebar_label: Usage
---
After starting, an interactive menu will let you file to be created. Firstly, you'll be asked for the type of the files whether it's a entity or service. Then you'll be prompted with the other options relevant to your selection.
For example, let's go through the steps of the creation of a service.
>Enter service name
- Enter the desired filename for the service. Spaces are not allowed!
- The tool will check for the existing filenames in the project and reject if found any.
>Is service open to outside?
- If you choose yes, cli adds swagger tags to service file.
>Are you going to have a database?
- If yes, it adds `connectionInstance` method and import to service file.
After answering questions it generates files and set imports which specified at the below.
- Creates new service with given name into services directory.
- Adds service export into ``services/index.ts`` file.
- Creates a new interface file in `src/Interfaces/Services` directory.
- Create index.ts file into service interface folder.
- Adds file exports into `src/Interfaces/index.ts` file.
- Adds service import in `test/Utils/BrokerHelper.ts` file.
- Adds service into setupBroker method in `test/Utils/BrokerHelper.ts` file.
- Create service helper into `src/ServiceHelper` directory.
- Adds service into index in `src/ServiceHelper/index.ts` file.
- Create service helper test into `test/Unit/ServiceHelper` directory.
- Create service test into `test/Unit/MicroService` directory.
- Create integration test into `test/Integration` directory.
================================================
FILE: documentation/docs/deployment.md
================================================
---
id: deployment
title: Deployment
sidebar_label: Deployment
---
## Build
Builds the app for production into the dist folder.
```sh
npm run build
```
Once you built the app, you can run microservices with;
```sh
npm run start
```
## Docker
If you want to run the app with Docker, we already included `docker-compose.yaml`, `docker-compose.env` files and scripts to start and stop Docker deployment.
To start;
```js
npm run dc:up
```
To stop;
```js
npm run dc:down
```
> We are using `NATS` for communication between microservices in Docker deployment.
*docker-compose.yaml*
```
version: "3.0"
services:
api:
build: .
image: api
env_file: docker-compose.env
environment:
SERVICES: api
PORT: 3000
ports:
- "3000:3000"
attack:
build: .
image: attack
env_file: docker-compose.env
environment:
SERVICES: attack
planet:
build: .
image: planet
env_file: docker-compose.env
environment:
SERVICES: planet
nats-server:
image: nats:latest
ports:
- "4222:4222"
```
Refer to moleculer deployment documentation;
https://moleculer.services/docs/0.13/deploying.html
================================================
FILE: documentation/docs/eslint.md
================================================
---
id: eslint
title: Usage
sidebar_label: ESLint
---
ESLint integrated to boilerplate for linting.
Lints with Eslint. Useful for CI.
```SH
npm run lint
```
Lints and fixes fixable problems.
```SH
npm run format
```
>Refer to [offical documentation](https://eslint.org/) for detailed usage.
================================================
FILE: documentation/docs/example.md
================================================
---
id: example-app
title: Example App
sidebar_label: Example App
---
>>>*"The defense systems on Alderaan, despite the Senator's protestations to the contrary, were as strong as any in the Empire. I should conclude that our demonstration was as impressive as it was thorough."*
―Darth Vader
We've integrated microservice example to show how services connect together. The aim of this example is to demonstrate how two different services communicate with each other.
Running `npm run setup-db` will seed the database and create a planet and a weapon. Planet's name is `Alderaan`, weapon's name is `Death Star`.
According to the story, Death Star will try to destroy Alderaan;
- **Death Star**: A weapon to destroy planets.
- **Alderaan**: A planet far far away.
TODO: Death Star is a planet destroyer weapon. It destroyed Alderaan.
## Overview
We have 2 services as seen below;
- Attack service with **Fire Action** (**api/attack/Fire**)
Death Star will use this service to attack another planet.
- Planet service with **Defend Action** (**api/planet/Defend**)
Alderaan will use this service to defend itself.
To get more information about **Actions**, visit [Moleculer Documentation](https://moleculer.services/docs/0.13/actions.html)
## Entities:
### Planet
```js
{
name: 'Alderaan',
shield: 100000
}
```
### Weapon
```js
{
name: 'Death Star',
damage: 1000,
ammo: 1000
}
```
To get more information about `Entities`, visit [TypeOrm Documentation](https://typeorm.io/#/entities)
## Services
Both services has a structure as seen below. We can make API request to these services;
Let's fire!
### Attack Service
Attack service will get weapon and planet names as parameters. Then it will use this information to make request to Planet service to find out how much damage is done and how much shield planet has left.
You can see example of calling a service from another service below, in **Communication between services** section.
See example attack request;
```sh
POST http://localhost:3000/api/attack/Fire
Params: {
"weaponName":"Death Star",
"planetName": "Alderaan"
}
Response: {
"planetMessage": "Planet took 474 damage and has 99404 shield left.",
"weaponMessage": "Death Star did 474 damage and left 999 ammo."
}
```
Then this service will decrease ammo of the given weapon and return messages about what damage is done and how much ammo left.
You can see how we decrease the ammo of the weapon below, in **Repositories** section.
### Planet Service:
Planet service will get weapon and planet name as parameters. Then, using **CalculateMeta** helper function, will calculate how much damage will be done.
After getting the damage, service will decrease given planet's shield and return informing message about planet and how much damage is done.
```sh
POST http://localhost:3000/api/planet/Defend
Params: {
"weaponName": "Death Star",
"planetName": "Alderaan"
}
Response: {
"damage": 122,
"planetMessage": "Planet took 122 damage and has 99878 shield left."
}
```
To get more information about `Moleculer Services`, please visit [Moleculer Documentation](https://moleculer.services/docs/0.13/services.html)
## Repositories
In order to interact with the database, we are using **Repository** pattern. Every **Entity** has a corresponding **Repository**.
For example, in order to fetch **Death Star Weapon** from the database, and make it ready to fire, we should;
```
const deathStar = WeaponRepository.Get('Death Star')
```
When a weapon fires, it loses 1 ammo. We should update the database properly by using repository.
```js
const { remainingAmmo } = await WeaponRepository.DecreaseAmmo('Death Star');
```
If you want to update the shield of the planet;
```ts
const { remainingShield } = await PlanetRepository.UpdateShield('Alderaan', 5000);
```
To get more information about **Repository Pattern** please [visit here](https://deviq.com/repository-pattern/)
## Communication between services
In MoleculerJS we are calling other services with a simple string parameter where service name and method separated by dot.
This is not useful. Since we don't have autocomplete, we need to remember every service and action name to call them.
```
const params = {...}
ctx.call("planet.Defend", params)
```
As the codebase goes bigger, it becomes harder and harder to remember every single service name and their actions.
To fix this, we introduced **ServiceHelpers** to call services. Every service has a helper;
```
export namespace AttackHelper {
const prefix: string = 'attack';
export const Fire = async (ctx: Context, params: IAttack.AttackInDto): Promise =>
await ctx.call(`${prefix}.Fire`, params);
}
```
Then we can use it in other services;
```
AttackHelper.Fire(ctx, {...params})
```
If we want to call `planet` service's `Defend` action inside `Attack` service, it's straightforward;
```
const { damage, planetMessage } = await PlanetHelper.Defend(ctx, { weaponName, planetName });
```
As you can see above, it's just invoking a function and getting variables back.
================================================
FILE: documentation/docs/features.md
================================================
---
id: features
title: What's included?
---
moleculerjs-boilerplate project provides a lot of features out of the box. Below is an overview of the included technologies.
* **Moleculer** - Moleculer is a fast, modern and powerful microservices framework for Node.js.
* **Typescript** - Superset of JavaScript which primarily provides optional static typing, classes and interfaces. path support(allias)
* **Built-in Project CLI**- Create service, model, interface and unit-test with one command by using built-in cli.
* **Docker** - A tool designed to make it easier to create, deploy, and run applications by using containers.
* **Eslint** - The pluggable linting utility.
* **Swagger** - A framework backed by a large ecosystem of tools that helps developers design, build, document, and consume RESTful Web services.
* **Jest** - Javascript testing framework , created by developers who created react
* **TypeORM** - TypeORM is specifically an ORM that converts data between JavaScript / TypeScript to a variety of databases.
* **Service Helpers** - Better way to consume services.
* **Seeder** - Easy to integrate, well structured seeder for tests.
================================================
FILE: documentation/docs/getting-started.md
================================================
---
id: getting-started
title: Getting Started
sidebar_label: Getting Started
---
## About Moleculer
Moleculer is a fast, modern and powerful microservices framework for Node.js. It helps you to build efficient, reliable & scalable services. Moleculer provides many features for building and managing your microservices.
### Features of Moleculer
- Promise-based solution (async/await compatible)
- request-reply concept
- support streams
- support event-driven architecture with balancing
- built-in service registry & dynamic service discovery
- load balanced requests & events (round-robin, random, cpu-usage, latency)
- many fault tolerance features (Circuit Breaker, Bulkhead, Retry, Timeout, Fallback)
- supports middlewares
- supports versioned services
- service mixins
- built-in caching solution (memory, Redis)
- pluggable transporters (TCP, NATS, MQTT, Redis, NATS Streaming, Kafka)
- pluggable serializers (JSON, Avro, MsgPack, Protocol Buffers, Thrift)
- pluggable validator
- multiple services on a node/server
- all nodes are equal, no master/leader node
- parameter validation with fastest-validator
- built-in health monitoring & metrics
- official API gateway module and many other modules…
This boilerplate make it easier to get started with a well-structured Moleculer Microservice.
For more information about [Moleculer](https://moleculer.services)
================================================
FILE: documentation/docs/setup.md
================================================
---
id: setup
title: Setup
---
To create a new app, you may choose one of the following methods:
#### Clone the repository:
```sh
git clone https://github.com/pankod/moleculerjs-boilerplate.git
```
#### Install the dependencies:
```sh
npm install
```
#### Setup Database (Optional):
We've integrated a microservice example to show how services connect together. We have an example database; you can copy this to play with services.
```sh
npm run setup-db
```
#### Running Microservices:
```sh
npm run dev
```
This command will build and run services in the `services` directory. Also it will generate documentation and start swagger UI at port 3001.
#### Running with Docker
```sh
npm run dc:up
```
This should start services independently at port 3000.
================================================
FILE: documentation/docs/structure.md
================================================
---
id: structure
title: Structure
sidebar_label: Structure
---
After the setup is complete, your app should have the following directory structure:
```js
.
├── public
│ ├── banner.png
│ ├── favicon.ico
│ └── index.html
├── services
│ ├── api.service.ts
│ ├── attack.service.ts
│ ├── index.ts
│ └── planet.service.ts
├── src
│ ├── Entities
│ │ ├── Connection.ts
│ │ ├── Planet.ts
│ │ ├── Weapon.ts
│ │ └── index.ts
│ ├── Interfaces
│ │ ├── Meta
│ │ │ ├── DamageMetaOutDto.d.ts
│ │ │ └── index.ts
│ │ ├── Repositories
│ │ │ ├── Planet
│ │ │ │ ├── DecreaseShieldOutDto.d.ts
│ │ │ │ └── index.ts
│ │ │ └── Weapon
│ │ │ ├── DecreaseAmmoOutDto.d.ts
│ │ │ └── index.ts
│ │ ├── Services
│ │ │ ├── Attack
│ │ │ │ ├── IAttack.d.ts
│ │ │ │ └── index.ts
│ │ │ └── Planet
│ │ │ ├── IPlanet.d.ts
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── Meta
│ │ ├── CalculateMeta.ts
│ │ └── index.ts
│ ├── Repositories
│ │ ├── ErrorHelpers.ts
│ │ ├── Planet.ts
│ │ ├── Shared.ts
│ │ ├── Weapon.ts
│ │ └── index.ts
│ └── ServiceHelpers
│ ├── AttackHelper.ts
│ ├── PlanetHelper.ts
│ └── index.ts
├── swagger
│ ├── config.js
│ ├── index.js
│ ├── package.json
│ └── swagger.json
├── test
│ ├── Config
│ │ ├── Connection.ts
│ │ ├── SetupDatabase.ts
│ │ └── mock.setup.ts
│ ├── Integration
│ │ ├── attack.spec.ts
│ │ └── planet.spec.ts
│ ├── Seeder
│ │ ├── AttackSeeder.ts
│ │ ├── PlanetSeeder.ts
│ │ └── index.ts
│ ├── Unit
│ │ ├── Helper
│ │ │ ├── AttackHelper.spec.ts
│ │ │ └── PlanetHelper.spec.ts
│ │ ├── Meta
│ │ │ └── CalculateMeta.spec.ts
│ │ ├── MicroServices
│ │ │ ├── attack.spec.ts
│ │ │ └── planet.spec.ts
│ │ └── Repositories
│ │ ├── ErrorHelpers.spec.ts
│ │ ├── Planet.spec.ts
│ │ └── Weapon.spec.ts
│ └── Utils
│ ├── BrokerHelper.ts
│ ├── DummyContext.ts
│ └── index.ts
├── Dockerfile
├── LICENSE
├── README.md
├── banner.png
├── cli.gif
├── db.sqlite
├── db.sqlite.example
├── docker-compose.env
├── docker-compose.yml
├── moleculer.config.ts
├── moleculerjs-cover.png
├── package-lock.json
├── package.json
├── swaggerConfig.json
├── tsconfig.json
└── tsconfig.production.json
```
================================================
FILE: documentation/docs/swagger.md
================================================
---
id: swagger
title: Usage
sidebar_label: Swagger
---
`swagger-jsdoc` is a library which returns the validated OpenAPI specification as JSON or YAML.
`moleculerjs-boilerplate` uses `swagger-jsdoc` for generating swagger.json file.
This code block shows swagger-jsdoc in **services/attack.service** of example app.
````
/**
* @swagger
*
* attack/Fire:
* post:
* description: Attacks to the planet with given weapon.
* produces:
* - application/json
* consumes:
* - application/json
* parameters:
* - in: body
* name: params
* schema:
* type: object
* required:
* - weaponName
* - planetName
* properties:
* weaponName:
* type: string
* default: Death Star
* planetName:
* type: string
* responses:
* 200:
* description: Example attack result
* 422:
* description: Missing parameters
*/
````
After running the service you will see the example API documentation on localhost:3001 which is shown at the below.
>Refer to [offical documentation](https://swagger.io/docs/specification/2-0/basic-structure/) for detailed usage.
================================================
FILE: documentation/docs/testing.md
================================================
---
id: testing
title: Testing
sidebar_label: Testing
---
This boilerplate uses [Jest](https://jestjs.io/docs/en/getting-started) for Unit Testing and [SuperTest](https://github.com/visionmedia/supertest) for integration tests.
## Seeder
We have included seeder to the project. Also we have a helper called `setupDatabase`, you can include this to `beforeEach` block of your tests.
This helper will reset database and seed the database.
Ideally, we are adding this setupDatabase helper to the `beforeEach` block. This will make sure that we run tests against freshly seeded database.
```ts
const setupDatabase = async (): Promise => {
await CreateConnection();
await Seeder.seed();
};
```
```ts
beforeEach(async () => {
await setupDatabase();
});
```
## Examples
### Testing Service Helpers
```js
describe('Weapon service helpers', () => {
it('should trigger Fire method', async () => {
const params: IAttack.AttackInDto = {
weaponName: 'Death Star',
planetName: 'Alderaan',
};
const result = await AttackHelper.Fire(DummyContext.getCall(params), params);
expect(result).toBeDefined();
});
});
```
### Testing Metas
```js
it('should calculate remaining shield', async () => {
const entityManager = getManager();
const weapon = await entityManager.findOne(Weapon, { name: 'Death Star' });
const planet = await entityManager.findOne(Planet, { name: 'Alderaan' });
const { damage, remainingShield } = await CalculateMeta.Damage(weapon, planet);
expect(remainingShield).toEqual(planet.shield - damage);
});
```
### Testing Service Methods
```typescript
const broker = BrokerHelper.setupBroker();
beforeEach(async () => {
await broker.start();
await setupDatabase();
});
afterEach(async () => {
await broker.stop();
});
describe('Test attack service', () => {
const params = {
planetName: 'Alderaan',
weaponName: 'Death Star',
};
describe('Fire method', async () => {
it('when ammo is up', async () => {
const { planetMessage, weaponMessage } = await AttackHelper.Fire(broker as any, params);
expect(planetMessage).toContain('Planet took');
expect(weaponMessage).toContain('Death Star did');
});
it('when ammo is empty', async () => {
getManager().update(Weapon, { name: 'Death Star' }, { ammo: 0 });
const { planetMessage, weaponMessage } = await AttackHelper.Fire(broker as any, params);
expect(planetMessage).toEqual('Planet took no damage');
expect(weaponMessage).toEqual('This weapon has no ammo');
});
});
});
```
### Testing Repositories
```typescript
describe('Planet Repository Methods', () => {
beforeEach(async () => {
await setupDatabase();
});
afterEach(async () => {
await getConnection().close();
});
describe('Get', () => {
it('should get planet if there is any', async () => {
const planetName = 'Alderaan';
const planet = await PlanetRepository.Get(planetName);
expect(planet.name).toEqual(planetName);
});
it('should raise error', async () => {
const planetName = 'I dont exist';
expect(() => PlanetRepository.Get(planetName)).toThrowError;
});
});
it('should update shield', async () => {
const planetName = 'Alderaan';
const expectedShield = 1000;
const { remainingShield } = await PlanetRepository.UpdateShield(planetName, expectedShield);
expect(remainingShield).toEqual(expectedShield);
});
});
```
### Integration Tests For Services
```typescript
const request = require("supertest");
const broker = BrokerHelper.setupBroker();
let server;
beforeEach(async () => {
await setupDatabase();
});
afterEach(async () => {
await getConnection().close();
});
beforeAll(() => {
const service = broker.createService(ApiGateway);
server = service.server;
return broker.start();
});
afterAll(() => broker.stop());
describe("Test Attack service requests", () => {
it("Test POST request on attack service Fire method", () => {
const params = {
planetName: 'Alderaan',
weaponName: 'Death Star'
}
return request(server)
.post("/attack/Fire")
.query({ ...params })
.then(res => {
expect(res.statusCode).toBe(200);
expect(res.headers["content-type"]).toBe("application/json; charset=utf-8");
expect(res.body.planetMessage).toContain('Planet took');
expect(res.body.weaponMessage).toContain('Death Star did');
});
});
});
```
================================================
FILE: documentation/docs/typeorm.md
================================================
---
id: typeorm
title: TypeORM & Repository
sidebar_label: TypeORM
---
`createConnection` method in `src/Entities/Connection` file is responsible for creating database connection.
Then, `Repositories` handles database interactions with `getManager` method of TypeORM.
Here is a planet Entity example from example app;
```
//#region Global Imports
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
//#endregion Global Imports
@Entity()
export class Planet {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
shield: number;
}
```
And this is Planet repository;
```
//#region Global Imports
import { getManager } from 'typeorm';
//#endregion Global Imports
//#region Local Imports
import { Planet } from '@Entities/Planet';
import { getResource } from './Shared';
//#endregion Local Imports
//#region Interface Imports
import { DecreaseShieldOutDto } from '@Interfaces';
//#endregion Interface Imports
export namespace PlanetRepository {
export const Get = async (planetName: string): Promise => {
return await getResource(Planet, { name: planetName });
};
export const DecreaseShield = async (
planetName: string,
remainingShield: number,
): Promise => {
const planet = await getResource(Planet, { name: planetName });
planet.shield = remainingShield;
await getManager().save(planet);
return { remainingShield: planet.shield };
};
}
```
>Refer to [offical documentation](https://typeorm.io/#/entities) for detailed usage.
================================================
FILE: documentation/website/config.yml
================================================
GIT_USER=docusaurus-bot
USE_SSH=true
================================================
FILE: documentation/website/core/Footer.js
================================================
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const docs = require("../docs.json");
class Footer extends React.Component {
constructor() {
super();
this.state = {
docs
}
}
docUrl(doc, language) {
const baseUrl = this.props.config.baseUrl;
const docsUrl = this.props.config.docsUrl;
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
const langPart = `${language ? `${language}/` : ''}`;
return `${baseUrl}${docsPart}${langPart}${doc}`;
}
pageUrl(doc, language) {
const baseUrl = this.props.config.baseUrl;
return baseUrl + (language ? `${language}/` : '') + doc;
}
render() {
return (
);
}
}
module.exports = Footer;
================================================
FILE: documentation/website/docs.json
================================================
[
{
"id": "getting-started",
"title": "Getting Started"
},
{
"id": "example-app",
"title": "Example App",
"sidebarLevel": null
},
{
"id": "testing",
"title": "Features",
"sidebarLevel": null
},
{
"id": "setup",
"title": "Setup",
"sidebarLevel": null
}
]
================================================
FILE: documentation/website/i18n/en.json
================================================
{
"_comment": "This file is auto-generated by write-translations.js",
"localized-strings": {
"next": "Next",
"previous": "Previous",
"tagline": "Moleculer JS Microservice Boilerplate with Typescript, TypeORM, CLI, Service Helpers, Swagger, Jest, Docker, Eslint support and everything you will ever need to deploy rock solid projects.",
"docs": {
"cli-migration-guide": {
"title": "CLI Migrate Guide",
"sidebar_label": "Migration Guide"
},
"cli-overview": {
"title": "Overview",
"sidebar_label": "Overview"
},
"cli-usage": {
"title": "Usage",
"sidebar_label": "Usage"
},
"deployment": {
"title": "Deployment",
"sidebar_label": "Deployment"
},
"eslint": {
"title": "Usage",
"sidebar_label": "ESLint"
},
"example-app": {
"title": "Example App",
"sidebar_label": "Example App"
},
"features": {
"title": "What's included?"
},
"getting-started": {
"title": "Getting Started",
"sidebar_label": "Getting Started"
},
"setup": {
"title": "Setup"
},
"structure": {
"title": "Structure",
"sidebar_label": "Structure"
},
"swagger": {
"title": "Usage",
"sidebar_label": "Swagger"
},
"testing": {
"title": "Testing",
"sidebar_label": "Testing"
},
"typeorm": {
"title": "TypeORM & Repository",
"sidebar_label": "TypeORM"
}
},
"links": {
"Docs": "Docs",
"Github": "Github"
},
"categories": {
"Introduction": "Introduction",
"Overview": "Overview",
"Features": "Features",
"Project CLI": "Project CLI"
}
},
"pages-strings": {
"Help Translate|recruit community translators for your project": "Help Translate",
"Edit this Doc|recruitment message asking to edit the doc source": "Edit",
"Translate this Doc|recruitment message asking to translate the docs": "Translate"
}
}
================================================
FILE: documentation/website/package.json
================================================
{
"scripts": {
"examples": "docusaurus-examples",
"start": "docusaurus-start",
"build": "docusaurus-build",
"publish-gh-pages": "docusaurus-publish",
"write-translations": "docusaurus-write-translations",
"version": "docusaurus-version",
"rename-version": "docusaurus-rename-version"
},
"devDependencies": {
"docusaurus": "^1.9.0"
}
}
================================================
FILE: documentation/website/pages/en/help.js
================================================
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;
function Help(props) {
const { config: siteConfig, language = '' } = props;
const { baseUrl, docsUrl } = siteConfig;
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
const langPart = `${language ? `${language}/` : ''}`;
const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`;
const supportLinks = [
{
content: `Learn more using the [documentation on this site.](${docUrl(
'doc1.html',
)})`,
title: 'Browse Docs',
},
{
content: 'Ask questions about the documentation and project',
title: 'Join the community',
},
{
content: "Find out what's new with this project",
title: 'Stay up to date',
},
];
return (
This project is maintained by a dedicated group of people.
);
}
module.exports = Help;
================================================
FILE: documentation/website/pages/en/index.js
================================================
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const CompLibrary = require('../../core/CompLibrary.js');
const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;
class HomeSplash extends React.Component {
render() {
const { siteConfig, language = '' } = this.props;
const { baseUrl, docsUrl } = siteConfig;
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
const langPart = `${language ? `${language}/` : ''}`;
const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`;
const SplashContainer = props => (
);
const ProjectTitle = () => (
{siteConfig.headerTitle}
{siteConfig.tagline}
);
const PromoSection = props => (
);
const Button = props => (
);
return (
);
}
}
class Index extends React.Component {
render() {
const { config: siteConfig, language = '' } = this.props;
const { baseUrl } = siteConfig;
const Block = props => (
);
const FeatureCallout = () => (
Features
);
const TryOut = () => (
{[
{
content:
'Jest is a testing tool from Facebook that makes it easy to perform unit testing in JavaScript.',
image: `${baseUrl}img/testing.png`,
imageAlign: 'left',
title: 'Jest',
},
]}
);
const LearnHow = () => (
{[
{
content:
'Pankod boilerplate is shipped with a CLI tool to streamline the creation of new components.',
image: `${baseUrl}img/cli.gif`,
imageAlign: 'right',
title: 'Built-in Project CLI',
},
]}
);
const LastFeature = () => (
{[
{
content: 'Provides easy communication contract between services.',
image: `${baseUrl}img/service-helpers.png`,
imageAlign: 'right',
title: 'Service Helpers'
},
]}
);
const Features = () => (
{[
{
content: 'Progressive microservices framework for Node.js.',
image: `${baseUrl}img/moleculer-logo2.png`,
imageAlign: 'top',
title: 'Moleculer',
},
{
content: 'Superset of JavaScript which primarily provides optional static typing, classes and interfaces.',
image: `${baseUrl}img/typescript-logo.png`,
imageAlign: 'top',
title: 'TypeScript',
},
{
content: 'TypeORM is specifically an ORM that converts data between JavaScript / TypeScript to a variety of databases',
image: `${baseUrl}img/typeorm.png`,
imageAlign: 'top',
title: 'TypeORM',
className: 'orm'
},
{
content: 'Create services, models and interfaces with one command by using built-in cli.',
image: `${baseUrl}img/cli-logo.png`,
imageAlign: 'top',
title: 'Project CLI',
}
]}
{[
{
content: 'Jest is a testing tool from Facebook that makes it easy to perform unit testing in JavaScript.',
image: `${baseUrl}img/jest-logo.png`,
imageAlign: 'top',
title: 'Jest',
},
{
content: 'Create, deploy, and run applications by using docker containers.',
image: `${baseUrl}img/docker2.png`,
imageAlign: 'top',
title: 'Docker',
},
{
content: 'Tools that helps developers design, build, document, and consume RESTful Web services.',
image: `${baseUrl}img/swagger.png`,
imageAlign: 'top',
title: 'Swagger',
},
{
content: 'Linter for the JavaScript programming language.',
image: `${baseUrl}img/eslint-logo.png`,
imageAlign: 'top',
title: 'Eslint',
}
]}
);
return (
);
}
}
module.exports = Index;
================================================
FILE: documentation/website/pages/en/users.js
================================================
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container;
class Users extends React.Component {
render() {
const { config: siteConfig } = this.props;
if ((siteConfig.users || []).length === 0) {
return null;
}
const editUrl = `${siteConfig.repoUrl}/edit/master/website/siteConfig.js`;
const showcase = siteConfig.users.map(user => (
));
return (
Who is Using This?
This project is used by many folks
{showcase}
Are you using this project?
Add your company
);
}
}
module.exports = Users;
================================================
FILE: documentation/website/sidebars.json
================================================
{
"docs": {
"Introduction": [
"getting-started",
"features"
],
"Overview": [
"setup",
"example-app",
"structure",
"deployment"
],
"Features": [
"testing",
"swagger",
"eslint",
"typeorm"
],
"Project CLI": [
"cli-overview",
"cli-usage",
"cli-migration-guide"
]
}
}
================================================
FILE: documentation/website/siteConfig.js
================================================
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// See https://docusaurus.io/docs/site-config for all the possible
// site configuration options.
// List of projects/orgs using your project for the users page.
const users = [
{
caption: 'User1',
// You will need to prepend the image path with your baseUrl
// if it is not '/', like: '/test-site/img/image.jpg'.
image: '/img/undraw_open_source.svg',
infoLink: 'https://www.facebook.com',
pinned: true,
},
];
const organizationName = 'pankod';
const projectName = 'moleculerjs-boilerplate';
const packageName = 'moleculerjs-boilerplate';
const url = [
{
title: 'Twitter',
url: 'https://twitter.com/PankodDev',
},
];
const siteConfig = {
title: 'Pankod', // Title for your website.
headerTitle: 'moleculerjs-boilerplate',
tagline:
'Moleculer JS Microservice Boilerplate with Typescript, TypeORM, CLI, Service Helpers, Swagger, Jest, Docker, Eslint support and everything you will ever need to deploy rock solid projects.',
url: 'https://pankod.github.io', // Your website URL
baseUrl: '/moleculerjs-boilerplate/', // Base URL for your project */
// For github.io type URLs, you would set the url and baseUrl like:
// url: 'https://facebook.github.io',
// baseUrl: '/test-site/',
socialMediaUrl: url,
packageName: packageName,
// Used for publishing and more
projectName: projectName,
organizationName: organizationName,
// For top-level user or org sites, the organization is still the same.
// e.g., for the https://JoelMarcey.github.io site, it would be set like...
// organizationName: 'JoelMarcey'
// For no header links in the top nav bar -> headerLinks: [],
headerLinks: [
{ doc: 'getting-started', label: 'Docs' },
/* { page: 'users', label: 'Help' }, */
{ href: 'https://github.com/pankod/' + packageName, label: 'Github' },
{ search: true },
{ languages: true },
],
// If you have users set above, you add it here:
users,
/* path to images for header/footer */
headerIcon: 'img/footer_icon.png',
footerIcon: 'img/footer_icon.png',
favicon: 'img/favicon.png',
/* Colors for website */
colors: {
primaryColor: '#2C3E50',
secondaryColor: '#83878D',
},
/* Custom fonts for website */
/*
fonts: {
myFont: [
"Times New Roman",
"Serif"
],
myOtherFont: [
"-apple-system",
"system-ui"
]
},
*/
// This copyright info is used in /core/Footer.js and blog RSS/Atom feeds.
copyright: `Copyright © ${new Date().getFullYear()} ${organizationName}`,
highlight: {
// Highlight.js theme to use for syntax highlighting in code blocks.
theme: 'tomorrow-night',
},
// Add custom scripts here that would be placed in