Prologue: Nodejs is hot, but it’s hard to see a lot of job postings at work. I have also used NodeJS to write Web services for many years. I feel good about sharing a set of code that I’ve been using for a long time. If you need it, take a look at it. We don’t have to worry about whether NodeJS is good or not. I wish I could solve the problem.

Fast development is also a kind of advantage, now small and medium-sized start-up companies, poor foundation, fast business requirements. Plus the API support of cloud service providers, why do we have to maintain a cumbersome service system? When I need the AI service, I just plug into the INTERFACE of the AI service provider. When I need THE CDN service, I will connect to the cloud service. When I need to pay, I will call the payment service API. Then a small, fast business solution will allow start-ups to quickly spread the market, two to three months can be quickly online. Especially in the prevalence of small programs today, there is no need for bulky APPS, cloud development is also a good choice, but enterprises always have a certain amount of data and business capabilities, or they will be hampered by cloud developers.

Today’s share is a set of code, will be posted on Github, github.com/vincent-li/… You also need to adjust the configuration file for your project. I dare not say it is very awesome, I have always been a local dog, if you have no idea of friends, is it possible to take it directly to do business first? Without further ado. The code goes.

The idea is relatively simple and I will disassemble it step by step. I see NodeJS as a Web service application. Don’t talk to me about the underlying stability issues, because I’ve solved them all with cloud services. The service framework uses KOA and the database is mongodb, so let’s talk about what a Web service really needs.

  • At its most basic, web services receive requests, query databases, and return results to the client. This kind of functionality is done under controllers.
  • Upload a file, use a plugin multer, www.npmjs.com/package/mul…
  • Static file services, which I will mention, should not be written on the server. Having said that, NodeJS still has limited capabilities. Use CDN interface.
  • Login authentication logic, using session. Some people say why not use token, the same meaning, just put in a different position.
  • Log analysis, using the log capabilities of daemon Pm2, more on this later.

I’m talking about a bunch of people who probably haven’t done it before, but don’t worry, just look at the code. Entry index.js file

const Koa = require('koa') const koaBodyParser = require('koa-bodyparser') const koaViews = require('koa-views') const koaStatic = require('koa-static') const koaSession = require('koa-session') const mongoStore = require('koa-session-mongo2') const koaLogger = require('koa-logger') const _config = require('.. /config') const router = require('./controllers') global._ = require('lodash') global.model = require('./models') const Use (koaLogger()) // Specify the static directory app.use(koaStatic(_config.static)) // Specify the views path app.use( KoaViews (_config.views, {map: {HTML: 'lodash',},})) use(koaBodyParser({enableTypes: ['json', 'form', 'text'],})) // Session initialization app. Use (koaSession({key: 'makefuture_sess', store: New mongoStore(_config.sessionURL), signed: false, // Cookie expire time, then clear, in milliseconds maxAge: Router app.use(router.routes(), router.allowedmethods ()) console.log(' boot port: ', _config.port) app.listen(_config.port)Copy the code

The js entry is the koA and a bunch of koA plug-ins that are then assembled according to the API.

Since projects have various environments, load different configuration items depending on the startup environment. const _config = require(‘.. /config’), the configuration items included some sensitive database connection accounts and passwords, which I replaced. Therefore, it is not recommended to upload the production configuration to GitLab during development. It is better to maintain and configure it.

