/rest-example

fork from https://github.com/Shyam-Chen/Express-Play

Primary LanguageJavaScriptMIT LicenseMIT

Express Play

🚚 A boilerplate for Node.js, Express, Mongoose, Heroku, mLab, Nodemon, PM2, and Babel.

Build Status Coverage Status // Dependency Status devDependency Status

🌈 Live Demo

This seed repository provides the following features:

  • ---------- Essentials ----------
  • Application routing with Express.
  • Data query language with GraphQL.
  • Universal RPC with gRPC.
  • Object document mapping with Mongoose.
  • Object relational mapping with Sequelize.
  • GraphQL SDL to model with Prisma.
  • Utility functions with Lodash.
  • Reactive extensions with ReactiveX.
  • Authenticate requests with Passport.
  • Real-time bidirectional communication with Socket.IO.
  • In-memory data structure store with Redis.
  • Multi-protocol messaging broker with RabbitMQ.
  • Online payments with Stripe.
  • Machine learning with TensorFlow.
  • OpenAPI specification with Swagger.
  • ---------- Tools ----------
  • Next generation JavaScript with Babel.
  • JavaScript static code analyzer with ESLint.
  • Unit testing with Jest.
  • End-to-End testing with Supertest.
  • Test coverage integration with Codecov.
  • Automatically restart application with Nodemon.
  • Keeping application alive with PM2.
  • ---------- Environments ----------
  • JavaScript runtime with Node.js.
  • Fast and deterministic builds with Yarn.
  • Version control with Git.
  • Code repository with GitHub.
  • Cloud application hosting with Now.sh.
  • Cloud NoSQL database hosting with mLab.
  • Cloud SQL database hosting with ElephantSQL.
  • Cloud memory cache hosting with RedisLabs.
  • Cloud storageβ€Ž hosting with Cloudinary.
  • Cloud message queue hosting with CloudAMQP.
  • Log management service with LogDNA.
  • Error tracking service with Sentry.
  • Performance and security with Cloudflare.
  • Software container with Docker.
  • Continuous integration with CircleCI.

Thinking in REST Stack / GraphQL Stack / Microservices.

Table of Contents

Getting Started

Follow steps to execute this boilerplate.

  1. Clone this boilerplate
$ git clone --depth 1 https://github.com/Shyam-Chen/Express-Play.git <PROJECT_NAME>
$ cd <PROJECT_NAME>
  1. Install dependencies
$ yarn install
  1. Start a local server
$ yarn start
  1. Compile code
$ yarn build
  1. Check code quality
$ yarn lint
  1. Runs unit tests
$ yarn unit
  1. Runs end-to-end tests
$ yarn e2e

Dockerization

Dockerize an application.

  1. Build and run the container in the background
$ docker-compose up -d app
  1. Run a command in a running container
$ docker-compose exec app <COMMAND>
  1. Remove the old container before creating the new one
$ docker-compose rm -fs
  1. Restart up the container in the background
$ docker-compose up -d --build app
  1. Push images to Docker Cloud
# .gitignore

  .DS_Store
  node_modules
  dist
  coverage
+ dev.Dockerfile
+ stage.Dockerfile
+ prod.Dockerfile
  *.log
$ docker login
$ docker build -f ./tools/<dev|stage|prod>.Dockerfile -t <IMAGE_NAME>:<IMAGE_TAG> .

# checkout
$ docker images

$ docker tag <IMAGE_NAME>:<IMAGE_TAG> <DOCKER_ID_USER>/<IMAGE_NAME>:<IMAGE_TAG>
$ docker push <DOCKER_ID_USER>/<IMAGE_NAME>:<IMAGE_TAG>

# remove
$ docker rmi <REPOSITORY>:<TAG>
# or
$ docker rmi <IMAGE_ID>
  1. Pull images from Docker Cloud
# circle.yml

  echo "${HEROKU_TOKEN}" | docker login -u "${HEROKU_USERNAME}" --password-stdin registry.heroku.com
- docker build -f ./tools/$DEPLOYMENT_ENVIRONMENT.Dockerfile -t $APP_NAME .
+ docker pull <DOCKER_ID_USER>/<IMAGE_NAME>:<IMAGE_TAG>
- docker tag $APP_NAME registry.heroku.com/$APP_NAME/web
+ docker tag <IMAGE_NAME>:<IMAGE_TAG> registry.heroku.com/<HEROKU_PROJECT>/web
  docker push registry.heroku.com/<HEROKU_PROJECT>/web

Configuration

Default environments

Set your local environment variables.

// src/env.js

export const NODE_ENV = process.env.NODE_ENV || 'development';

export const HOST = process.env.HOST || '0.0.0.0';
export const PORT = process.env.PORT || 3000;

export const SECRET = process.env.SECRET || 'PUT_YOUR_SECRET_HERE';

export const MONGODB_URI = process.env.MONGODB_URI || '<PUT_YOUR_MONGODB_URI_HERE>';
export const POSTGRES_URL = process.env.POSTGRES_URL || 'PUT_YOUR_POSTGRES_URL_HERE';

