├── .editorconfig ├── .gitignore ├── .npmrc ├── .vscode └── settings.json ├── README.md ├── package.json ├── src └── server │ ├── config.ts │ ├── logging.ts │ ├── routes.ts │ └── server.ts ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | indent_style = space 4 | indent_size = 4 5 | insert_final_newline = true 6 | 7 | [package.json] 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "./node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KoaJS & Typescript Starter Project 2 | 3 | Requirements: 4 | * Node v8 5 | 6 | Features: 7 | * Watch mode - server auto-restarts when code changes 8 | * Koa v2 with #beautiful async functions 9 | 10 | Included middleware: 11 | * koa-router 12 | * Custom logger with pretty + JSON modes (based on `NODE_ENV`) 13 | 14 | ## Getting Started 15 | 16 | ``` 17 | npm i 18 | npm run watch-server 19 | ``` 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-typescript-starter", 3 | "version": "1.0.0", 4 | "main": "dist/index.js", 5 | "license": "CC0-1.0", 6 | "scripts": { 7 | "build-server": "tslint --project . && tsc", 8 | "watch-server": "cross-env NODE_ENV=development nodemon --watch src/**/* -e ts,tsx --exec ts-node src/server/server.ts" 9 | }, 10 | "devDependencies": { 11 | "@types/koa": "2.0.39", 12 | "@types/koa-router": "7.0.23", 13 | "cross-env": "5.0.5", 14 | "nodemon": "1.12.0", 15 | "ts-node": "3.3.0", 16 | "tslint": "5.7.0", 17 | "typescript": "2.5.2" 18 | }, 19 | "dependencies": { 20 | "koa": "2.3.0", 21 | "koa-router": "7.2.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/server/config.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface IConfig { 3 | port: number; 4 | prettyLog: boolean; 5 | } 6 | 7 | const config = { 8 | port: process.env.NODE_PORT || 3000, 9 | prettyLog: process.env.NODE_ENV == 'development', 10 | }; 11 | 12 | export { config }; 13 | -------------------------------------------------------------------------------- /src/server/logging.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as Koa from 'koa'; 3 | import { config } from './config'; 4 | 5 | interface ILogData { 6 | method: string; 7 | url: string; 8 | query: string; 9 | remoteAddress: string; 10 | host: string; 11 | userAgent: string; 12 | statusCode: number; 13 | errorMessage: string; 14 | errorStack: string; 15 | data: any; 16 | responseTime: number; 17 | } 18 | 19 | function outputLog(data: Partial, thrownError: any) { 20 | if (config.prettyLog) { 21 | console.log(`${data.statusCode} ${data.method} ${data.url} - ${data.responseTime}ms`); 22 | if (thrownError) { 23 | console.error(thrownError); 24 | } 25 | } 26 | else if (data.statusCode < 400) { 27 | process.stdout.write(JSON.stringify(data) + '\n'); 28 | } 29 | else { 30 | process.stderr.write(JSON.stringify(data) + '\n'); 31 | } 32 | } 33 | 34 | export async function logger(ctx: Koa.Context, next: () => Promise) { 35 | 36 | const start = new Date().getMilliseconds(); 37 | 38 | const logData: Partial = { 39 | method: ctx.method, 40 | url: ctx.url, 41 | query: ctx.query, 42 | remoteAddress: ctx.request.ip, 43 | host: ctx.headers['host'], 44 | userAgent: ctx.headers['user-agent'], 45 | }; 46 | 47 | let errorThrown: any = null; 48 | try { 49 | await next(); 50 | logData.statusCode = ctx.status; 51 | } 52 | catch (e) { 53 | errorThrown = e; 54 | logData.errorMessage = e.message; 55 | logData.errorStack = e.stack; 56 | logData.statusCode = e.status || 500; 57 | if (e.data) { 58 | logData.data = e.data; 59 | } 60 | } 61 | 62 | logData.responseTime = new Date().getMilliseconds() - start; 63 | outputLog(logData, errorThrown); 64 | 65 | if (errorThrown) { 66 | throw errorThrown; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/server/routes.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as Router from 'koa-router'; 3 | 4 | const router = new Router(); 5 | 6 | router.get('/', async (ctx) => { 7 | ctx.body = 'Hello World!'; 8 | }); 9 | 10 | router.get('/test', async (ctx) => { 11 | ctx.status = 201; 12 | ctx.body = 'test'; 13 | }); 14 | 15 | export const routes = router.routes(); 16 | -------------------------------------------------------------------------------- /src/server/server.ts: -------------------------------------------------------------------------------- 1 | import * as Koa from 'koa'; 2 | 3 | import { config } from './config'; 4 | import { logger } from './logging'; 5 | import { routes } from './routes'; 6 | 7 | const app = new Koa(); 8 | 9 | app.use(logger); 10 | app.use(routes); 11 | 12 | app.listen(config.port); 13 | 14 | console.log(`Server running on port ${config.port}`); 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2017", 5 | "noImplicitAny": true, 6 | "outDir": "./dist", 7 | "sourceMap": true 8 | }, 9 | "include": [ 10 | "src/**/*" 11 | ] 12 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "quotemark": [ 9 | true, 10 | "single" 11 | ], 12 | "no-console": [ 13 | false 14 | ], 15 | "one-line": [ 16 | true, 17 | "check-open-brace", 18 | "check-whitespace" 19 | ], 20 | "object-literal-sort-keys": [ 21 | false 22 | ], 23 | "no-string-literal": false, 24 | "triple-equals": [ 25 | false 26 | ] 27 | }, 28 | "rulesDirectory": [] 29 | } 30 | --------------------------------------------------------------------------------