preface

This article was written while I was building the Node server infrastructure and is the first in this series, with more updates on some of the ideas I implemented on the project. Follow the author into the koA2 project construction!

Environment to prepare

I. Project initialization

// 1. Ensure that Node.js is installed
node -v
// 2. Ensure that NPM or YARN has been installedNPM -v or yarn-v// 3. Enter the empty project file koa2(project name)
cd koa2
// 4. Initialize package.json
yarn init
Install koA
yarn add koa -s
Copy the code

Second, test the minimum system

  1. The new app. Js
const Koa = require("koa"); const app = new Koa(); app.use(async (ctx) => { ctx.body = "hello koa2"; }); app.listen(3000); Console. log(" server on: http://localhost:3000/");Copy the code
  1. Operating system
node app.js
Copy the code
  1. Viewer open
http://localhost:3000/
Copy the code

After the above minimum system test, we can check whether there is a problem with our project initialization, but we will reduce the errors in the following work.

Three, install middleware

  1. Koa-onerror Indicates an error message
  2. Koa-bodyparser parses json request data in the body
  3. Koa – logger logs
  4. Koa – cors across domains
  5. Koa -static Indicates static resources
  6. Koa – the router routing
  7. Nodemon hot update
  8. The debug debug mode
yarn add koa-router koa-onerror koa-bodyparser koa-logger koa-cors koa-static nodemon debug --save
Copy the code

Install the database

  1. Because the author is familiar with mysql, the database uses mysql
yarn add mysql --save
Copy the code

5. Final directory structure

  1. Middelware middleware
  2. Controller, service code of the control layer
  3. Config Database Settings
  4. Dbhelper SQL statements
  5. Routes routing
  6. Modal object layer
│ app. Js │ package. Json │ REAAME. Md │ yarn. The lock ├ ─ app │ ├ ─ controller │ │ users. Js │ │ │ └ ─ modal │ users. Js ├ ─ bin │ WWW ├ ─ config │ constant. Js │ database. Js │ dbPool. Js ├ ─ dbhelper │ users. Js ├ ─ middlewares │ │ index. The js │ └ ─ middleware │ ├─ ├─ ├─ garbage ├─public │ faviconCopy the code

Basic configuration

1. Rewrite app.js

  1. Registered middleware
const Koa = require("koa");
const app = new Koa();

const onerror = require("koa-onerror");
const bodyparser = require("koa-bodyparser");
const logger = require("koa-logger");
const router = require("./routes/index");
const cors = require("koa-cors");

/ / registration error
onerror(app);
/ / register bodyparser
app.use(bodyparser());
// Register logs
app.use(logger());
// Register static resources
app.use(require("koa-static")(__dirname + "/public"));
// Register across domains
app.use(cors());
// Register custom middleware
require('./middlewares/index')(app);
// Register the route
app.use(router.routes(), router.allowedMethods());

