background
Previously based on egg JS has developed several projects. It is found that each project has functions such as configuration file, database connection operation, data model definition, wechat login authorization processing, etc. when making A new project, it will always copy the previous project to delete and modify. Sometimes A practical function is added to project A and A cool tool function is added to project B, but it is forgotten later, There is no place for unified sedimentation, so A scaffold is made, which can be used out of the box for new project development and facilitate technical sedimentation.
location
Based on egg JS encapsulates a layer, aggregating common functions together, closer to the business, if egg JS is noodles, so this back-end scaffold is noodles.
Function introduction
modular | Function introduction | speed of progress |
---|---|---|
Database operation | The built-in ORM egg serialize plug-in is used as the database operation library, which can be adapted to Postgres, MySQL, MariaDB and SQLite databases | √ |
Interface routing configuration | The built-in egg router plus plug-in solves the problem of routing namespace | √ |
Automatically generate interface documents | Built in egg swagger doc plug-in, write interface notes according to the contract, and then automatically generate interface use documents | √ |
Field verification | The built-in egg validate plus plug-in is used as field verification, which can be used alone or in combination with the egg swagger doc plug-in. It not only automatically generates interface documents, but also realizes field verification | √ |
Multi environment configuration | Built in local development environment, test environment and production environment configuration, switching and expansion are very convenient | √ |
Cross domain configuration | Different cross domain configurations are set in multiple environments to meet the different demands of cross domain strategies in each environment | √ |
exception handling | Uniformly encapsulate and handle the errors thrown, and do not throw the errors directly to the front end, so as to improve the security and better front-end use experience | √ |
Tool library | It encapsulates common tool functions such as menu tree processing, version number comparison and uid generation, which makes development more convenient | √ |
unit testing | Complete unit tests make the code more robust | √ |
Wechat applet login | Built in the whole process of authorized login and obtaining mobile phone number of wechat applet, which can be seamlessly connected with wechat applet | √ |
eslint | Strict and complete eslint rules are built in to ensure code specification and more efficient collaborative development | √ |
Project structure
. ├── LICENSE ├── README.md # Please read me before you get into trouble ├── app # Main program code directory │ ├── contract # Data structure required for automatic generation of interface documents │ ├── controller # controller │ ├── extend # Framework extension │ ├── middleware # middleware │ ├── model # data model │ ├── public # Public static resources │ ├── router.js # Routing configuration │ └── service # service ├── app.js # entrance ├── appveyor.yml ├── config │ ├── config.default.js # Default configuration, including customized application configuration, security policy, cookie, cross domain, permission, etc │ ├── config.local.js # Configuration items specific to the local environment │ ├── config.prod.js # Configuration items specific to production environment │ ├── config.test.js # Configuration items specific to the test environment │ ├── config.unittest.js # Configuration items specific to unit test environment │ └── plugin.js # Plug ins, which can be customized to enable or disable some plug-ins ├── jsconfig.json ├── note │ ├── database.sql # Create database script │ ├── note.md # Project Development Notes │ └── tables.sql # Create user table script ├── package.json ├── test # unit testing │ └── app └── yarn.lock
Project configuration
If there is no database, execute the following sql to create a new database:
CREATE DATABASE IF NOT EXISTS template_node_egg DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;
Then import the user data table:
/* Navicat Premium Data Transfer Source Server : local Source Server Type : MySQL Source Server Version : 50722 Source Host : localhost:3306 Source Schema : template_node_egg Target Server Type : MySQL Target Server Version : 50722 File Encoding : 65001 Date: 02/01/2022 23:22:25 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `open_id` varchar(255) NOT NULL COMMENT 'Same as wechat openid', `union_id` varchar(255) DEFAULT NULL COMMENT 'Same as wechat unionid,As a reserved field, it does not necessarily have a value', `nick_name` varchar(255) NOT NULL COMMENT 'Nickname, same as wechat nickName', `password` varchar(255) DEFAULT NULL COMMENT 'The login password, as a reserved field, does not necessarily have a value', `avatar_url` text NOT NULL COMMENT 'Avatar, same as wechat avatarUrl', `phone` varchar(255) DEFAULT NULL COMMENT 'Mobile number, may be empty', `gender` int(11) DEFAULT NULL COMMENT 'Gender, may be empty', `country` varchar(255) DEFAULT NULL COMMENT 'Country, may be empty', `province` varchar(255) DEFAULT NULL COMMENT 'Province, may be empty', `city` varchar(255) DEFAULT NULL COMMENT 'City, may be empty', `language` varchar(255) DEFAULT NULL COMMENT 'Language, may be empty', `logged_at` datetime DEFAULT NULL COMMENT 'Last login time', `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of user -- ---------------------------- BEGIN; INSERT INTO `user` VALUES (1, 'o8FXk5E4u7hwaguN6kSq-KPXApJ1', NULL, 'Test account', NULL, 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20180520%2F0473e00bdfd2476fbe0c228a45a1652c.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1628131130&t=05ee794a54bad8edd2fd8bb2536db5b9', NULL, NULL, NULL, NULL, NULL, NULL, '2022-01-01 23:59:59', '2022-01-01 23:59:59', '2022-01-01 23:59:59'); COMMIT; SET FOREIGN_KEY_CHECKS = 1;
Then yarn dev starts the development. If it is started for the first time, it may need to be in config / config local. JS modify the password configuration:
config.sequelize = { // Connect users username: 'root', // Connection password password: '', // The connected database can be changed into the existing database as needed database: 'template_node_egg', // Connection address host: '127.0.0.1', // Database type dialect: 'mysql' }
If the user table field in the existing database follows model / user The defined in JS is not correct and needs to be modified, otherwise an error will be reported
Application configuration
Customized application configuration, which takes effect globally after modification.
// Interface prefix name, which is modified with the business system const apiPrefixName = 'api' // Interface full prefix const apiPrefix = `/${apiPrefixName}` // The background interface prefix is modified with the business system const manageApiPrefixName = 'manage' // Background interface prefix const manageApiPrefix = `/${manageApiPrefixName}` const userConfig = { // The application name is used to specify the directory of log files and the key of cookie s. It is unique. The default is app Name, which can also be changed to other strings appName: app.name, apiPrefixName, apiPrefix, manageApiPrefixName, manageApiPrefix, // The default code and error message configuration only needs to be changed resCode: { // Successful code identification success: { code: 0 }, // Error code identification and prompt error: { code: 602, message: 'Parameter exception' }, // code identification and prompt of server exception serverError: { code: 500, message: 'Server exception' }, // Unregistered code ID and prompt notLogged: { code: 601, message: 'Please login before operation' } } }
Cookie configuration
For example, the built-in interface and the background service interface in the scaffold are not required. The middle and background interface services are optional. If you don't want to put them together, you can ignore them directly.
The application interface service uses CTX session. [keyname] to set and obtain values. For example, if you want to set the id field as a cookie, the way to set a cookie is:
ctx.session.id = 1
To get a cookie is:
ctx.service.user.info(ctx.session.id)
CTX is required to obtain the interface service in the middle and background cookies. Get (key, cookie) method. See here for specific usage ctx. Cookie usage document.
Security policy configuration
-
Eggis enabled by default The security policy provided by JS.
-
By default, cross domain is allowed for scaffolds, but this setting is turned off in the production environment, and the white list is automatically added, which makes it easier to start development:
// Front end port, modified according to the actual situation const port = 9001 const domainWhiteList = [ ...new Set([ `http://127.0.0.1:${port}`, `http://localhost:${port}`, // Attempt to automatically obtain the whitelist of local IP settings when the service is started `http://${getLocalhost()}:${port}` ]) ] config.security = { domainWhiteList } // Cross domain is allowed by default, and this setting is turned off in the production environment config.cors = { origin: '*', allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH' }
Permission configuration
The interface services with login system basically need to specify some interfaces that can be accessed without login. The scaffold has built-in white list interfaces related to login:
[ `${apiPrefix}/user/mock`, `${apiPrefix}/user/login`, `${apiPrefix}/user/logout`, `${apiPrefix}/user/phone` ]
Auto generate document configuration
Built in egg swagger doc plug-in, write interface notes according to the contract, and then automatically generate interface use documents. All data model structures are defined in app/contract.
See here for specific use documents Egg swagger doc usage document , I won't repeat it here.
If this function is not required, it can also be found in config / plugin Disable automatic document generation in JS:
{ swaggerdoc: { enable: false, package: 'egg-swagger-doc' } }
development
Generally, to develop a new interface, you need to complete the following four steps:
New model
Add a new log in app/model JS file and add data model:
'use strict' /** * Log table */ module.exports = app => { // Sequelize. Data type provided by JS const { STRING } = app.Sequelize const Log = app.model.define(/* Data sheet name */ 'log', { // Data table fields content: { // Field type type: STRING(255), // field comment comment: 'Operation content' }, remarks: { type: STRING(255), allowNull: false, comment: 'remarks' }, actionType: { type: STRING(255), comment: 'Operation type' } }) return Log }
New services
Add a log in app/service JS file and add a database operation method to get the list:
'use strict' const Service = require('egg').Service class LogService extends Service { // Get log list async logs(actionType) { const { ctx } = this try { const data = await ctx.model.Log.findAll({ where: { actionType } }) return ctx.helper.clone(data) } catch (error) { ctx.logger.error(error) } return false } } module.exports = LogService
New controller
In APP / contract / dto JS to add a logInfo model:
{ // ... logInfo: { content: { type: 'string', description: 'Operation content' }, remarks: { type: 'string', description: 'remarks' }, actionType: { type: 'string', description: 'Operation type' } } }
Then add a new log in app/controller JS file and add a method to get the list:
'use strict' const Controller = require('egg').Controller /** * @controller Log Log module */ class LogController extends Controller { /** * @summary Get operation record list * @description Query the user action list according to the specified filter criteria * @router get /log/logs * @response 0 logInfo Log list */ async logs() { const { ctx } = this const { actionType } = ctx.request.query // Parameter verification const rules = { actionType: { required: true, message: 'Operation type cannot be empty' } } const passed = await ctx.validate(rules, ctx.request.query) if (!passed) return const data = await ctx.service.log.logs(actionType) if (data) { this.ctx.helper.success(data) } else { this.ctx.helper.error(null, 'Log list failed') } } } module.exports = LogController
New interface
In APP / router JS adds a new routing interface:
// Get log list subRouter.get('/log/logs', controller.log.logs)
The above steps are simple, of course.
Of course, when adding an interface, you don't need to add another controller file. Basically, you add a method to a controller, and then add a database operation method to the service.
release
Before publishing, it needs to be in config / config Configure the database connection configuration in prod.js:
config.sequelize = { username: 'root', password: '', database: 'template_node_egg', host: '127.0.0.1', dialect: 'mysql' }
Start:
yarn start
stop it:
yarn stop
Using WY cli to create scaffolding
Global installation:
npm i @wytxer/wy-cli -g
Then execute the WY init project name command to install the scaffold, and select the background scaffold from the list:
❯ Backstage scaffold( Node.js + Egg.js + Sequelize)
Common commands of scaffold:
# install yarn install # Start development yarn dev # Production environment startup yarn start # Production environment stop yarn stop