我们在上一章《快速开始》中了解到了开发API必备的proto.ts的作用,在这一章我们需要实现具体的API啦,我们首先建立一个list文件夹到api下,然后在list文件夹下我们需要新建4个API,分别是增删改查。你可能会觉得:“什么?好麻烦啊,我不想手动建立那么多proto和index.ts”,在这里我们可以在dev环境下,去写便捷操作符,cli会检测到操作符并且自动给你生成模板,比如这样:

屏幕录制2022-04-10 下午2.19.34_.gif

Add

接着我们实现一下add接口,我们需要add接口为post请求,并且规定参数必须传递title(string),返回必须是success(boolean)

  1. export interface ReqParams {
  2. title: string;
  3. }
  4. export interface ReqQuery {
  5. }
  6. export interface Res {
  7. success: boolean;
  8. }
  1. import { useApi, Post } from "@swordjs/sword-framework";
  2. import { ReqQuery, ReqParams, Res } from "./proto";
  3. export const main = useApi<{
  4. query: ReqQuery;
  5. params: ReqParams;
  6. res: Res;
  7. }>({
  8. instruct: [Post()],
  9. handler: (ctx) => {
  10. return {
  11. success: true,
  12. };
  13. },
  14. });

Update

我们可以在handler去处理我们的add逻辑,比方说调用数据库的save方法等等…,接着我们同样实现一下update接口,update允许我们传递一个新的title和列表的id:

  1. export interface ReqParams {
  2. title: string;
  3. id: number
  4. }
  5. export interface ReqQuery {
  6. }
  7. export interface Res {
  8. success: boolean;
  9. }
  1. import { useApi, Put} from "@swordjs/sword-framework";
  2. import { ReqQuery, ReqParams, Res } from "./proto";
  3. export const main = useApi<{
  4. query: ReqQuery;
  5. params: ReqParams;
  6. res: Res;
  7. }>({
  8. instruct: [Put()],
  9. handler: (ctx) => {
  10. return {
  11. success: true,
  12. };
  13. },
  14. });

Delete

删除接口,我们只需要一个id作为参数就可以了:

  1. export interface ReqParams {
  2. id: number;
  3. }
  4. export interface ReqQuery {
  5. }
  6. export interface Res {
  7. success: boolean;
  8. }
  1. import { useApi, Delete } from "@swordjs/sword-framework";
  2. import { ReqQuery, ReqParams, Res } from "./proto";
  3. export const main = useApi<{
  4. query: ReqQuery;
  5. params: ReqParams;
  6. res: Res;
  7. }>({
  8. instruct: [Delete()],
  9. handler: (ctx) => {
  10. return {
  11. success: true,
  12. };
  13. },
  14. });

Query

查询接口,我们希望用户可以用2种方法传递参数,第一种传递方式就是limit/offset,第二种传递方式就是直接传一个page页数,所以需要我们的proto更复杂一点:

  1. export type ReqParams = {
  2. page: number;
  3. } | {
  4. offset: number;
  5. limit: number;
  6. }
  7. export interface ReqQuery {
  8. }
  9. export interface Res {
  10. list: unknown[];
  11. }

我们导出了一个类型别名ReqParams,然后我们就可以在接口中,用上下文直接获取到这些参数,但是请注意当类型别名有多个类型选项时,是没有IDE类型提示的,但是框架的类型校验仍然是有效的:

  1. import { useApi } from "@swordjs/sword-framework";
  2. import { ReqQuery, ReqParams, Res } from "./proto";
  3. export const main = useApi<{
  4. query: ReqQuery;
  5. params: ReqParams;
  6. res: Res;
  7. }>({
  8. handler: (ctx) => {
  9. return {
  10. list: ["test"],
  11. };
  12. },
  13. });

结束

你可以发现,得益于proto,我们编写的api不用再去校验数据的基本类型,它可以确保你的api实现中的参数是安全可靠的,也能给客户端保证每个接口的返回都是严格类型的,解决了很多前后端联调出现的问题。而且我们把proto单独实现,而不和我们的api耦合也是有原因的:

  • 不和业务耦合
  • 对之后的proto -> api文档,更友好,可以单独的去解析ast
  • 可以和前端共享,甚至是生成前端的api代码(类似TSRPC)

Sword.js的类型系统使用了TSRPC的类型系统核心,它不仅支持ts的基本数据类型,还支持ArrayBuffer、Date和一些工具函数,比如Pick, Partial等等