前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过 Serverless Components Koa 构建后台服务

通过 Serverless Components Koa 构建后台服务

原创
作者头像
lqmeta
发布2020-07-06 01:13:38
1.1K0
发布2020-07-06 01:13:38
举报
文章被收录于专栏:得一

Serverless Components 是支持多个云资源编排和组织的场景化解决方案,主要基于客户的具体场景,如 Express 框架支持、网站部署等。Serverless Components 可以有效简化云资源的配置和管理,将网关、COS 和 CAM 等产品联动起来,让客户更多关注场景和业务。

本文将讲解如何使用 Serverless Components Koa 快速构建一个后台接口服务,并验收新建、查询用户等功能。

项目的框架结构:

代码语言:txt
复制
koa-backend-demo
├── config
│   ├── database
│   │   └── mysql.js
│   └── index.js
├── controller.js
├── controllers
│   └── User
│       ├── User.js
│       └── index.js
├── models
│   └── UserModel.js
├── package.json
├── serverless.yml
├── sls.js
└── utils
    ├── ApiResponse
    │   └── ApiResponse.js
    └── utils.js

配置 serverless.yml

代码语言:txt
复制
# serverless.yml

component: koa # (required) name of the component. In that case, it's koa.
name: koa-backend-demo # (required) name of your koa component instance.

inputs:
  src:
    src: ./ # (optional) path to the source folder. 
    exclude:
      - .env
  region: ap-guangzhou
  runtime: Nodejs12.16
  apigatewayConf:
    protocols:
      - http
      - https
    environment: release

新建 sls.js

代码语言:txt
复制
const Koa = require('koa');
const router = require('koa-router')();
const controller = require('./controller');
const app = new Koa();

// x-response-time 响应时间
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});

// 配置路由
app.use(controller());

module.exports = app;

新建 controller.js

controller.js 用于遍历 controllers 文件夹下的文件去配置接口路由。

代码语言:txt
复制
const fs = require('fs');
const config = require('./config/index.js');

function addMapping(router, mapping) {
	for (let url in mapping) {
		if (url.startsWith('GET ')) {
			let path = config.prefix + url.substring(4);
			router.get(path, mapping[url]);
		} else if (url.startsWith('POST ')) {
			let path = config.prefix + url.substring(5);
			router.post(path, mapping[url]);
		} else {
			console.error(`invalid URL: ${url}`);
		}
	}
}

function addControllers(router, dir) {
  let files = fs.readdirSync(__dirname + '/' + dir);
  let js_files = [];
  for (let i in files) {
    js_files.push(`${files[i]}/index.js`);
  }

	for (let f of js_files) {
		console.log(`process controller: ${f}...`);
		let mapping = require(__dirname + '/' + dir + '/' + f);
		addMapping(router, mapping);
	}
}
module.exports = function (dir) {
	let controllers_dir = dir || 'controllers';
	let router = require('koa-router')();
	addControllers(router, controllers_dir);
	return router.routes();
};

config 配置文件夹

config/index.js

配置接口前缀

代码语言:txt
复制
'use strict'

module.exports = {
	prefix: '/api',
}

config/database/mysql.js 数据库配置

代码语言:txt
复制
'use strict'

const mysql = require('mysql2');

const MYSQL_CONFIG = {
  HOST: 'xxxxxxxxxxxx.gz.tencentcdb.com', // 腾讯云数据库
  PORT: '3306',
  DATABASE: 'mydata',
  USERNAME: 'root',
  PASSWORD: 'xxxxxxxx'
};

// init mysql connection
const initMysqlPool = () => {
  const promisePool = mysql
    .createPool({
      host: MYSQL_CONFIG.HOST,
      user: MYSQL_CONFIG.USERNAME,
      port: MYSQL_CONFIG.PORT,
      password: MYSQL_CONFIG.PASSWORD,
      database: MYSQL_CONFIG.DATABASE,
      connectionLimit: 1,
    })
    .promise();

  return promisePool;
}

module.exports = {
  MYSQL_CONFIG,
  initMysqlPool,
}

备注:此处需要配置远程连接 MySQL 的配置信息(即 MYSQL_CONFIG)

新建 models 文件夹

通过 UserModel.js 操作数据库

代码语言:txt
复制
const { initMysqlPool } = require('./../config/database/mysql');
const pool = initMysqlPool();

class UserModel {
  constructor({
    id = 0,
    name = '',
    email = '',
    site = '',
  }) {
    this.id = id;
    this.name = name;
    this.email = email;
    this.site = site;
  }

  // 注册用户
  insertData({
    name = '',
    email = '',
    site = '',
  }) {
    return pool.query('INSERT into users SET ?', {
      name,
      email,
      site,
    })
  }

  // 查找所有用户
  findAllUsers() {
    return pool.query('SELECT * FROM users');
  }

  // 通过名字查找用户信息
  findDataByName( name ) {
    return pool.query(
      'SELECT * FROM users WHERE name = ?',
      [
        name,
      ]
    )
  }
}

module.exports = UserModel;

controllers 控制器

controllers/User/index.js

用于定义接口路由:

代码语言:txt
复制
'use strict'

const User = require('./User');
const user = new User();

