Skip to content

Commit

Permalink
Adding docker (dev and prod)
Browse files Browse the repository at this point in the history
  • Loading branch information
pablanco committed Nov 27, 2020
1 parent c2cc223 commit e760e3e
Show file tree
Hide file tree
Showing 18 changed files with 1,002 additions and 948 deletions.
13 changes: 13 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Git and VSCode
.git
.gitignore
/.vscode

# Docker
Dockerfile.*
docker-compose.yml

# NPM dependencies
node_modules
npm-debug.log
package-lock.json
3 changes: 1 addition & 2 deletions .env.ci
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
PORT = 3001
JWT_SECRET = some-secret-string
DOCS_ENABLED=false
TYPEORM_CONNECTION = postgres
TYPEORM_HOST = localhost
TYPEORM_USERNAME = postgres
TYPEORM_DATABASE = postgres
TYPEORM_PORT = 5432
TYPEORM_SYNCHRONIZE = true
TYPEORM_LOGGING = false
TYPEORM_ENTITIES = src/entities/**/*.ts
TYPEORM_MIGRATIONS = src/database/migrations/**/*.ts
TYPEORM_MIGRATIONS_RUN = false
TYPEORM_SEEDING_FACTORIES = src/database/factories/**/*.ts
TYPEORM_SEEDING_SEEDS = src/database/seeds/**/*.ts
Expand Down
3 changes: 1 addition & 2 deletions .example.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
PORT = 3000
JWT_SECRET = some-secret-string
DOCS_ENABLED=true
TYPEORM_CONNECTION = postgres
TYPEORM_HOST = localhost
TYPEORM_USERNAME =
Expand All @@ -8,8 +9,6 @@ TYPEORM_DATABASE = express-api-base-development
TYPEORM_PORT = 5432
TYPEORM_SYNCHRONIZE = false
TYPEORM_LOGGING = true
TYPEORM_ENTITIES = src/entities/**/*.ts
TYPEORM_MIGRATIONS = src/database/migrations/**/*.ts
TYPEORM_MIGRATIONS_RUN = true
TYPEORM_SEEDING_FACTORIES = src/database/factories/**/*.ts
TYPEORM_SEEDING_SEEDS = src/database/seeds/**/*.ts
Expand Down
3 changes: 1 addition & 2 deletions .example.env.test
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
PORT = 3001
JWT_SECRET = some-secret-string
DOCS_ENABLED=true
TYPEORM_CONNECTION = postgres
TYPEORM_HOST = localhost
TYPEORM_USERNAME =
Expand All @@ -8,8 +9,6 @@ TYPEORM_DATABASE = express-api-base-test
TYPEORM_PORT = 5432
TYPEORM_SYNCHRONIZE = true
TYPEORM_LOGGING = false
TYPEORM_ENTITIES = src/entities/**/*.ts
TYPEORM_MIGRATIONS = src/database/migrations/**/*.ts
TYPEORM_MIGRATIONS_RUN = true
TYPEORM_SEEDING_FACTORIES = src/database/factories/**/*.ts
TYPEORM_SEEDING_SEEDS = src/database/seeds/**/*.ts
Expand Down
10 changes: 10 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Check out https://hub.docker.com/_/node to select a new base image
FROM node:12.19.0-alpine
WORKDIR /app
COPY ./package.json .
COPY tsconfig* ./
COPY ormconfig* ./
COPY src ./src
RUN yarn
EXPOSE 3000
CMD [ "yarn", "dev" ]
18 changes: 18 additions & 0 deletions Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Check out https://hub.docker.com/_/node to select a new base image
FROM node:12.19.0-alpine AS builder
WORKDIR /app
COPY . .
RUN yarn
RUN yarn build

FROM node:12.19.0-alpine AS production
WORKDIR /app
COPY --from=builder ./app/dist ./dist
COPY package* ./
COPY tsconfig* ./
COPY prod-paths* ./
COPY ormconfig* ./
RUN yarn install --production
RUN rm -rf dist/__test__
EXPOSE 3000
CMD [ "yarn", "prod" ]
50 changes: 42 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# express-api-base
# Node-TS-Api-Base

This project includes the boilerplate for a basic rest-api made in Node.JS with Express + Typescript.

Expand Down Expand Up @@ -43,6 +43,45 @@ Run `yarn migration:revert` to rollback migrations.
Run `yarn migration:show` to see the list of all migrations (pending and also ran).


## Running with Docker

