Hello World API 编写
上一步我们设置了 TypeScript 环境。
这一步,我们用 tsdk 写一个简单的返回 Hello World 接口,和一个更新 Hello World 的接口。
新建 server/src/gen-route.ts
gen-route.ts 该文件负责中间件,错误处理等逻辑。
server/src/gen-route.ts
import { ZodError } from 'zod';
import { genRouteFactory, Protocol } from 'tsdk-server-adapters';
import { ProtocolTypes } from '@/src/shared/tsdk-helper';
import { APIConfig, APITypesKey } from '@/src/shared/tsdk-types';
const middlewares = [authMiddleware];
const genRouteObj = genRouteFactory<APIConfig, RequestInfo>(
onErrorHandler,
ProtocolTypes,
middlewares,
APITypesKey
);
export const routeBus = genRouteObj.routeBus;
export const genRoute = genRouteObj.genRoute;
export interface RequestInfo {
type: string;
ip: string;
lang: string;
username?: string;
userId?: number;
token?: string;
}
export type ReadonlyRequestInfo = Readonly<RequestInfo>;
async function authMiddleware(protocol: Protocol, apiConfig: APIConfig, reqInfo: RequestInfo) {
if (protocol === 'socket.io' || protocol === 'ws') {
// only parse once for socket
}
if (!apiConfig.needAuth) {
return Promise.resolve();
}
if (!reqInfo.token) {
return Promise.reject(new AuthError());
}
return Promise.resolve();
}
class AuthError extends Error {
message = 'AuthError';
}
class CustomError extends Error {
statusCode: number;
}
function onErrorHandler(
e: CustomError,
{ protocol, send, msgId }: Parameters<Parameters<typeof genRouteFactory>[0]>[1]
) {
if (e instanceof ZodError) {
return send({
_id: msgId,
status: 400,
result: {
msg: e.errors,
},
});
}
let status = e.statusCode || 500;
const msg = e.message;
if (e instanceof AuthError) {
status = 401;
}
return send({ _id: msgId, status, result: { msg } });
}
新建入口 server/src/main.ts
server/src/main.ts
import http from 'http';
import express, { Request } from 'express';
import multer from 'multer'
import { Server } from 'socket.io';
import { socketIOAdapterFactory } from 'tsdk-server-adapters/lib/socket.io-adapter';
import { expressAdapterFactory } from 'tsdk-server-adapters/lib/express-adapter';
import { checkMethodHasBody, ProtocolTypes } from '@/src/shared/tsdk-helper';
import { RequestInfo, routeBus } from './gen-route';
const port = 3030;
const app = express();
const server = http.createServer(app);
const io = new Server(server);
io.on('connection', (socket) => {
const { address, query, headers } = socket.handshake;
const reqInfo = {
ip: address,
lang: 'zh-CN',
token: query.token as string,
type: query.type as string,
};
socketIOAdapterFactory<RequestInfo>({
routeBus,
async getReqInfo() {
return reqInfo;
},
getType(reqInfo) {
return reqInfo.type;
},
async getData(data) {
return data;
},
protocolType: ProtocolTypes,
})(socket);
});
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(multer().none());
app.get('/', (req, res) => {
res.end('hi, from express.');
});
app.use(
'/api/:type',
expressAdapterFactory<RequestInfo>({
routeBus,
async getReqInfo(req: Request) {
return {
ip: req.ip as string,
lang: 'zh-CN',
type: req.params.type,
token: req.headers.authorization,
};
},
getType(reqInfo) {
return reqInfo.type;
},
async getData(req) {
return checkMethodHasBody(req.method) ? req.body : req.query;
},
})
);
server.listen(port, () => {
console.log(`Server listening at ${port}`);
});
编写配置 src/Hello.apiconf.ts
💡
别慌!接口配置可以通过 tsdk 内置的代码片段 Snippet Code 快速生成!! 查看说明
server/src/Hello.apiconf.ts
import * as z from 'zod';
import { APIConfig, transformPath } from '@/src/shared/tsdk-helper';
/**
* Get hello ({@link APIConfig})
* @category hello
*/
export const GetHelloConfig: APIConfig = {
type: 'user',
method: 'get',
path: transformPath('GetHello'),
description: 'Get hello',
category: 'hello',
needAuth: false,
};
/**
* @category hello
*/
export type GetHelloReq = {};
/**
* @category hello
*/
export type GetHelloRes = {
result: string;
};
// --------- GetHello END ---------
/**
* Update hello ({@link APIConfig})
* @category hello
*/
export const UpdateHelloConfig = {
type: 'user' as const,
method: 'post' as const,
path: transformPath('UpdateHello'),
description: 'Update hello',
category: 'hello',
needAuth: false,
schema: z.object({
payload: z.string().min(1),
}),
};
/**
* @category hello
*/
export type UpdateHelloReq = z.infer<typeof UpdateHelloConfig.schema>;
// export type UpdateHelloReq = {payload: string};
/**
* @category hello
*/
export type UpdateHelloRes = {
result: string;
};
// --------- UpdateHello END ---------
编写接口 src/Hello.api.ts
server/src/Hello.api.ts
import { genRoute, type RequestInfo } from './gen-route';
import {
GetHelloReq,
GetHelloRes,
GetHelloConfig,
UpdateHelloReq,
UpdateHelloRes,
UpdateHelloConfig,
} from './Hello.apiconf';
const DB = {
data: 'Hello World',
};
export function setupHelloAPI() {
genRoute<GetHelloReq, GetHelloRes>(
GetHelloConfig,
async (data, reqInfo: Readonly<RequestInfo>) => {
return { result: DB.data };
}
);
genRoute<UpdateHelloReq, UpdateHelloRes>(
UpdateHelloConfig,
async (data, reqInfo: Readonly<RequestInfo>) => {
DB.data = data.payload;
return { result: data.payload };
}
);
}
安装接口到 server/src/main/ts
server/src/main.ts
上面已创建好,我们只需要更新入口文件即可。server/src/main.ts
import http from 'http';
import { socketIOAdapterFactory } from 'tsdk-server-adapters/lib/socket.io-adapter';
import express from 'express';
import multer from 'multer';
import { expressAdapterFactory } from 'tsdk-server-adapters/lib/express-adapter';
import { checkMethodHasBody, ProtocolTypes } from '@/src/shared/tsdk-helper';
import { RequestInfo, routeBus } from './gen-route';
import { setupHelloAPI } from './Hello.api';
const port = 3030;
const io = new Server(server);
io.on('connection', (socket) => {
const { address, query, headers } = socket.handshake;
const reqInfo = {
ip: address,
lang: 'zh-CN',
token: query.token as string,
type: query.type as string,
};
socketIOAdapterFactory<RequestInfo>({
routeBus,
async getReqInfo() {
return reqInfo;
},
getType(reqInfo) {
return reqInfo.type;
},
async getData(data) {
return data;
},
protocolType: ProtocolTypes,
})(socket);
});
const app = express();
const server = http.createServer(app);
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(multer().none());
app.get('/', (req, res) => {
res.end('hi, from express.');
});
app.use(
'/api/:type',
expressAdapterFactory<RequestInfo>({
routeBus,
async getReqInfo(req) {
return {
ip: req.ip,
lang: 'zh-CN',
type: req.params.type,
token: req.headers.authorization,
};
},
getType(reqInfo) {
return reqInfo.type;
},
async getData(req) {
return checkMethodHasBody(req.method) ? req.body : req.query;
},
})
);
setupHelloAPI();
server.listen(port, () => {
console.log(`Server listening at ${port}`);
});
导出模块 ./fe-sdk
执行以下命令导出模块到 ./fe-sdk 文件夹
cd server
npm run sync-sdk
创建 nest-cli.json
nest-cli.json
{
"collection": "@nestjs/schematics",
"monorepo": true,
"root": "./",
"sourceRoot": "src",
"entryFile": "main",
"compilerOptions": {
"webpack": false,
"tsConfigPath": "tsconfig.json"
}
}
添加运行命令到 server/package.json
server/package.json
{
...
"scripts": {
...
"dev": "nest start --watch",
"build": "nest build",
"checktype": "tsc --noEmit"
}
...
}
运行开发环境
cd server
npm run dev
打包
npm run build