module.exports = {
  'GET /user/get-user-by-name': user.getUserByName.bind(user),
  'GET /user/get-user-list': user.getUserList.bind(user),
  'GET /user/create-user': user.createUser.bind(user),
}

备注: /user/create-user 接口建议使用 POST 请求(即 'POST /user/create-user': user.createUser.bind(user) 定义),此处为了演示方便定义为 GET 请求。

controllers/User/User.js

业务逻辑

代码语言:txt
复制
'use strict';

const ApiResponse = require('./../../utils/ApiResponse/ApiResponse');
const { awaitWrap } = require('./../../utils/utils');
const UserModel = require('./../../models/UserModel');

const userModel = new UserModel({});

class User {

  async getUserList(ctx, next) {
    const res = new ApiResponse({});

    const [data, err] = await awaitWrap(
      userModel.findAllUsers()
    )
    if (err) {
      res.errDetail = err;
      ctx.body = res;
      return false;
    }

    const [userData] = data;

    res.code = 0;
    res.msg = '请求成功';
    res.data = userData;
    ctx.body = res;
  }

  async createUser(ctx, next) {
    const res = new ApiResponse({});

    const name = ctx.request.query.name || "";
    const email = ctx.request.query.email || "";
    const site = ctx.request.query.site || "";

    const userData = await this._getUserByName(name);
    if (
      userData.code === 0 &&userData.data &&
      userData.data.length > 0
    ) {
      res.code = 0;
      res.msg = '已存在该用户名';
      res.data = userData.data;
      ctx.body = res;
      return true;
    }
    
    const [data, err] = await awaitWrap(
      userModel.insertData({
        name,
        email,
        site,
      })
    );
    if (err) {
      res.errDetail = err;
      ctx.body = res;
      return false;
    }

    const [insertData] = data;

    res.code = 0;
    res.msg = '请求成功';
    res.data = [
      new UserModel({
        id: insertData.insertId,
        name: name,
        email: email,
        site: site,
      })
    ]

    ctx.body = res;
  }

  async _getUserByName(name) {
    const res = new ApiResponse({});

    if (!name) {
      res.msg = '用户姓名不能为空';
      return res;
    }

    const [data, err] = await awaitWrap(
      userModel.findDataByName(name)
    );
    if (err) {
      res.errDetail = err;
      return res;
    }

    const [userData] = data;
    if (userData.length > 0) {
      res.code = 0;
      res.msg = '请求成功';
      res.data = userData;
    } else {
      res.code = 0;
      res.msg = '用户信息不存在';
      res.data = []
    }

    return res;
  }

  async getUserByName(ctx, next) {
    const name = ctx.request.query.name || "";
    const res = await this._getUserByName(name);

    ctx.body = res;
  }
}

module.exports = User;

utils 工具函数

utils/ApiResponse/ApiResponse.js

接口响应的数据结构

代码语言:txt
复制
'use strict'

class ApiResponse {
  constructor({
    code = 500,
    data = {},
    msg = '系统异常',
    errDetail = '',
  }) {
		this.code = code;
		this.data = data;
		this.msg = msg;
		this.errDetail = errDetail;
	}
}

module.exports = ApiResponse;

utils/utils.js

提供 awaitWrap 函数捕获 promise 报错

代码语言:txt
复制
/**
 * @param {*} promise 
 * @returns [data, err]
 */
const awaitWrap = (promise) => {
  return promise
    .then(data => [data, null])
    .catch(err => [null, err]);
}

module.exports = {
  awaitWrap,
}

package.json

通过 npm init 新建 package.json ,安装项目依赖包:

代码语言:txt
复制
npm i --save koa koa-router mysql2

使用 sls deploy 部署

代码语言:txt
复制
sls deploy

若未有 .env 配置文件,则通过微信扫码登录授权。

sls deploy 部署
sls deploy 部署

如上图所示,kao 接口服务的请求 url 是 https://service-gwlsee6c-1253513412.gz.apigw.tencentcs.com/release

我们刚刚写了三个接口,分别为:

新增用户接口:

https://service-gwlsee6c-1253513412.gz.apigw.tencentcs.com/release/api/user/create-user?name=xiaoming&email=1234566@qq.com&site=F

新增用户接口
新增用户接口

通过名字查找用户信息:

https://service-gwlsee6c-1253513412.gz.apigw.tencentcs.com/release/api/user/get-user-by-name?name=xiaoming

查找所有用户:

https://service-gwlsee6c-1253513412.gz.apigw.tencentcs.com/release/api/user/get-user-list

查找所有用户
查找所有用户

项目代码:

通过 Serverless Components Koa 构建后台服务

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 配置 serverless.yml
  • 新建 sls.js
  • 新建 controller.js
  • config 配置文件夹
    • config/index.js
      • config/database/mysql.js 数据库配置
      • 新建 models 文件夹
      • controllers 控制器
        • controllers/User/index.js
          • controllers/User/User.js
          • utils 工具函数
            • utils/ApiResponse/ApiResponse.js
              • utils/utils.js
              • package.json
              • 使用 sls deploy 部署
              相关产品与服务
              云数据库 MySQL
              腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档