preface
Wechat provides cloud functions for small program developers as a serverless solution to replace the backend. Wechat cloud function and wechat small program front end function are closely combined, providing considerable convenience for developers. However, cloud functions are different from traditional server-side development in design, development, and debugging.
Our team recently developed an applet product. In order to reduce the use of server to a greater extent, the business background completely uses cloud functions to provide services (mainly because our team does not have a professional backend -_-). In addition, eggJS + Vue, which is familiar to front-end children’s shoes, is used to make a set of simple management background for auditing and data management.
The system is divided into small program end, cloud function end, Web end. Database A database that uses cloud services.
I. General interface definition for cloud function call
1, the cloud
We have encapsulated a generic runFunc method in the cloud, which all cloud functions follow and then go into the corresponding function according to the fname.
RunFunc basic inputs:
attribute | meaning |
---|---|
fname | Custom cloud function name |
clientType | Applets/Web side |
version | Version information for update intervention control when necessary |
token | The user token |
adminOpenId | The OpenID of a Web user cannot be automatically obtained and must be imported manually |
RunFunc’s basic processing flow makes a sequence of judgments:
Code:
//cloudfunctions/runFunc/index.js async function main(event, context) { let {fname, version, token, clientType, adminOpenId, ... Opt} = event let result, role const {OPENID} = cloud.getwxContext () fname){ return {code: If (fname==='getToken'){return await getToken(cloud, db)} If (semver.lt(version, LOWEST_VERSION)) {return {code: '0602', info: 'The current version is too low! '}} const authResult = authCheck(token) role = authResult.role // Check token if(! Token | | authResult. Unauthorized) {return {code: '0401', the info: 'user state failure, data: {role}} } } else { role = opt.role } switch(fname){ case 'getList': return await getList() //... Default: return {code: '0404', info:' not found '}}}Copy the code
2, small program end
On the applet side, we also encapsulate a cloud function call method that can focus on handling standard cloud function call errors.
Unfortunately, the official document does not provide the standard definition of return value of cloud function. Through debugging, it is found that cloud function call usually throws errCode, errMsg, requestId, result and other values. Normally, if errCode is empty, the call will run normally. But some methods fail and do not return errCode. On the official BBS questions also have no reply, we can only through errCode | |! /ok/.test(errMsg) to determine if the execution failed.
//miniprogram/utils/api.js/callCloud const app = getApp() async function callCloud(fname, opt){ try{ const {errCode, errMsg, requestId, result} = await wx.cloud.callFunction({ name: 'runFunc', data: { fname, clientType: 0, version: app.globalData.appVersion, token: app.globalData.token, ... opt } }) if(errCode || ! /ok/.test(errMsg)){wx.showtoast ({title: 'server error ', icon: 'none', duration: 2000}) return {code: '0700', info: 'Server busy ', subcode: errCode, subinfo: errMsg} } else { const {code, info, data} = result if(code === '0401'){ wx.showToast({ title: info, duration: 2000})} if (code = = = '0602') {/ / todo: be forced to upgrade code wx. ShowToast ({title: info | | 'to forced to upgrade, icon:' none ', duration: 2000 }) } if(code! . = = '0000') {wx showToast ({title: info | | 'error' icon: 'none', duration: 2000 }) } return result } } catch(e){ return {code: '0500', info: E.m essage | | 'server busy'}}} exports. CallCloud = callCloudCopy the code
In the page js, call the cloud function:
//miniprogram/pages/demo/demo.js const api = require('.. /.. /utils/api') Page({ data:{///... }, onLoad: async function(){ const {code, info} = await api.callCloud('getList') //... }})Copy the code
3. Web – interface
On the Web server side, we provide a generic route for calling cloud functions
//router.js
router.post('/call/:funcname', jwt, controller.home.callCloudFunction)
Copy the code
In controller we send the following URL request:
POST https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=ACCESS_TOKEN&env=ENV&name=FUNCTION_NAME
Copy the code
//home.controller const url = util.format('https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=%s&env=%s&name=%s', access_token, cloudEnv, functionName) const { data: optData, ... restOpt } = opt const result = await ctx.curl(url, { method: 'POST', contentType: 'json', dataType: 'json', data: { clientType: 1, adminOpenId: ctx.state.user && ctx.state.user.openId ? ctx.state.user.openId : '', role: ctx.state.user && ctx.state.user.role ? parseInt(ctx.state.user.role) : 0, ... optData }, ... RestOpt}) const {errcode, errmsg, resp_data} = result.data /* errcode is null, return JSON. Parse (resp_data) errcode is null, Return the corresponding errmsg */Copy the code
4. Web end – front end
Accordingly, the call to the Web front end is very simple:
const {code, info, data} = await axios('/call/runFunc', { data: {fname: functionName, options} })
Copy the code
In this way we ensure that the data structure of the cloud function and the error code are uniform on all three ends.
Second, about the user status
Regular Web projects or APP projects use tokens to maintain user login status. One of the advantages of using cloud functions in applets is that there is no need to write the login process. Each request of the applets can easily obtain the user’s OpenID through the method cloud.getwxContext () in the cloud, as follows:
const { OPENID } = cloud.getWXContext()
Copy the code
Do you only need OpenID and no longer need token in applets? I still recommend using tokens to identify user validity, since tokens can carry information such as user role \ login time for finer business control.
// const token = encode({t: now, r: role})Copy the code
If the token does not exist or becomes invalid after a user enters the home page, the user can update the token by invoking the getToken method in the cloud and add or update the user record in the User table.
// If there is no token, fetch the token if(! app.globalData.token){ app.globalData.token = await api.callCloud('getToken') }Copy the code
Api.callcloud is a generic way to customize cloud functions described earlier.
Async function getToken(cloud, db){ try{ const { OPENID } = cloud.getWXContext() let result = await db.collection('user').where({openId: OPENID}).get() // If the user already exists in the user table, If (result.data && result.data.length>0){await db.collection('user'). Where ({openId: openId}).update({data: {latestTime: now}}) token = encode({d: now, r: record.role}) return {code: '0000', info: 'success ', data: {token, openId: OPENID, role: record.role}} } } else{ let record = { openId: OPENID, firstTime: now, latestTime: Add ({data: record}) token = encode({d: data: record}) now, role: 0} // Add ({data: record}) token = encode({d: data: record}) Now, r:0}) return {code: '0000', info: 'successful ', data: {token, openId: openId, role: 0}}}} catch(e){return {code: '0500', the info: e.m essage | | 'system error'}}}Copy the code
In this way, the OpenID of the user can also be brought into the Web side and the user system can be the same as that of the small program side. For details, see “Small Program Scanning Code Logging in to the Web Side”.