-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
121 lines (98 loc) · 3.11 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import express, { Express, Request, Response } from "express";
import Ajv from "ajv";
/** Sample types */
type EditTodoParams = {
id: string;
};
type EditTodoReqBody = {
message: string;
completed?: boolean;
};
type EditTodoResBody = {
id: string;
message: string;
completed: boolean;
};
/** Sample controller */
function editTodo(
params: EditTodoParams,
reqBody: EditTodoReqBody,
reqQuery: any
): EditTodoResBody {
return {
id: params.id,
message: reqBody.message,
completed: !reqBody.completed,
};
}
const validationError = (name: string, params: any, schema: any) =>
`Could not validate ${name} ${JSON.stringify(
params,
undefined,
2
)} against schema ${JSON.stringify(schema, undefined, 2)}`;
function put<Params, ReqBody, ResBody, ReqQuery>(
app: Express,
route: string,
schemas: {
paramsSchema?: string;
reqBodySchema?: string;
resBodySchema?: string;
reqQuerySchema?: string;
},
controller: (params: Params, reqBody: ReqBody, reqQuery: ReqQuery) => ResBody
) {
const wrappedController = (req: Request, res: Response) => {
console.log("req", req);
const ajv = new Ajv();
const paramsSchema = schemas.paramsSchema
? require(`./schemas/${schemas.paramsSchema}.json`)
: {};
const validateParams = ajv.compile<Params>(paramsSchema);
if (!validateParams(req.params)) {
throw validationError("request", req.params, paramsSchema);
}
const reqBodySchema = schemas.reqBodySchema
? require(`./schemas/${schemas.reqBodySchema}.json`)
: {};
const validateReqBody = ajv.compile<ReqBody>(reqBodySchema);
if (!validateReqBody(req.body)) {
throw validationError("body", req.body, reqBodySchema);
}
const reqQuerySchema = schemas.reqQuerySchema
? require(`./schemas/${schemas.reqQuerySchema}.json`)
: {};
const validateReqQuery = ajv.compile<ReqQuery>(reqQuerySchema);
if (!validateReqQuery(req.query)) {
throw validationError("query", req.query, reqQuerySchema);
}
const resBody = controller(req.params, req.body, req.query);
const resBodySchema = schemas.resBodySchema
? require(`./schemas/${schemas.resBodySchema}.json`)
: {};
const validateResBody = ajv.compile<ResBody>(resBodySchema);
if (!validateResBody(resBody)) {
throw validationError("response body", resBody, resBodySchema);
}
res.status(200).json(resBody);
};
app.put(route, wrappedController);
}
const app = express();
app.use(express.json());
/** We get type inference on the generic here
* Or we can include it to ensure that the type
* signature matches the strings used for schemas.
*/
put<EditTodoParams, EditTodoReqBody, EditTodoResBody, undefined>(
app,
"/todo/:id",
{
paramsSchema: "EditTodoParams", // references schemas.json/EditTodoParams
reqBodySchema: "EditTodoReqBody", // references schemas.json/EditTodoReqBody
resBodySchema: "EditTodoResBody", // references schemas.json/EditTodoResBody
reqQuerySchema: undefined, // none for this route are expected
},
editTodo
);
app.listen(8001, () => console.log("Listening on port 8001"));