### Prerequisites
In order to run the app with Docker, you should install or update to the latest version, we recommend to install [Docker-Desktop](https://docs.docker.com/get-docker/) due to composer and some cool CLI-UI tools are included.

### Development with Docker

The following commands will build and run all you need to start working on the base, without any other installation requirements. Important: if you already have postgres running locally, you'll need to kill the service before run `docker-compose up`.

```
docker-compose --env-file .env.dev build
```

```
docker-compose --env-file .env.dev up
```

### Deployment with Docker (only for production)

The following commands will `build and run` a Docker image ready for production and size-optimized.

#### Build Docker image

```
docker build -f Dockerfile.prod -t node-ts-api-base .
```

#### Run docker image (you need to add .env file as param)

```
docker run --rm --env-file=.env.prod -p 3000:3000 --name node-api node-ts-api-base
```


## API Documentation

After running the server you will find OpenAPI Specification here: `http://<host>:<port>/docs`


## App scaffolding

This is the suggested scaffolding for this project. You can take a look at:
Expand All @@ -63,12 +102,14 @@ This is the suggested scaffolding for this project. You can take a look at:
│ ├── services
│ │ └── ...
│ ├── app.ts (App root and is where the application will be configured.)
│ ├── server.ts (HTTP server)
├── README.md
├── .nvmrc (Locks down your Node version.)
├── jest.config.js (Jest configuration file.)
├── yarn-lock.json
├── package.json (Your application’s package manifest.)
├── tsconfig.json (The TypeScript project configuration.)
├── prod-path.js (Tool used to run in production, translates ts-path and alias)
```

## Dependencies
Expand All @@ -84,13 +125,6 @@ This is the suggested scaffolding for this project. You can take a look at:
- [express-rate-limit](https://github.com/nfriedly/express-rate-limit) - Basic rate-limiting middleware used to limit repeated requests to public APIs
- [morgan](https://github.com/expressjs/morgan) - HTTP request logger middleware for node.js

## API Documentation

After running the server you will find OpenAPI Specification here: `http://<host>:<port>/docs`

## Deployment with docker

Work in progress ...

## Code Quality

Expand Down
44 changes: 44 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
version: '3'
services:
postgres:
image: postgres:latest
ports:
- '${TYPEORM_PORT}:${TYPEORM_PORT}'
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: "${TYPEORM_DATABASE}"
node-api-base:
build:
context: ./
dockerfile: Dockerfile.dev
environment:
PORT: "${PORT}"
JWT_SECRET: "${JWT_SECRET}"
TYPEORM_CONNECTION: postgres
TYPEORM_HOST: postgres
TYPEORM_USERNAME: postgres
TYPEORM_PASSWORD: postgres
TYPEORM_DATABASE: "${TYPEORM_DATABASE}"
TYPEORM_PORT: "${TYPEORM_PORT}"
TYPEORM_SYNCHRONIZE: "true"
TYPEORM_LOGGING: "true"
TYPEORM_MIGRATIONS_RUN: "${TYPEORM_MIGRATIONS_RUN}"
TYPEORM_SEEDING_FACTORIES: "${TYPEORM_SEEDING_FACTORIES}"
TYPEORM_SEEDING_SEEDS: "${TYPEORM_SEEDING_SEEDS}"
ACCESS_TOKEN_LIFE: "${ACCESS_TOKEN_LIFE}"
RATE_LIMIT_WINDOW: "${RATE_LIMIT_WINDOW}"
RATE_LIMIT_MAX_REQUESTS: "${RATE_LIMIT_MAX_REQUESTS}"
S3_ID: "${S3_ID}"
S3_SECRET: "${S3_SECRET}"
S3_BUCKETNAME: "${S3_BUCKETNAME}"
ports:
- '${PORT}:${PORT}'
restart: on-failure
container_name: node-api-base
depends_on:
- postgres
links:
- postgres
volumes:
- "./src:/app/src"
- /src/node_modules
20 changes: 16 additions & 4 deletions ormconfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,23 @@ module.exports = {
port: Number.parseInt(process.env.TYPEORM_PORT || '3000'),
synchronize: process.env.TYPEORM_SYNCHRONIZE === 'true',
logging: process.env.TYPEORM_LOGGING,
entities: ['src/entities/**/*.ts'],
migrations: ['src/database/migrations/**/*.ts'],
entities: [
process.env.ENVIRONMENT === 'prod'
? 'dist/src/entities/**/*.js'
: 'src/entities/**/*.ts'
],
migrations: [
process.env.ENVIRONMENT === 'prod'
? 'dist/src/database/migrations/**/*.js'
: 'src/database/migrations/**/*.ts'
],
migrationsRun: process.env.TYPEORM_MIGRATIONS_RUN === 'true',
cli: {
migrationsDir: 'src/database/migrations',
entitiesDir: 'src/entities'
migrationsDir:
process.env.ENVIRONMENT === 'prod'
? 'dist/src/database/migrations'
: 'src/database/migrations',
entitiesDir:
process.env.ENVIRONMENT === 'prod' ? 'dist/src/entities' : 'src/entities'
}
};
21 changes: 11 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "express-api-base",
"name": "node-ts-api-base",
"version": "1.0.0",
"description": "Rest api base with nodejs + express",
"license": "MIT",
"scripts": {
"dev": "yarn && nodemon --watch 'src/**/*.ts' --exec 'ts-node -r tsconfig-paths/register' src/server.ts",
"dev": "yarn && nodemon --watch 'src/**/*.ts' --exec 'ENVIRONMENT=dev ts-node -r tsconfig-paths/register' src/server.ts",
"prebuild": "rm -rf /dist",
"build": "tsc",
"start": "node -r tsconfig-paths/register -r ts-node/register ./dist/index.js",
"build": "tsc && cp -R src/admin dist/src && cp ./ormconfig.js dist/",
"start": "ENVIRONMENT=dev node -r tsconfig-paths/register -r ts-node/register ./dist/src/server.js",
"prod": "ENVIRONMENT=prod node -r ./prod-paths.js ./dist/src/server.js",
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js",
"test": "ENVIRONMENT=test jest --runInBand --useStderr",
"test:cover": "yarn test --coverage",
Expand Down Expand Up @@ -45,17 +46,16 @@
"@types/node": "^14.14.10",
"@types/supertest": "^2.0.10",
"@types/swagger-ui-express": "^4.1.2",
"dotenv": "^8.2.0",
"eslint": "^7.14.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.1.4",
"jest": "^26.6.3",
"jest-sonar-reporter": "^2.0.0",
"nodemon": "^2.0.6",
"prettier": "^2.2.0",
"supertest": "^6.0.1",
"ts-node": "^9.0.0",
"tsconfig-paths": "^3.9.0",
"typeorm-seeding": "^1.6.1",
"typescript": "^4.1.2"
},
"dependencies": {
Expand All @@ -72,22 +72,23 @@
"class-validator": "^0.12.2",
"class-validator-jsonschema": "^2.0.3",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"express-formidable": "^1.2.0",
"express-rate-limit": "^5.2.3",
"helmet": "^4.2.0",
"jest": "^26.6.3",
"jsonwebtoken": "^8.5.1",
"morgan": "^1.10.0",
"multer": "^1.4.2",
"pg": "^8.5.1",
"reflect-metadata": "^0.1.13",
"routing-controllers": "^0.9.0-alpha.6",
"routing-controllers-openapi": "^2.1.0",
"supertest": "^6.0.1",
"swagger-ui-express": "^4.1.5",
"tsconfig-paths": "^3.9.0",
"ts-jest": "^26.4.4",
"typedi": "^0.8.0",
"typeorm": "^0.2.29"
"typeorm": "^0.2.29",
"typeorm-seeding": "^1.6.1"
}
}
13 changes: 13 additions & 0 deletions prod-paths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable guard-for-in */
/* eslint-disable no-undef */
const tsConfig = require('./tsconfig.json');
const tsConfigPaths = require('tsconfig-paths');