Const port = Number. ParseInt (process. The env. Port) | | 6060 const mongoUri = 'mongo: / / the abcd: * * * * * * * * * * * * @ 0.0.0.0:3717 /? authSource=admin' const root = '/Users/liwenqiang/codespace/git.ichoice.cc/makefuture/make' const host = 'http://localhost:6060' module.exports = { host, port, cdn: 'https://cdn.izelas.run', hostName: 'makefuture.app', home: root, static: `${root}/static`, views: `${root}/src/views`, mongoUri, dbName: 'makeFuture ', // store session library and table configuration sessionURL: {url: `${mongoUri}&poolSize=5&useUnifiedTopology=true&useNewUrlParser=true`, db: 'makefuture', collection: 'session', // this is set to the time when the database session is periodically cleared, which should be consistent with the expiration time of the cookie. // Cookies are periodically cleared by the browser. It should be noted that the old index should be deleted once the index is created and modified. MaxAge: 24 * 60 * 60,}}Copy the code

Use Global to add common libraries to your development architecture so you don’t have to refer to them over and over again. I’m going to introduce LoDash here, and I think loDash’s method is good enough for us. There is also crypto. In fact, for a long time, all development based on business is an assembly of various mature components. The solutions are very mature, so I don’t think the difficult business, at least the difficult is not the technology, but the product form.

Models can be initialized independently of koA and does not need to be included in the entire KOA request context, but is frequently used in business processes, so it should be initialized in Global and available whenever possible.

const mongoose = require('mongoose') const { mongoUri, dbName } = require('.. /.. /config') mongoose.Promise = global.Promise const conn = mongoose.createConnection(mongoUri, { dbName, useNewUrlParser: true, useUnifiedTopology: true, }) const db = {} const sysinfo = require('./_sysinfo') const user = require('./_user') const project = require('./_project') const page = require('./_page') const component = require('./_component') const file = require('./_file') const models = [sysinfo, user, project, page, component, file] models.forEach((item) => { let newSchema = new mongoose.Schema( (typeof item.schema === 'function' && item.schema(mongoose.Schema)) || item.schema, { collection: item.name } ) db[item.name] = conn.model(item.name, newSchema) }) module.exports = dbCopy the code

Simple don’t don’t of, is the config configuration inside connection string, on the component method, mongoose. CreateConnection, mongoose is to use better directing a connected component, each database has a corresponding js help do in operation. I prefer mongodb because it perfectly fits with JSON structure, is relatively consistent in understanding, and basically conforms to JS operation specifications. Not having to write SQL is really comfortable. So if you look at any data object, you’ll see.

Const model = {name: 'user', schema: {nick_name: String, // phone: String, // phone: String, // password avatar_URL: String, // avatar sms_code: Number, // SMS login code expire_at: Number, // Verification code expiration time create_AT: Module. Exports = modelCopy the code

Name is the name of the corresponding table in the database, which is also the name of the call. It can be inconsistent with the file name. I’ll call it _user,name: user, and when I call it, it’s model.user, which is very convenient. The field names and types correspond to mongo’s collection, which is a table. Can also with object and function, specific operation can go to www.npmjs.com/package/mon… Official operation methods of Mongo are supported, depending on the version of Mongoose and mongodb. Official operation methods can be used. After creating the model, you can call the method with await Model.*. Async (async); async (async); async (async); async (async); async (async); No, it’s syntactically supported and not hard to understand.

Nodejs is used for static rendering. The purpose is to inject data into the page, so it is much easier to do the js entry by yourself. I’m going to talk about the session section, because this diagram is a little bit easier, so if you don’t know the concept of session, please read the related materials. The session module here does several things.

  • Inject a session object in the KOA CTX context, ctx.session = {}, and put all the login information in it. Such as
 bo = {
    userid: u._id.toString(),
    nick_name: u.nick_name,
    avatar_url: u.avatar_url,
  }
  ctx.session = bo
Copy the code
  • Automatically stores the data in ctx.session to the specified database, in this case mongodb, so that login information is not lost due to service outages.
  • Clear session information after a session timeout according to the configuration.

So we just need to make a global filter and check ctx.session for login information every time. For example, if the userID is empty, it will redirect directly to the login page.

Now there’s only one left to handle requests, which is the big story of Web services. Using the BodyParse plug-in, we filtered all the requests and sorted the request parameters into JSON objects.

Use (koaBodyParser({enableTypes: ['json', 'form', 'text'],}))Copy the code

It can then be retrieved from ctx.request.query or ctx.request.body. Ctx.request. query represents the parameters of the GET request. Ctx.request. body stores the POST request parameters. You can also handle head, PUT, and delete requests. Actually, I don’t think so. See for yourselves.

/** * const Router = require('@koa/ Router ') const Router = Router({prefix: '/ API /user',}) /** * The user login send short messages in the cell phone * @ path - / API/user/smscode * * @ @ method - POST params mobile number * @ returns * * phone - users data - string | object Return data * code 0 | | 500, * success - true | false, * message - '* / router. Post ('/smscode' sendSmsCode)Copy the code

The above is a route definition method, prefix indicates the uniform prefix. Post is the name of the method, which means that an interface is initialized. The access path is/API /user/smscode, and the corresponding execution method is sendSmsCode, which is an interface for sending SMS verification codes to the client. Of course, I don’t need to write the text message, just use the cloud service directly. I use ali cloud SMS interface, sent 66.

const sendSmsCode = async (ctx) => { const { phone } = ctx.request.body if (! phone) { ctx.body = getResponse(false, 'e501') return } if (! checkPhone(phone)) { ctx.body = getResponse(false, 'e552') return} let u = await model.user.findone ({phone}).lean() // If (u && u.sms_code && U.xpire_at && u.expire_at > Date.now()) { ctx.body = getResponse(false, 'e553') return} let aliyun = await model.sysinfo.findOne({key: 'aliyun' }).lean() if (aliyun && aliyun.val.accessKeyId && aliyun.val.accessSecret) { const client = new Alicloud({ accessKeyId: aliyun.val.accessKeyId, accessKeySecret: aliyun.val.accessSecret, endpoint: 'https://dysmsapi.aliyuncs.com', apiVersion: '2017-05-25', }) / / for random digital const code = 6 + ` ${_. Random (9)} ${_. Random (9)} ${_. Random (9)} ${_. Random (9)} ${_. Random (9)} ${_. Random (9)} ` Const params = {RegionId: 'cn- Hangzhou ', PhoneNumbers: phone, SignName: 'cn-hangzhou', TemplateCode: 'SMS_203670207', TemplateParam: JSON.stringify({ code }), } const res = await client.request('SendSms', params, { method: 'POST' }) if (res && res.Code === 'OK') { if (u) { await model.user.updateOne( { phone }, { sms_code: code, expire_at: Date.now() + 15 * 60 * 1000})} else {await model.user.create({nick_name: 'code ', phone, create_at: Date.now(), sms_code: code, expire_at: Date.now() + 15 * 60 * 1000,})} ctx.body = getResponse(true, 'yes ')} else {// FMTP (' no ') ', res.message) console.log(' Ali cloud SMS failed to send, cause: ', res.Message) ctx.body = getResponse(false, 'e500') } } else { fmtp('aliyun --->', aliyun.val) ctx.body = getResponse(false, 'e500') } return }Copy the code

I want you to look at the code, I don’t want to talk too much about it, but I mentioned a lot of things. This code basically can run, but, I uploaded the code is modified key information, if you want to connect to the database to enter their own database parameters. See the configuration file for details. Hopefully, you’re good at coding and troubleshooting some of the problems yourself. In fact, the business layer, according to the business development of many companies are different, need different code, or in the same words, product form determines the code form.

The essence is, standing on the shoulder of cloud manufacturers, quickly put their business landing, AI I like to connect baidu, voice analysis to IFLYtek, server, storage, cloud database, SMS to Ali cloud, IM generally connected to Tencent, after all, small program scene or wechat ecological stability. My entrepreneurial experience is that the small program (wechat) +Nodejs service + Aliyun enterprise service + Tencent intelligent service, the dry business of Ma Slip, the reason why did not succeed is the f * * king business environment.

You see the officer think carefully, in the fierce competition in cloud service providers today, gave a lot of small and beautiful enterprises vitality. In the past, the things we wanted to do required a variety of talents, time and effort, and the key may not be good. Now most are to call the existing cloud services, our small business to do their own business, convenient and fast. And I don’t have to talk about pressure or stability. I solved all the security and stability problems by using the cloud service capabilities. Is!!! I don’t know anything about the bottom line, but that doesn’t stop us from being the backbone of an enterprise. Not necessarily worse than some people, many people who focus on technology, not only do not solve the problems of the enterprise, but also increase the burden of the enterprise, and finally say that the enterprise has nothing here and nothing there. What’s the problem? A couple of whining words, you can see.