// logger-handling
app.use(async (ctx, next) => {
  const start = new Date(a);await next();
  const ms = new Date() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
// error-handling
app.on("error".(err, ctx) = > {
  console.error("server error", err, ctx);
});

module.exports = app;
Copy the code
  1. Koa-onerror, KOA-BodyParser, KOA-Logger, KOA-CORS and other middleware are common MIDDLEWARE of KOA, and the usage will not be introduced here
  2. Koa-router and Middlewares do some custom operations, which are described below

2. Add project entry

  1. Create the entry file bin/ WWW
#! /usr/bin/env node

/** * Module dependencies. */

 var app = require('.. /app');
 var debug = require('debug') ('demo:server');
 var http = require('http');
 
 /** * Get port from environment and store in Express. */
 
 var port = normalizePort('3000');
 // app.set('port', port);
 
 /** * Create HTTP server. */
 
 var server = http.createServer(app.callback());
 
 /** * Listen on provided port, on all network interfaces. */
 
 server.listen(port);
 server.on('error', onError);
 server.on('listening', onListening);
 
 /** * Normalize a port into a number, string, or false. */
 
 function normalizePort(val) {
   var port = parseInt(val, 10);
 
   if (isNaN(port)) {
     // named pipe
     return val;
   }
 
   if (port >= 0) {
     // port number
     return port;
   }
 
   return false;
 }
 
 /** * Event listener for HTTP server "error" event. */
 
 function onError(error) {
   if(error.syscall ! = ='listen') {
     throw error;
   }
 
   var bind = typeof port === 'string'
     ? 'Pipe ' + port
     : 'Port ' + port;
 
   // handle specific listen errors with friendly messages
   switch (error.code) {
     case 'EACCES':
       console.error(bind + ' requires elevated privileges');
      // process.exit(1);
       break;
     case 'EADDRINUSE':
       console.error(bind + ' is already in use');
      // process.exit(1);
       break;
     default:
       throwerror; }}/** * Event listener for HTTP server "listening" event. */
 
 function onListening() {
   var addr = server.address();
   var bind = typeof addr === 'string'
     ? 'pipe ' + addr
     : 'port ' + addr.port;
   debug('Listening on ' + bind);
 }
 
Copy the code

Rewrite package.json

  1. Adding a Startup Script
"scripts": {
    "start": "node bin/www"."dev": "nodemon bin/www" / / hot update
},
Copy the code
  1. The complete package. Json
// package.json
{
  "name": "koa2"."version": "1.0.0"."description": "koa2+mysql"."main": "index.js"."license": "MIT"."scripts": {
    "start": "node bin/www"."dev": "nodemon bin/www"
  },
  "dependencies": {
    "debug": "^ 4.3.3." "."koa": "^ 2.13.4." "."koa-bodyparser": "^ 4.3.0"."koa-cors": "^ 0.0.16"."koa-logger": "^ 3.2.1." "."koa-onerror": "^ 4.1.0." "."koa-router": "^ 10.1.1"."koa-static": "^ 5.0.0"."mysql": "^ 2.18.1"."nodemon": "^ 2.0.15"}}Copy the code

Add static pages

  1. When we register static resources in app.js, we know that we put them in the public folder
  2. Create the index.html page
<! DOCTYPE html> <html lang="en"> ... </html>Copy the code
  1. Add the favicon.ico icon

5. Start the project

  1. Start the project
// yarn dev Specifies hot update yarn devCopy the code
  1. Viewer open
http://localhost:3000/
Copy the code
  • With koA-static static resource processing, the root directory is opened by default/public/index.html.
  • Access does not require adding public, i.eThe file name http://localhost:3000/This form is accessible.

The routing configuration

  1. Routes are split into modules to facilitate route decoupling.
  2. The registered routes are decoupled from the business layer to facilitate the reuse of business logic.
  3. Add route prefixes in the routing module to make routes easier to read.
  • Those of you who have used Webpack will know that you can get a qualified context by using require.context.

One, the implementation of automatic file loading function

  1. Create the file utils/ autoloadfile.js
#! /usr/bin/env node
const path = require("path");
const fs = require("fs");
const getPathInfo = (p) = > path.parse(p);

/ * * *@description // Recursively read the file, similar to webpack's require.context() *@param {String} Directory File directory *@param {Boolean} UseSubdirectories Whether to query subdirectories. Default: false *@param {array} ExtList query file suffix, default ['.js'] */
const autoLoadFile = (
  directory,
  useSubdirectories = false,
  extList = [".js"]
) = > {
  const filesList = [];
  // Read files recursively
  function readFileList(directory, useSubdirectories, extList) {
    const files = fs.readdirSync(directory);
    files.forEach((item) = > {
      const fullPath = path.join(directory, item);
      const stat = fs.statSync(fullPath);
      if (stat.isDirectory() && useSubdirectories) {
        readFileList(path.join(directory, item), useSubdirectories, extList);
      } else {
        constinfo = getPathInfo(fullPath); extList.includes(info.ext) && filesList.push(fullPath); }}); } readFileList(directory, useSubdirectories, extList);// Generate the required objects
  const res = filesList.map((item) = > ({
    path: item,
    data: require(item), ... getPathInfo(item), }));return res;
};

module.exports = autoLoadFile;

Copy the code
  1. Use the sample
const context = require(".. /utils/autoLoadFile");
const fileList = context(path.join(__dirname, "./route"), true);
Copy the code

2. Route self-registration

  1. Create the routes/index.js self-executing file
const router = require("koa-router") ();const path = require("path");
const context = require(".. /utils/autoLoadFile");

/ * * *@param {Array} List of files that arR needs to register routes */
function importAll(arr) {
  arr.forEach((key) = > {
    // This method is used by nested routines
    router.use("/api", key.data.routes(), key.data.allowedMethods());
  });
}
importAll(context(path.join(__dirname, "./route"), false));

module.exports = router;
Copy the code

3. Register routes

  1. app.js
// routes/index) const router = require("./routes/index"); App.use (router.routes(), router.allowedmethods ());Copy the code

4. Create a routing module

  1. Now that the infrastructure is ready, try creating a module
// routes/route/users.js const router = require("koa-router")(); // Module route prefix router.prefix("/users"); router.post("/", function (ctx, next) { ctx.body = "this a users response!" ; }); /** * @param {username} username * @param {password} password */ router.post("/login", async (ctx) => { const request = ctx.request.body; const { username, password } = request; If (the username & password) {CTX. Body = {" code ": 200," MSG ":" success ", "data", "login succeeds"}}}); module.exports = router;Copy the code
  1. The final routing path: localhost: 3000 / API/users/login

    • /apiIs the route prefix.
    • /usersIs the module route prefix
    • /loginSpecific interface
  2. The results of

Middleware configuration

1. Middleware self-registration

  1. Creates a self-executing file middlewares/index.js
const path = require("path"); const context = require(".. /utils/autoLoadFile"); /** * @param {Array} arr */ const install = (app) => {context(path.join(__dirname, ". false).forEach((key) => { app.use(key.data); }); }; module.exports = install;Copy the code

Second, middleware registration

  1. app.js
// Register custom middleware
require('./middlewares/index')(app);
Copy the code

Response body middleware

  1. Create message.js middleware that encapsulates the response body
// middlewares/middleware/message.js

module.exports = async (ctx, next) => {
  ctx.res.$success = (data, code = 200) = > {
    const _data = {
      code,
    };
    if (typeof data === "object") {
      _data.msg = "success";
      _data.data = data;
    } else {
      _data.msg = data;
    }
    ctx.body = _data;
  };

  ctx.res.$error = (err, code = 500) = > {
    const _data = {
      code,
    };
    if (typeof err === "object") {
      _data.msg = "error";
      _data.data = JSON.stringify(err);
    } else {
      _data.msg = err;
    }
    ctx.body = _data;
  };

  await next();
};
Copy the code

Examples of response body middleware

 router.post('/login'.async (ctx) => {
  const request = ctx.request.body;
  const { username, password } = request;
  if (username&&password) {
    ctx.res.$success('Login successful');
  } else {
    ctx.res.$error("Request failed".403); }})Copy the code

5. Response body middleware results

Success / / {" code ": 200," MSG ":" success ", "data", "login succeeds"} failure / / {" code ": 403," MSG ":" error ", "data" : "request failed}"Copy the code

The resources

  1. Koa website koajs.com/
  2. Koa middleware use of Koa – the router www.jianshu.com/p/f169c342b…
  3. Koa – the router official documentation wohugb. Gitbooks. IO/koajs/conte…
  4. Koa – github.com/koajs/bodyp bodyparser source code…
  5. Koa – error source github.com/koajs/error…
  6. Koa – github.com/koajs/logge logger source code…

conclusion

In this paper, a relatively simple project architecture is built from the point of view of project initialization, and automatic registration of routing and middleware is realized.

I will continue to update this article from the perspectives of building RESTFUL apis, login token verification, permission management, and more.

I am Front-end Creek. If you are interested, please follow the official account of Front-end Creek and add me on wechat wXL-15153496335.

Gitee warehouse address for a star thank you!!