Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7b8f0ea
Configure mysql client and remove postgres as a deps
RobusGauli Apr 8, 2020
7d22fbb
Add errors instance for token and network
RobusGauli Apr 8, 2020
cac594c
Integrat LMS authentication
RobusGauli Apr 8, 2020
d6febc9
Continuation of user session for req-resp roundtrip
RobusGauli Apr 8, 2020
eb71aa6
Add JSDOC for model class
RobusGauli Apr 9, 2020
3f5c4d0
Remove docker-compose file with postgres deps
RobusGauli Apr 9, 2020
49d37e4
Rename package json name
RobusGauli Apr 9, 2020
3d80d77
Remove docker-compose instruction from readme
RobusGauli Apr 9, 2020
a1ce2e0
fix lint issues
RobusGauli Apr 9, 2020
ec3668c
Use async/await keyword instead of 'then'
RobusGauli Apr 9, 2020
ae88dac
Add error formatter in winston
RobusGauli Apr 9, 2020
8f313f0
Change api of model
RobusGauli Apr 9, 2020
6f9b0e8
Remove status code from custom error type
RobusGauli Apr 10, 2020
7ec80a9
Add option to namespace the log
RobusGauli Apr 10, 2020
5a0a2e3
Integrate async-store for requestID injection
RobusGauli Apr 10, 2020
e167c6c
Remove session.js used for continuation-local-storage
RobusGauli Apr 10, 2020
d92508c
Allow pagination for fetch query
RobusGauli Apr 10, 2020
6a46a5d
Remove unused joi validation service
RobusGauli Apr 10, 2020
2566319
Clean up log format
RobusGauli Apr 10, 2020
a459ca8
Add prettier rule for strpping single arg braces
RobusGauli Apr 12, 2020
1b9a6b2
Rename env flag for file transport
RobusGauli Apr 12, 2020
4dc44ec
Remove http error codes from CustomError
RobusGauli Apr 12, 2020
27c155a
Remove useless request interceptor
RobusGauli Apr 14, 2020
abf5472
Fix pr issues
RobusGauli Apr 14, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .env.docker
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# Application
APP_NAME='Express API ES6 Starter'
APP_NAME='Node JS Starter'
APP_VERSION='1.0.0'
APP_PORT='8848'
APP_HOST='0.0.0.0'

# Log
LOGGING_DIR='logs'
LOGGING_LEVEL='debug'
LOG_DIR='logs'
LOG_LEVEL='debug'
LOG_RETENTION_PERIOD=''
ENABLE_FILE_LOG_TRANSPORT='TRUE'

# Database
DB_CLIENT='pg'
DB_CLIENT='mysql'

# App Environment
DB_PORT='5432'
Expand Down
15 changes: 12 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
# Application
APP_NAME='Express API ES6 Starter'
APP_NAME='Node JS Starter'
APP_VERSION='1.0.0'
APP_PORT='8848'
APP_HOST='127.0.0.1'

# Log
LOG_DIR='logs'
LOG_LEVEL='debug'
LOG_RETENTION_PERIOD=''
ENABLE_FILE_LOG_TRANSPORT='TRUE'

# Database
DB_CLIENT='pg'
DB_CLIENT='mysql'


# App Environment
DB_PORT='5432'
DB_PORT='3306'
DB_HOST='localhost'
DB_NAME='express'
DB_USER='username'
DB_PASSWORD='password'


# Authenication parameters
AUTH_URL='http://localhost:5000/'
AUTH_CLIENT_ID='secret-client-id'

# Sentry
# https://docs.sentry.io/quickstart
SENTRY_DSN=''
Expand Down
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
tabWidth: 2
printWidth: 120
singleQuote: true
arrowParens: avoid
11 changes: 5 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,28 @@ node_js:
- lts/dubnium

services:
- postgresql
- mysql

branches:
only:
- master

env: >
NODE_ENV=test
APP_NAME='Express API ES6 Starter'
APP_NAME='Node JS Starter'
APP_VERSION='1.0.0'
TEST_APP_PORT='9945'
TEST_DB_NAME='express_test'
TEST_DB_NAME='app_test'
TEST_DB_PASSWORD=''
TEST_DB_PORT='5432'
TEST_DB_USER='postgres'
TEST_DB_PORT='3306'
TEST_DB_USER='mysql'

before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH="$HOME/.yarn/bin:$PATH"

before_script:
- cp .env.example .env
- psql -c 'create database express_test;' -U postgres
- yarn migrate

script:
Expand Down
22 changes: 0 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,27 +37,6 @@ Example,
$ yarn make:migration create_tags_table
$ yarn make:seeder 02_insert_tags

## Using Docker

### Using docker-compose