const { baseUrl, paths } = tsConfig.compilerOptions;
for (const path in paths) {
paths[path][0] = paths[path][0]
.replace('src', 'dist/src')
.replace('.ts', '.js');
}

tsConfigPaths.register({ baseUrl, paths });
15 changes: 8 additions & 7 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import {
} from 'routing-controllers';
import helmet from 'helmet';
import { Container } from 'typedi';
import Auth from '@middlewares/auth.middleware';
import { AuthMiddleware, LoggingMiddleware } from '@middlewares';
import { controllers } from '@controllers';
import rateLimit from 'express-rate-limit';
import { swaggerSpec } from './swagger';
import {
RATE_LIMIT_MAX_REQUESTS,
RATE_LIMIT_WINDOW,
ENVIRONMENT
DOCS_ENABLED
} from '@config';

// required by routing-controllers
Expand Down Expand Up @@ -52,17 +53,17 @@ app.use(
const routingControllersOptions: any = {
routePrefix: '/api/v1',
cors: true,
authorizationChecker: Auth.checker,
controllers: [`${__dirname}/controllers/*.ts`],
middlewares: [`${__dirname}/middlewares/*.ts`],
interceptors: [`${__dirname}/interceptors/*.ts`]
authorizationChecker: AuthMiddleware.checker,
controllers,
middlewares: [AuthMiddleware, LoggingMiddleware],
interceptors: []
};

// Wrap server with routing-controllers
useExpressServer(app, routingControllersOptions);

// Setup Swagger
if (ENVIRONMENT !== 'prod') {
if (DOCS_ENABLED === 'true') {
swaggerSpec(getMetadataArgsStorage, routingControllersOptions, app);
}

Expand Down
3 changes: 2 additions & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export const {
RATE_LIMIT_MAX_REQUESTS,
S3_ID,
S3_SECRET,
S3_BUCKETNAME
S3_BUCKETNAME,
DOCS_ENABLED
} = process.env;
4 changes: 4 additions & 0 deletions src/controllers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { AuthController } from './auth.controller';
import { UserController } from './users.controller';

export const controllers = [AuthController, UserController];
2 changes: 1 addition & 1 deletion src/middlewares/auth.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Action } from 'routing-controllers';
import { verifyJWT } from '@services/jwt.service';

export default class {
export class AuthMiddleware {
static async checker(action: Action): Promise<boolean> {
let token = action.request.headers['authorization'];
if (!token) {
Expand Down
2 changes: 2 additions & 0 deletions src/middlewares/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './auth.middleware';
export * from './logging.middleware';
Loading

0 comments on commit e760e3e

Please sign in to comment.