export const REDIS_PORT = process.env.REDIS_PORT || '<PUT_YOUR_REDIS_PORT_HERE>';
export const REDIS_HOST = process.env.REDIS_HOST || '<PUT_YOUR_REDIS_HOST_HERE>';

// ...

Deployment environments

Set your deployment environment variables.

# tools/<dev|stage|prod>.Dockerfile

# envs --
ENV SECRET <PUT_YOUR_SECRET_HERE>

ENV MONGODB_URI <PUT_YOUR_MONGODB_URI>
ENV POSTGRES_URL <PUT_YOUR_POSTGRES_URL_HERE>

ENV REDIS_PORT <PUT_YOUR_REDIS_PORT_HERE>
ENV REDIS_HOST <PUT_YOUR_REDIS_HOST_HERE>

# ...
# -- envs

Using Libraries

  1. Example of REST
import { Router } from 'express';

import { List } from './document';

const router = Router();

router.get('/', async (req, res) => {
  const data = await List.find({}).exec();
  res.json(data);
});

export default router;
  1. Example of GraphQL
import gql from 'graphql-tag';

import { List } from './document';

export const listTypeDefs = gql`
  type List {
    _id: ID!
    text: String!
  }

  type Query {
    list: [List]
  }
`;

export const listResolvers = {
  Query: {
    async list(root, { _id, text }) {
      const data = await List.find({}).exec();
      return data;
    },
  },
};
  1. Example of Document
import mongoose, { Schema } from 'mongoose';

const listSchema = new Schema({
  text: {
    type: String,
    required: true,
  },
});

export const List = mongoose.model('List', listSchema);
  1. Example of Relational
import Sequelize from 'sequelize';

import sequelize from '~/core/sequelize';

export const RelationalList = sequelize.define('List', {
  text: Sequelize.STRING,
});
  1. Example of Lodash
import { of } from 'rxjs';
import { lowerFirst, pad } from 'lodash';

of(lowerFirst('Hello'), pad('World', 5))
  .subscribe(value => console.log(value));
  // hello
  // World
  1. Example of ReactiveX
import { timer, of } from 'rxjs';
import { mapTo, combineAll } from 'rxjs/operators';

timer(2000)
  .pipe(
    mapTo(of('Hello', 'World')),
    combineAll(),
  )
  .subscribe(value => console.log(value));
  // ["Hello"]
  // ["World"]
  1. Example of Socket
connSocket.emit('A', { foo: 'bar' });
connSocket.on('B', data => console.log(data));  // { foo: 'baz' }
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script>
const socket = io('http://localhost:3000/');

socket.on('connect', () => console.log('Socket: Accept a connection.'));

socket.on('A', (data) => {
  console.log(data);  // { foo: 'bar' }
  socket.emit('B', { foo: 'baz' });
});
</script>
  1. Example of Redis
import redis from '~/core/redis';

redis.hmset('thing', {
  foo: 'js',
  bar: 'html',
  baz: 'css',
});

redis.hgetall('thing', (err, object) => {
  console.log(object);
});

Directory Structure

The structure follows the LIFT Guidelines.

.
β”œβ”€β”€ src
β”‚   β”œβ”€β”€ core  -> core feature module
β”‚   β”œβ”€β”€ <FEATURE>  -> feature modules
β”‚   β”‚   β”œβ”€β”€ __tests__
β”‚   β”‚   β”‚   β”œβ”€β”€ <FEATURE>.e2e-spec.js
β”‚   β”‚   β”‚   └── <FEATURE>.spec.js
β”‚   β”‚   β”œβ”€β”€ _<THING>  -> feature of private things
β”‚   β”‚   β”‚   └── ...
β”‚   β”‚   └── <FEATURE>.js
β”‚   β”œβ”€β”€ <GROUP>  -> module group
β”‚   β”‚   └── <FEATURE>  -> feature modules
β”‚   β”‚       β”œβ”€β”€ __tests__
β”‚   β”‚       β”‚   β”œβ”€β”€ <FEATURE>.e2e-spec.js
β”‚   β”‚       β”‚   └── <FEATURE>.spec.js
β”‚   β”‚       β”œβ”€β”€ _<THING>  -> feature of private things
β”‚   β”‚       β”‚   └── ...
β”‚   β”‚       └── <FEATURE>.js
β”‚   β”œβ”€β”€ shared  -> shared feature module
β”‚   β”œβ”€β”€ app.js
β”‚   └── env.js
β”œβ”€β”€ tools
β”‚   └── ...
β”œβ”€β”€ .babelrc
β”œβ”€β”€ .editorconfig
β”œβ”€β”€ .eslintrc
β”œβ”€β”€ .gitignore
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ LICENSE
β”œβ”€β”€ README.md
β”œβ”€β”€ circle.yml
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ jest.config.js
β”œβ”€β”€ package.json
β”œβ”€β”€ processes.js
└── yarn.lock