Use [docker-compose](https://docs.docker.com/compose/) to quickly bring up a stack with pre-configured Postgres database container. Data is ephemeral and containers will disappear when stack is removed.

Specific configuration for Docker is in `.env.docker`

- `0.0.0.0` as `$APP_HOST` to expose app on Docker network interface
- Pre-configured Postgres settings - can be updated to point to another Postgres host

Bring up stack,

$ docker-compose up

Navigate to http://localhost:8848/api-docs/ to verify application is running from docker.

Bring down stack,

$ docker-compose down

### Multi-stage docker builds

There are multiple build targets available for different stages. These images can be used to deploy or run jobs in different container based cloud infrastructure like Kubernetes, AWS ECS, Fargate, GCP Cloud Run etc.
Expand Down Expand Up @@ -100,4 +79,3 @@ To run the tests you need to create a separate test database. Don't forget to up
Run tests with coverage.

$ yarn test:coverage

41 changes: 0 additions & 41 deletions docker-compose.yml

This file was deleted.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "express-api-es6-starter",
"name": "@leapfrogtechnology/nodejs-starter",
"version": "1.0.0",
"description": "Express API ES6 Starter",
"description": "NodeJS Starter",
"scripts": {
"start": "node dist",
"prestart": "yarn build",
Expand Down Expand Up @@ -47,10 +47,10 @@
"dependencies": {
"@hapi/boom": "^9.1.0",
"@hapi/joi": "^17.1.1",
"@leapfrogtechnology/async-store": "^1.2.0",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to do a PR / issue if there are anything for it.

"@sentry/node": "^5.15.0",
"axios": "^0.19.2",
"body-parser": "^1.19.0",
"bookshelf": "^1.1.0",
"bookshelf-virtuals-plugin": "^0.1.1",
"compression": "^1.7.4",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
Expand All @@ -60,7 +60,7 @@
"knex": "^0.20.12",
"lodash": "^4.17.13",
"morgan": "^1.10.0",
"pg": "^7.18.2",
"mysql": "^2.18.1",
"serve-favicon": "^2.5.0",
"swagger-jsdoc": "^3.5.0",
"swagger-ui-dist": "^3.25.0",
Expand Down
70 changes: 70 additions & 0 deletions src/auth/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as store from '@leapfrogtechnology/async-store';

import { http } from '../utils/http';
import TokenError from '../errors/token';

/**
* Extract token from headers in http request.
*
* @param {Object} headers
* @returns {Object}
*/
function extractTokenFromHeaders(headers = {}) {
const { authorization = '' } = headers;

const [tokenType, token] = authorization.split(' ').filter(Boolean);

if (tokenType !== 'Bearer' || !token) {
return {
ok: false,
Copy link
Member

@mesaugat mesaugat Apr 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some go-langish stuff here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it straight. Either return null to signify empty value or throw an error to state the failure -- nothing in between.

};
}

return {
token,
};
Comment on lines +17 to +25

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why different results in between cases? I think it could only be - token or null OR token or error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, user of the API when we tend to return null, undefined, "" or throws have the tendency to check for the error like so:

if (!token){}

OR

 if (token === ""){}

OR

try{ // statements }catch(err){}

all of which are optional if user choose not to handle the error properly.

Agreed that return signature and method is a bit verbose here, however i think it forces dev to handle error explicitly. Allows dev to treat error as just another value.

Any thoughts? This is not the typical JS standard way of error handling however languages like go, rust follows the Tuple/Enum pattern.

}

/**
* Fetch user from auth server using token.
*
* @param {String} token
* @throws {NetworkError}
* @returns {Promise}
*/
async function fetchUserByToken(token) {
const { data } = await http.get(`${process.env.AUTH_URL}/userinfo`, {
headers: {
accessToken: token,
clientId: process.env.AUTH_CLIENT_ID,
},
});

return data;
}

/**
* Validate token received in header.
*
* @param {Object} req
* @param {Object} res
* @param {Object} next
*/
async function authenticateUser(req, res, next) {
try {
const { ok, token } = extractTokenFromHeaders(req.headers);

if (!ok) {
Comment on lines +55 to +57

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't be more simple than this.

Suggested change
const { ok, token } = extractTokenFromHeaders(req.headers);
if (!ok) {
const token = extractTokenFromHeaders(req.headers);
if (!token) {

throw new TokenError('Invalid token');
}

const user = await fetchUserByToken(token);

store.set(user);
next();
} catch (err) {
next(err);
}
}

export default authenticateUser;
37 changes: 37 additions & 0 deletions src/controllers/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import HttpStatus from 'http-status-codes';

import * as userService from '../services/user';

/**
* Get all users.
*
* @param {Object} req
* @param {Object} res
* @param {Function} next
*/
export async function fetch(req, res, next) {
try {
const data = await userService.fetch();

res.json({ data });
} catch (err) {
next(err);
}
}

/**
* Create a new user.
*
* @param {Object} req
* @param {Object} res
* @param {Function} next
*/
export async function create(req, res, next) {
try {
const data = await userService.create();

res.status(HttpStatus.CREATED).json({ data });
} catch (err) {
next(err);
}
}
73 changes: 0 additions & 73 deletions src/controllers/users.js

This file was deleted.

Loading