Update the KOA development framework with typescript
The strongly typed TypeScript development experience has obvious advantages over JavaScript in terms of maintenance projects, so it is imperative to remodel the scaffolding that is commonly used.
Let’s start with the koA-based node backend scaffolding:
- The construction of project development environment and typescript compilation environment;
- Add typing support for Node, KOA, KOA middleware, and libraries used;
- A feature transformation project for typesript.
Set up project development environment
The development and compilation environment is built based on gulp. The gulp-typescript plug-in is used to compile typescript files. The gulp-Nodemon can monitor changes in file content and automatically compile and restart Node services to improve development efficiency.
npm install -D gulp gulp-nodemon gulp-typescript ts-node typescript
Copy the code
Gulp configuration
Gulpfile. The setting of js
const { src, dest, watch, series, task } = require('gulp');
const del = require('del');
const ts = require('gulp-typescript');
const nodemon = require('gulp-nodemon');
const tsProject = ts.createProject('tsconfig.json');
function clean(cb) {
return del(['dist'], cb);
}
// Output js to dist directory
function toJs() {
return src('src/**/*.ts')
.pipe(tsProject())
.pipe(dest('dist'));
}
// Nodemon monitors ts files
function runNodemon() {
nodemon({
inspect: true.script: 'src/app.ts'.watch: ['src'].ext: 'ts'.env: { NODE_ENV: 'development' },
// tasks: ['build'],
}).on('crash', () = > {console.error('Application has crashed! \n');
});
}
const build = series(clean, toJs);
task('build', build);
exports.build = build;
exports.default = runNodemon;
Copy the code
The typescript configuration
Tsconfig. The setting of json
{"compilerOptions": {"baseUrl": ".", // import relative start path "outDir": "./dist", // build output directory "module": "commonjs", "target": "Esnext", / / the node environment support esnext "allowSyntheticDefaultImports" : true, "importHelpers" : true, "strict" : false, "moduleResolution": "node", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "noImplicitAny": true, "suppressImplicitAnyIndexErrors": true, "noUnusedParameters": true, "noUnusedLocals": true, "noImplicitReturns": True, "experimentalDecorators": true, // Open the decorator using "emitDecoratorMetadata": true, "allowJs": true, "sourceMap": true, "paths": { "@/*": [ "src/*" ] } }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "dist" ] }Copy the code
Eslint configuration
Of course, ESLint also adds support for typescript
npm install -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
Copy the code
. Eslintrc. Json Settings
{
"env": {
"es6": true."node": true
},
"extends": [
"eslint:recommended"."plugin:@typescript-eslint/eslint-recommended"]."globals": {
"Atomics": "readonly"."SharedArrayBuffer": "readonly"
},
"parser": "@typescript-eslint/parser"."parserOptions": {
"ecmaVersion": 2018."sourceType": "module"
},
"plugins": [
"@typescript-eslint"]."rules": {
"indent": [ "warn".2]."no-unused-vars": 0}}Copy the code
Package. json runs the configuration
Finally, set the scripts of package.json
"scripts": {
"start": "gulp",// dev
"build": "gulp build", // output
"eslint": "eslint --fix --ext .js,.ts src/",
"server": "export NODE_ENV=production && node dist/app" // production server
},
Copy the code
Add typing support
The project uses the following components
- jsonwebtoken
- koa
- koa-body
- koa-compress
- koa-favicon
- koa-logger
- koa-router
- koa-static
- koa2-cors
- log4js
Install the corresponding type file, and don’t forget @types/node
npm install -D @types/jsonwebtoken @types/koa @types/koa-compress @types/koa-favicon @types/koa-logger @types/koa-router @types/koa-static @types/koa2-cors @types/log4js @types/node
Copy the code
Transform projects using typescript decorators
The.NET MVC framework is handy for using decorators to configure controllers, and typescript decorators can now do the same. The reflect-metadata library is used here. If you’ve used Java or C#, you’ll be familiar with the principle of reflection.
Defines the decorator for the HTTP request
We no longer have to go back and forth between route configuration and controller methods to find and match
import 'reflect-metadata'
import { ROUTER_MAP } from '.. /constant'
/** * @desc generate HTTP method Decorator * @param {string} method - HTTP method, such as get, post, head * @return Decorator - Decorator */
function createMethodDecorator(method: string) {
// The decorator accepts the route path as an argument
return function httpMethodDecorator(path: string) {
return (proto: any, name: string) = > {
const target = proto.constructor;
const routeMap = Reflect.getMetadata(ROUTER_MAP, target, 'method') | | []; routeMap.push({ name, method, path }); Reflect.defineMetadata(ROUTER_MAP, routeMap, target,'method');
};
};
}
Export the HTTP method decorator
export const post = createMethodDecorator('post');
export const get = createMethodDecorator('get');
export const del = createMethodDecorator('del');
export const put = createMethodDecorator('put');
export const patch = createMethodDecorator('patch');
export const options = createMethodDecorator('options');
export const head = createMethodDecorator('head');
export const all = createMethodDecorator('all');
Copy the code
Method of decorating the controller
export default class Sign {
@post('/login')
async login (ctx: Context) {
const { email, password } = ctx.request.body;
const users = await userDao.getUser({ email });
// ...
return ctx.body = {
code: 0,
message: 'Login successful',
data
};
}
@post('/register')
async register (ctx: Context) {
const { email, password } = ctx.request.body;
const salt = makeSalt();
// ...
return ctx.body = {
code: 0,
message: 'Registration successful! ',
data
}
}
}
Copy the code
Collect metadata and add routes
Now that we’ve added decorators to the methods of the corresponding controllers, how do we collect the metadata? This requires the fs file module provided by Node. When the Node service is started for the first time, scan the Controller folder, collect all controller modules, combine the metadata collected by the decorator, and add the corresponding method to the KOA-Router.
import 'reflect-metadata'
import fs from 'fs'
import path from 'path'
import { ROUTER_MAP } from './constant'
import { RouteMeta } from './type'
import Router from 'koa-router'
const addRouter = (router: Router) = > {
const ctrPath = path.join(__dirname, 'controller');
const modules: ObjectConstructor [] = [];
// Scan the Controller folder to collect all controllers
fs.readdirSync(ctrPath).forEach(name= > {
if (/ ^ ^.] +? \.(t|j)s$/.test(name)) {
modules.push(require(path.join(ctrPath, name)).default)
}
});
// Add a route with meta data
modules.forEach(m= > {
const routerMap: RouteMeta[] = Reflect.getMetadata(ROUTER_MAP, m, 'method') | | [];if (routerMap.length) {
const ctr = new m();
routerMap.forEach(route= > {
const{ name, method, path } = route; router[method](path, ctr[name]); })}})}export default addRouter
Copy the code
The last
So the koA project scaffolding transformation is basically completed, see the source code koA-Server