The background,
Koa is a new Web framework, built by the same people behind Express, that aims to be a smaller, more expressive, and more robust cornerstone of web application and API development. By making use of async functions, Koa helps you discard callback functions and greatly enhances error handling. Koa does not bundle any middleware, but rather provides an elegant way to help you write server-side applications quickly and happily.
Combined with the current Node popular three frameworks, Express, Koa, egg. When I used Express, I found the callback was killing me. Finally one day I tried Koa for development and found it too comfortable. ES6 has a lot of syntax support and the synchronous mode is in place. However, in the process of learning KOA, I found that I had understood all the basic knowledge and followed the official documents, but I felt I couldn’t start. Feel the development process, stratification is not too obvious, simple business logic is good, a lot of trouble will come. After checking the information, there was a KOA scaffold called KOA-Generator. I tried it immediately and found it was not the template I wanted. It seems that Github has not been maintained for 2 years, and some new features have not been added to KoA2, which feels a bit outdated. So combined with the pattern of others, but also to avoid too much duplication behind their own wheel. Write an initial template for a Koa project. The main functions of this template are integrated with modules such as Logger, Router, JWT, Mongoose and PM2, as well as some middleware sets. This template is basically enough for simple background projects, without considering high concurrency processing, and will continue to be improved in the future. For starters, it’s a quick way to start new projects, so read this before you start.
2. Directory structure
The following directories are the basic directory structure of the template. The following chapters will introduce the configuration of each directory, so that you can have a clear picture of the project structure during development and locate problems easily.
├─.gitignore // Ignore file Config ├─app.js // Application import ├─config.js // Public config file ├─ file.config.js // pm2 config file ├─package.json // Rely on file config ├ ─ README. Md / / README. Md document ├ ─ routes / / routing | ├ ─ private. Js / / calibration interface | └ public. Js / / public interface ├ ─ models / / database configuration and model | ├ ─ index, js / / database configuration | └ user. Js / / the user's schema file ├ ─ middlewares / / middleware | ├ ─ cors, js / / cross domain middleware | ├ ─ JWT, js / / JWT middleware | ├ ─ logger. Js / / Log print middleware | └ response. Js / / response and exception handling middleware ├ ─ logs / / log directory | ├ ─ koa - the template. The log | └ koa - the template. The log - 2019-05-28 ├ ─ lib/library/tools | ├ ─ error. Js / / exception handling | └ mongo. Js / / mongo configuration ├ ─ controllers / / operation business logic | ├ ─ index. The js / / configuration | ├ ─ the login. The js / / login | └ test. Js / / test ├ ─ services / / operation database | ├ ─ index. The js / / configuration | ├ ─ user. Js / / user ├ ─ bin/directory/start | └ WWW / / boot file configurationCopy the code
3. Construction process
1. Environment preparation
Since Node.js V7.6.0 fully supports async/await, so
Node.js environments must be 7.6.0 or higher
Node.js environment version v7.6 or later
NPM version 3.x and above quickly start installing KOA2
2. Initializepackage.json
npm init
3. Install KOA2
npm install koa
After performing steps 2 and 3, there will be the following file in the file, but this is definitely not what we need, so we will start to move the brick wall.
4. Create the bin file
Create a new bin file, create a new WWW file in this directory, because our back-end project is basically running on Linux, we don’t have to worry about the suffix of the file, just need to know whether the file is executable or not. What’s the use of this file? In fact, this file is used for deployment to launch our entire back-end application, which is the integrated runtime environment in our front end. We run, shut down, restart all in this file. The basic code is as follows:
#! /usr/bin/env node
/**
* Module dependencies.
*/
const app = require('.. /app')
const http = require('http')
const config = require('.. /config')
/**
* Get port from environment and store in Express.
*/
const port = normalizePort(process.env.PORT || config.port)
// app.set('port', port);
/**
* Create HTTP server.
*/
const 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) {
const 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
}
const 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:
throw error
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
const addr = server.address()
const bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port
console.log('Listening on ' + bind)}Copy the code
I’m sure you are familiar with this code bin after using KOA-Generator, this is actually his code inside, and the EXPRESS project WWW file is basically the same. The basic idea is to use the HTTP module in Node.js to expose and listen for your port, which is introduced in the config file config.js.
5. Create app.js
Create a new file app.js and take a quick look at the code
'use strict'
const Koa = require('koa')
const bodyParser = require('koa-bodyparser')()
const staticCache = require('koa-static-cache')
const config = require('./config')
const publicRouter = require('./routes/public')
const privateRouter = require('./routes/private')
const { loggerMiddleware } = require('./middlewares/logger')
const { errorHandler, responseHandler } = require('./middlewares/response') const app = new Koa() // Logger app.use(loggerMiddleware) // Error Handler app.use(errorHandler) // Global Middlewares app.use(bodyParser) app.use(staticCache(config.publicDir)) // Routes app.use(publicRouter.routes(), publicRouter.allowedMethods()) app.use(privateRouter.routes(), privateRouter.allowedMethods()) // Response app.use(responseHandler) module.exports = appCopy the code
In this file, we can see a lot of middleware. The middleware is executed from the outside in, and then from the inside out, namely the Onion pattern. If you do not know the middle partner can go to find relevant information. The execution process of middleware is passed by app.use(). You can simply understand it as a function written by yourself and execute it in turn. Each middleware will pass in two parameters in the APP call: CTX and next
CTX: Koa Context encapsulates node request and response objects in a single object, which provides many useful methods for writing Web applications and apis. These operations are often used in HTTP server development, so they are added to the context layer rather than the higher-level framework, thus forcing middleware to re-implement these common methods. Next: The next middleware function, that is, every middleware function must write this if it wants to go down, otherwise it will not execute. It can be understood as next() in the route guard of the front-end VUe-router to perform the next step or pass the parameter.Copy the code
Other middleware needs to be introduced in this file. You can introduce related middleware first, which will be explained one by one later. If any error occurs, comment it out first
Middlewares file
Several middleware are mainly used in this project. One is logger.js, Response. js, jwt.js and other middleware. After we create the corresponding middleware in this directory, remember to introduce it to app.js, or it will not take effect again. Remember the order of introduction, refer to the code above. Let’s start with middleware
Middleware execution is like an onion, but rather than layer by layer, it is bounded by next, executing the parts of the layer before Next, and executing the parts of the layer after Next when the next layer is finished.
An onion structure, from the top down a layer in, and then from the bottom up a layer back, is not a little feeling.
1, logger. Js
You can think for a moment, if our project is in development or online, we need to see the logs we execute or the parameters of the request and the error information, if not reflected in each request, it will be difficult for us to locate the problem is the front-end or back-end. The logger middleware is used to deal with these situations. In the original KOA template, it simply prints logs. This middleware is encapsulated by the Log4JS module. This middleware will print a fixed format in the console or log, such as HTTP request method, return status, request URL, IP address, request time, etc., and we can also take advantage of log4JS configuration to print custom logs. This can be used instead of console.log(). When using this middleware, it must be placed in the first middleware to ensure that all requests and operations are logged by logger before being sent to the next middleware.
Install plugin: NPM I log4js, this file also needs to introduce fs, path,config.js file, log4 related configuration you can go to the official website to check, here is mainly to get each request parameters method type, etc., can be added according to their own needs. Has been annotated in the code, you can see the code while understanding.
The code is as follows:
'use strict'
const fs = require('fs')
const path = require('path')
const log4js = require('log4js')
const config = require('.. /config'Parse (config.logpath).dir const logsDir = path.parse(config.logpath).dirif(! Fs.existssync (logsDir)) {fs.mkdirsync (logsDir)} // Configurelog4.js
log4js.configure({
appenders: {
console: { type: 'console' },
dateFile: { type: 'dateFile', filename: config.logPath, pattern: '-yyyy-MM-dd' }
},
categories: {
default: {
appenders: ['console'.'dateFile'],
level: 'info'
}
}
})
const logger = log4js.getLogger('[Default]') // Logger middleware const loggerMiddleware = async (CTX, Next) => {// request start time const start = new Date() await next() // End time const ms = new Date() - start // Print out request related parameters const remoteAddress = ctx.headers['x-forwarded-for'] || ctx.ip || ctx.ips ||
(ctx.socket && (ctx.socket.remoteAddress || (ctx.socket.socket && ctx.socket.socket.remoteAddress)))
let logText = `${ctx.method} ${ctx.status} ${ctx.url}Request parameters:${JSON.stringify(ctx.request.body)}Response parameters:${JSON.stringify(ctx.body)} - ${remoteAddress} - ${ms}ms`
logger.info(logText)
}
module.exports = {
logger,
loggerMiddleware
}
Copy the code
2, the response. Js
In the basic KOA template, we can use ctx.body to return the front end, but we find that some things are often written repeatedly, so it is better to put them forward for encapsulation, and there is no need to worry about the inconsistent return formats. First look at the code:
'use strict'
const { logger } = require('./logger'Const responseHandler = (CTX) => {const responseHandler = (CTX) => {if(ctx.result ! == undefined) { ctx.type ='json'
ctx.body = {
code: 200,
msg: ctx.msg || ' ', data: Ctx.result}}} // This middleware handles exceptions that occur in other middleware, and we catch exceptions after next(), Const errorHandler = (CTX, next) => {return next().catch(err => {
if(err.code == null) { logger.error(err.stack) } ctx.body = { code: err.code || -1, data: null, msg: Err.message.trim ()} // Ensure that the return status is 200 ctx.status = 200return Promise.resolve()
})
}
module.exports = {
responseHandler,
errorHandler
}
Copy the code
The back of the code will be exposed responseHandler and errorHandler, responseHandler correct response, we are in business, only need to CTX. To write the result. This middleware can be placed at the back of all middleware, so that all previous middleware needs to pass through it before returning to the front. ErrorHandler is used to catch errors or exceptions. You can return the response to the front end, otherwise the front end will appear a padding state until the timeout.
3, JWT. Js
New file jwt.js, we first understand what JWT is, how the process is
What JWT is:
JWT declarations are typically used to communicate authenticated user identity information between the identity provider and the service provider
What is the structure of the JSON Web Token
Header
The header typically consists of two parts: the token type (” JWT “) and the algorithm name (such as HMAC SHA256 or RSA, etc.).
payload
The second part of the JWT is payload, which contains claims (demands). Declarations are declarations about entities (usually users) and other data.
To get the Signature part, you must have the encoded header, the encoded payload, a secret key, and the Signature algorithm specified in the header
JWT process:
The user requests the server using a username and password
The server authenticates the user’s information
The server sends the user a token through authentication
The client stores the token and supplies it with each request
The server validates the token value and returns data
After understanding this JWT, we used this middleware to package KOA-JWT and JSONWebToken in our project. JWT was used to generate token to judge the uniqueness of users. After each login, we returned to the front end, and each API requiring authentication in the front end required token verification. We use KOA-JWT for token generation, but how can we get token resolved users in each interface? This middleware plays a key role. After passing the authentication, the information will be stored in CTX for global use. After completing the middleware, how to reference it will be explained later.
Remember to install the koA-jWT and jsonWebToken plug-ins. Here is the jwt.js code:
'use strict'
const koaJwt = require('koa-jwt')
const jwt = require('jsonwebtoken')
const config = require('.. /config')
const jwtMiddleware = koaJwt({ secret: config.secret })
module.exports = function(CTX, next) {// Decrypt the token and store it in CTX.if (typeof ctx.request.headers.authorization === 'string') {
const token = ctx.request.headers.authorization.slice(7)
ctx.jwtData = jwt.verify(token, config.secret)
} else {
throw {code: 401, message: 'no authorization'}
}
} catch (err) {
throw {code: 401, message: err.message}
}
next()
}
Copy the code
4. Cers. js file
In front – and back-end interface requests, cross-domain situations can occur due to browser limitations. Common cross-domain solutions are:
1. JSONP is cross-domain
2. Nginx reverse proxy
3. Modify heADE on the server
4, the document domain
5, the window name
6, postMessage
7. Background configuration runs across domains
How to set up cross-domains in KOA
First take a look at how to set up cross-domain in KOA, cORS specific implementation process, detailed introduction, has been annotated in the code. Take a look at the original configuration first, then use middleware directly, but still need to understand the specific implementation, in case of a problem, can quickly familiar investigation.
App. use(async (CTX, next) => {// Allow requests from all domain names ctx.set("Access-Control-Allow-Origin"."*"); // ctx.set()"Access-Control-Allow-Origin"."http://localhost:8080"); // Set the allowed HTTP request method ctx.set("Access-Control-Allow-Methods"."OPTIONS, GET, PUT, POST, DELETE"); // The field is required. It is also a comma-separated string indicating all header information fields supported by the server.ctx.set ("Access-Control-Allow-Headers"."x-requested-with, accept, origin, content-type"); After receiving the Request, the server checks the Origin, access-Control-request-method, and access-Control-request-headers fields to confirm that cross-source requests are allowed. // Content-type indicates the media Type information in the specific request ctx.set("Content-Type"."application/json; charset=utf-8"); // This field is optional. Its value is a Boolean value indicating whether cookies are allowed to be sent. By default, cookies are not included in CORS requests. // When set to allow requests to carry cookies, ensure that"Access-Control-Allow-Origin"Is the domain name that the server has, but cannot be"*";
ctx.set("Access-Control-Allow-Credentials".true); // This field is optional and specifies the validity period of this precheck request, in seconds. // If the request method is PUT or DELETE or the content-Type field is application/json, the server will send a request for validation in advance. That is, the server does not need to verify ctx.set("Access-Control-Max-Age", 300); /* The getResponseHeader() method of the XMLHttpRequest object takes only six basic fields when CORS requests: Cache-control, Content-language, Content-Type, Expires, Last-Modified, Pragma. */ / Use access-Control-expose-headers, // getResponseHeader('myData'Can return to us the required value / / https://www.rails365.net/articles/cors-jin-jie-expose-headers-wu CTX. Set ("Access-Control-Expose-Headers"."myData");
await next();
})
Copy the code
Relatively used is the god package good KOA-CORS middleware, you can check the NPM documents, in this project is the MIDDLEWARE of KOA-CORS, the basic configuration is written in the CERs.js, and then reference through the middleware. Note Write it in front of the router to avoid requesting the interface without cross-domain configuration.
In app.js, remember to install the introduction of KOA-Cors
// cors
app.use(cors(corsHandler))
Copy the code
'use strict'
const corsHandler = {
origin: function (ctx) {
if (ctx.url === '/test') {// Here you can configure the address of the interface that does not run cross-domainreturn false;
}
return The '*';
},
exposeHeaders: ['WWW-Authenticate'.'Server-Authorization'],
maxAge: 5,
credentials: true,
allowMethods: ['GET'.'POST'.'DELETE'],
allowHeaders: ['Content-Type'.'Authorization'.'Accept'],
}
module.exports = {
corsHandler
}
Copy the code
Koa – helmet middleware
Koa-helmet can help your app protect against some of the more common security web security risks. It’s actually a combination of nine security middleware components, most of which work on HTTP headers. The following is the default enabled feature.
Use this middleware first installed in your project, NPM I Koa-Helmet –save, where you can just reference the default configuration directly, and if you need to see the official documentation to make your own configuration. For more information on koA’s security features, check out the god’s blog at cnodejs.org/topic/5a437…
This can be referenced directly in our app.js
const helmet = require("koa-helmet")
// Helmet
app.use(helmet())
Copy the code
Other Middleware
Koa middleware can be said that many gods have made wheels for us, we can directly use them, such as: BodyParser, KOA-session, middleware koA-convert that will convert middleware into koA2 that can be used, EJS template koA-EJS, we need to reference according to their needs, because it is the basic template, not add too much middleware, reduce volume. Koa-bodyparser and Koa-static-Cache are also used in our project. Remember to install them and introduce them in app.js
7. Lib files
This folder is mainly used to store the tool class folder, some global tool processing files can be put here, currently there are only two files in this project, create error. Js and mongoDB
Error. Js is mainly used to throw exceptions in the middleware. As we have added the middleware to capture exceptions before, we can directly throw exceptions if there are any errors in the middleware operation process. The CodedError method inherits Error. ForbiddenError and InvalidQueryError inherit CodedError. Remember to instantiate this constructor when using it. If you are not familiar with ES6 inheritance, take a look at the documentation and then take a look at the utility class.
'use strict'
class CodedError extends Error {
constructor (message = 'Unknown error', code = -1) {super(message) this.code = code}} module.exports = {CodedError, /** * deny access to constructor */ ForbiddenError: class ForbiddenError extends CodedError { constructor (message ='Access denied') {super(message, 403)}}, /** * invalid argument constructor */ InvalidQueryError: class InvalidQueryError extends CodedError { constructor (message ='Invalid argument') {
super(message, 400)
}
}
}
Copy the code
The mongoDB. Js file is a link configuration to mongoDB, which is covered later in Models.
8. Models file
In this project, Mongoose is used to operate the mongoDB database. Mongoose has simple syntax and requires too much learning cost. MongoBD can be stored flexibly according to the official documentation configuration and API operations. Mongoose’s configuration consists of three major parts: Connect, Models, and Schema
Connect: Used to create database connections and listen
Schema: Schema is mainly used to define the structure of document in MongoDB Collection, which can be understood as the definition of table structure by Mongoose (not only can define the structure and attributes of the document, but also the instance method, static model method and compound index of the document). Each schema is mapped to a collection in mongodb. Schema does not have the ability to operate a database. It simply means the definition of fields.
Models: A Model is a fancy constructor compiled from a Schema with abstract properties and behavior. Each instance of a Model is a document, which can be saved to and operated on the database. A model is a model generated by a schema that performs operations on a database.
In our project, we configure the global collection in the Models file. The index.js file handles the connect and Models steps. Create index.js, install Mongoose, import related files, and copy the following code:
const fs = require('fs');
const path = require('path');
const mongoose = require('mongoose'); // mongoose module const config = require('.. /config')
const { logger } = require('.. /middlewares/logger')
let url = "mongodb://" + config.mongoDB.host + ":" + config.mongoDB.port + "/"+ config.mongoDB.database; var mongo = mongoose.createConnection(url); // Create a database connectionletdb = { mongoose: mongoose, mongo: mongo, models: {} }; Check/mongo/error.'error'.function(err) { logger.error(new Error(err)); }); / / open the mongo. Once ('open'.function () {
logger.info("mongo is opened"); }); Fs.readdirsync (__dirname).filter(function (file) {
return (file.indexOf(".")! == 0) && (file ! = ="index.js");
}).forEach(function(file) { var modelFile = require(path.join(__dirname, file)); var schema = new mongoose.Schema(modelFile.schema); db.models[modelFile.name] = mongo.model(modelFile.name, schema, modelFile.name); }); // Select model db.getModel = based on namefunction (name) {
return this.models[name];
};
module.exports = db;
Copy the code
The link part of the code is pretty straightforward, but the Models part is not. This is part of the modular development process to integrate other JS files under the Models file for developers to use without having to import and export every file they write.
In the initial case, models introduction only needs mongoose. Model (‘ name ‘, schema); And expose it, you can operate on the database.
fs.readdirSync(__dirname)
.filter(function (file) {
return (file.indexOf(".")! == 0) && (file ! = ="index.js");
}).forEach(function (file) {
var modelFile = require(path.join(__dirname, file));
var schema = new mongoose.Schema(modelFile.schema);
db.models[modelFile.name] = mongo.model(modelFile.name, schema, modelFile.name);
});
Copy the code
In this file, we do one thing: read all files in the Models directory with names other than index.js and suffix.js, reference them with require, import them into models and expose them to the action database. The advantage of this approach is that as the project gets bigger and bigger, if we need to add a new schema, we can simply create a new.js file in the models directory without having to do the relational operations introduced
Because of the previous step, we can directly add a new schema configuration file. Index. js automatically introduces and exposes the model
'use strict'
module.exports = {
name: "user"Schema: {uuid: String, // uuid userName: String, // userName: String, // password: String, // password}};Copy the code
We can use it like this,
const User = require('.. /models/index').getModel('user')
const user = await User.findOne({userName: userName})
Copy the code
9. PM2 configuration
PM2 is a Nodejs process management tool that can be used in production environments, and it has a load balancing built in. It not only ensures that the service is always online without interruption, but also provides 0 second reload function, as well as a series of other process management and monitoring functions. And it’s very simple to use. Pm2 official documents have been detailed configuration description, I will not briefly describe one by one here, mainly talking about how my KOA project with PM2 related management or deployment. You can view the common PM2 commands when they are needed. You do not need to recite them. It can also be combined with package.json and run with custom commands. Json script configuration and initialization file file.config.js are configured to run in multiple environments, and we can switch environments as needed.
Add the package.json file as follows:
"scripts": {
"start": "node ./bin/www"."dev": "pm2 start ecosystem.config.js --env dev"."test": "pm2 start ecosystem.config.js --env test"."pro": "pm2 start ecosystem.config.js --env pro"."logs": "pm2 logs"."stop": "pm2 stop ecosystem.config.js"
},
Copy the code
One of the
NPM run dev: development environment NPM run test: test environment NPM run pro: production environment NPM run logs: View PM2 logs. NPM run Stop: Stops the PM2 serviceCopy the code
Added file.config. js file:
module.exports = {
apps : [{
name: 'API',
script: './bin/www',
// Options reference: https://pm2.io/doc/en/runtime/reference/ecosystem-file/
args: 'one two',
instances: 1,
autorestart: true,
watch: true, ignore_watch: [// do not listen to the file'node_modules'.'logs'
],
max_memory_restart: '1G',
env_pro: {
"NODE_ENV": "production"."REMOTE_ADDR": ""
},
env_dev: {
"NODE_ENV": "development"."REMOTE_ADDR": ""
},
env_test: {
"NODE_ENV": "test"."REMOTE_ADDR": ""}}};Copy the code
This file is mainly the basic configuration of PM2. You can modify it directly in the file instead of configuring it every time. What we need to pay attention to is that we can add the environment and variables we need in env. The watch attribute in the file can be configured to monitor changes in the file and automatically restart the project, which is easier to use. If you want to ignore changes to a folder, ignore_watch can be set to ignore_watch.
10. Route configuration
Create a router file that stores basic routing configurations. Create two private and public files, install the router, and import the route in app.js.
const publicRouter = require('./routes/public')
const privateRouter = require('./routes/private')
// Routes
app.use(publicRouter.routes(), publicRouter.allowedMethods())
app.use(privateRouter.routes(), privateRouter.allowedMethods())
Copy the code
Each route must be exposed so that the middleware can be used in the app.js file. PublicRouter. AllowedMethods () according to CTX. Status set response headers
Private: Routes in this file must pass JWT authentication before they can be accessed. Router. use(jwtMiddleware) should be placed in front of the request route so that you can pass through it every time. Router.prefix (‘/ API ‘) should be added to each request. This prefix can be changed directly to the service directory
'use strict'
const Router = require('koa-router')
const controllers = require('.. /controllers')
const jwtMiddleware = require('.. /middlewares/jwt')
const router = new Router()
router.prefix('/api')
router.use(jwtMiddleware)
router.get('/test', controllers.test.test)
module.exports = router
Copy the code
Public: on the contrary, this file is mainly used for login verification, which is commonly used for login, registration, and other interfaces that do not need authentication.
'use strict'
const Router = require('koa-router')
const controllers = require('.. /controllers')
const router = new Router()
router.prefix('/api')
router.post('/login', controllers.login.login)
module.exports = router
Copy the code
Why aren’t we dealing with business logic here? In fact, this is following the MVC idea of separation. Put the database operations in the Controllers file. This will not look particularly messy if we have more than one interface. So let’s talk about this file.
11, Controllers files
In order to make the whole project more modular, this directory mainly deals with the callback function of the corresponding route. Generally, we do not include steps such as business logic operation in the Router file. In this directory, routes and controller are separated, which is convenient for code viewing and code maintenance and development.
Create a new index.js file in controller:
This file is similar to the collection in the index.js file in the Models directory, where the other files are exported to unify to index to expose.
'use strict'
const fs = require('fs') const files = fs.readdirSync(__dirname).filter(file => file ! = ='index.js')
const controllers = {}
for (const file of files) {
if (file.toLowerCase().endsWith('js')) {
const controller = require(`./${file}`)
controllers[`${file.replace(/\.js/, '')}`] = controller
}
}
module.exports = controllers
Copy the code
Other files can be written in the following basic framework, using previously encapsulated operations such as operations, responses, JWT, and so on. You can take a look at the following code analysis.
Create a new user.js file, which is our business file, and we can add it as needed
'use strict'
const jwt = require('jsonwebtoken')
const config = require('.. /config')
const userServices = require('.. /services').user
const { InvalidQueryError } = require('.. /lib/error')
const login = {}
login.login = async (ctx, next) => {
console.log(userServices)
const {userName, password} = ctx.request.body
if(! userName || ! password) { throw new InvalidQueryError() } const user = await userServices.login({ userName: userName, password: password })if(! user) { ctx.result =' '
ctx.msg = 'User does not exist'
} else{ctx.result = jwt.sign({data: user._id, // set token expiration time exp: Math.floor(Date.now() / 1000) + (60 * 60), // 60 seconds * 60 minutes = 1 hour }, config.secret) }return next()
}
module.exports = login
Copy the code
Note: Add Authorization attribute in request Header parameter, value format: Bearer [token]
11. Services file
The “services” file is used to handle the configuration of the database as shown in the controllers file. The “services” file is used to handle the configuration of the database as shown in the controllers file. The “services” file is used to handle the configuration of database as shown in the controllers file.
'use strict'
const fs = require('fs') const files = fs.readdirSync(__dirname).filter(file => file ! = ='index.js')
const services = {}
for (const file of files) {
if (file.toLowerCase().endsWith('js')) {
const service = require(`./${file}`)
services[`${file.replace(/\.js/, '')}`] = service
}
}
module.exports = services
Copy the code
If you need to create other module operations, you can create a new file such as user.js, which is currently a collection operation on the database user, as shown in the following example:
const User = require('.. /models/index').getModel('user')
const user = {
/**
* @Description: 登录
* @date 2019/5/30
* @params: { Object } userData
* @return: { Object | null }
*/
async login (userData) {
return await User.findOne(userData)
}
}
module.exports = user
Copy the code
12. Config.js file
The root directory of the file is mainly used to store global new configuration, if no global configuration in a project, then a place to change affects a lot of other places, this is not conducive to the work efficiency, in the process of development, we usually put the commonly used in this document, for example, the database parameter, ports, keys, global variables, etc. Look at your requirements for appropriate changes. This file exposes the variable and requires the reference.
'use strict'
const path = require('path')
module.exports = {
port: '3001',
secret: 'secret',
publicDir: path.resolve(__dirname, './public'),
logPath: path.resolve(__dirname, './logs/koa-template.log'),
mongoDB: {
database: 'mall',
username: 'root',
password: 'root',
host: '127.0.0.1',
port: 27017
}
}
Copy the code
Package. The json file
There is usually a package.json file under the root of each Nodejs project. This file, which can be generated by NPM init, defines the various modules required by the project, as well as the project’s configuration information (such as name, version, license, and other metadata). Inside the package.json file is a JSON object, each member of which is a setting for the current project. We can also configure our NPM run XXX command in it, you can configure according to your needs. This is the package.json file that the project needs. See if it’s the same as yours.
{
"name": "koa-template"."version": "0.1.0 from"."author": "bayi"."private": true."scripts": {
"start": "node ./bin/www"."dev": "pm2 start ecosystem.config.js --env dev"."test": "pm2 start ecosystem.config.js --env test"."pro": "pm2 start ecosystem.config.js --env pro"."logs": "pm2 logs"."stop": "pm2 stop ecosystem.config.js"
},
"dependencies": {
"koa": "^ 2.6.2." "."koa-bodyparser": "^ 2"."koa-helmet": "^ 4.1.0." "."koa-jwt": "^ 3.5.1 track of"."koa-router": "^ 7.4.0"."koa-static-cache": "^ 5.1.2." "."koa2-cors": "^ 2.0.6"."log4js": "^ 3.0.6"."mongoose": "^ 5.5.5." "}}Copy the code
Third, other
Making address:
Github.com/bayi-lzp/ko… (star! Star! Star!
Technology stack:
Koa2, mongoose
Updated in the near future:
Typescript, Redis, Docker