0%

使用ts版的eggjs来搭建api

前面也用egg写过一些api服务,但都不是ts版的,在默认提示上感觉很不爽,正好这次egg.js已经有了ts的版本,那么我们直接来用ts版的egg来构建我们的api服务。

创建项目

我们按照egg.js文档的步骤来用脚手架创建项目。

首先安装egg-init,这个是创建项目的工具:

1
npm i egg-init -g

当然,如果我们已经存在了项目的文件夹的话,就用渐进式安装就可以了。

这里我们直接用egg-init来创建:

1
2
3
4
5
6
7
8
9
10
11
12
T:myobj tonyyang$ egg-init my-api
[egg-init] use registry: https://registry.npmjs.org
[egg-init] target dir is /Users/my-api
[egg-init] fetching npm info of egg-init-config
? Please select a boilerplate type (Use arrow keys)
──────────────
❯ simple - Simple egg app boilerplate
sequelize - egg app with sequelize
ts - Simple egg && typescript app boilerplate
empty - Empty egg app boilerplate
plugin - egg plugin boilerplate
framework - egg framework boilerplate

可以看到,在命令行里面会出现一个选项让我们选择创建的项目的类型,这里我们选择用ts,选择第二个就好了。然后一路回车,这样我们项目就创建好了。

egg-init只是帮我们创建了项目的骨架以及一些通用配置,我们需要npm i一下,然后就可以运行起来:

1
npm run dev

跑起来后,我们可以访问http://localhost:7001,可以看到接口的返回:hi, egg。目前为止,项目创建完毕,也正常运行。

项目目录

我们来看看创建好的项目目录:

  • app 应用文件夹
    • controller 处理用户请求
    • public 放置静态资源文件(可选)
    • service 编写业务逻辑(可选)
    • model 数据库领域模型(可选)
    • middleware 编写中间件(可选
    • router.ts 定义路由的文件,可以扩展为路由文件夹,但是要在这个js中引入
  • config 配置文件
    • config.{env}.ts 不同环境的不同配置文件
    • plugin.ts 管理插件
  • logs 日志
  • run 运行时文件
  • test 单元测试文件
  • typing ts类型支持
  • tslint.json 代码验证规则

通过脚手架创建的时候app里面是不带model文件夹的,因为我们需要使用Sequelize这个ORM框架,所以我们在app下面手动创建model文件夹。

在项目中安装Sequelize

Sequelize是一个ORM框架,我们用它来管理数据层的代码。

需要在机器上安装mysql并让它正常运行,我们需要配置参数,比如数据库的密码和ip等。

  • 安装插件egg-sequelizemysql2
    1
    npm i --save egg-sequelize mysql2
  • config/plugin.js中引入egg-sequelize插件
    1
    2
    3
    4
    5
    6
    const plugin: EggPlugin = {
    sequelize: {
    enable: true,
    package: 'egg-sequelize',
    },
    };
  • config/config.default.ts中配置sequelize
    1
    2
    3
    4
    5
    6
    7
    8
    config.sequelize = {
    dialect: 'mysql',
    host: 'ip...',
    port: 3306,
    password: 'password',
    database: 'database',
    timezone: '+08:00',
    };
    注意,确保数据库的配置是正确的并可以访问到数据库,要不然egg会崩掉。如果不知道是什么问题崩掉的,那么重启egg就可以看到报错信息。

    使用model来操作数据库

    Sequelize都配置好了,我们需要建立数据模型来让项目有操作数据库的能力。

    定义model

    首先我们在数据库中创建好表:hero,然后我们建立hero的模型:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // app/model/hero.ts
    import {Application} from 'egg';
    export default (app: Application) => {
    const {STRING, INTEGER, DATE} = app.Sequelize;
    const model = app.model.define('heroes', {
    id: { type: INTEGER, primaryKey: true, autoIncrement: true },
    name: STRING(40),
    created_at: DATE,
    updated_at: DATE,
    });
    return model;
    };
    观察typings/app文件夹可以看到,当我们创建model的时候,会自动生成对应的index.d.ts文件,这是为了方便支持ts的提示,不要修改这个ts文件。

    使用model

    定义了类型HeroInterface
    1
    2
    3
    4
    5
    6
    export interface HeroInterface {
    id: number;
    name: string;
    created_at: string;
    updated_at: string;
    }
    定义了请求返回的数据格式Code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    export const Code = {
    ERROR: {
    code: 1,
    msg: 'failed',
    },
    SUCCESS: {
    code: 0,
    msg: 'success',
    },
    然后在service里面调用model:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // app/service/hero.ts
    import {Service} from 'egg';
    import {HeroInterface} from '../interface/hero.interface';
    import {Code} from '../util/util';
    export default class Hero extends Service {
    public async list() {
    const {ctx} = this;
    const result: any = await ctx.model.Hero.all({});
    return Object.assign({}, Code.SUCCESS, {
    data: result,
    });
    }
    }
    然后在controller中使用service:
    1
    2
    3
    4
    5
    6
    7
    8
    // app/controller/hero.ts
    import {Controller} from 'egg';
    export default class Hero extends Controller {
    public async list() {
    const {ctx} = this;
    ctx.body = await ctx.service.hero.list();
    }
    }
    最后在路由中添加访问路径:
    1
    2
    3
    4
    5
    6
    // app/router.ts
    import { Application } from 'egg';
    export default (app: Application) => {
    const { controller, router } = app;
    router.get('/hero/list', controller.hero.list);
    }
    在浏览器中进行访问(或者调用api)就可以看到我们从数据库中取到的数据:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    "code": 0,
    "msg": "success",
    "data": [
    {
    "id": 13,
    "name": "Iron Man",
    "created_at": "2018-09-28T05:35:46.000Z",
    "updated_at": "2018-09-28T05:35:46.000Z"
    }
    ]
    }

    跨域支持

    我们创建的项目是用来当api的,那么实际情况下是通过跨域调用的,也就是说,客户端和api是不同的域,这种情况也需要支持,目前来讲,是无法调用成功的,我们需要添加跨域支持

跨域我们需要用到插件egg-cors,先在我们的项目中安装:

1
npm i egg-cors --save

plugin.ts中启用跨域:

1
2
3
4
5
6
7
8
9
10
11
12
import {EggPlugin} from 'egg';
const plugin: EggPlugin = {
sequelize: {
enable: true,
package: 'egg-sequelize',
},
cors: {
enable: true,
package: 'egg-cors',
},
};
export default plugin;

然后在app/config/config.default.ts中添加白名单访问列表:

1
2
3
4
5
6
7
8
config.security = {
domainWhiteList: [
'localhost:4200',
],
csrf: {
enable: false,
},
};

同时,需要将config.cors中的origin不可设置为*

1
2
3
4
5
config.cors = {
// origin: '*',
credentials: true,
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
};

最后

ok,大功告成,基本的项目框架完成;数据库访问功能完成;跨域访问完成;然后就可以添加自己的业务了,have fun~ 😊

码字辛苦,打赏个咖啡☕️可好?💘