-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPerson.ts
More file actions
109 lines (102 loc) · 4.14 KB
/
Copy pathPerson.ts
File metadata and controls
109 lines (102 loc) · 4.14 KB
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
import { getAppInstance } from '@adaptivestone/framework/helpers/appInstance.js';
import AbstractController, {
type TMiddleware,
} from '@adaptivestone/framework/modules/AbstractController.js';
import { NotFoundError } from '@adaptivestone/framework/services/http/httpErrors.js';
import RateLimiter from '@adaptivestone/framework/services/http/middleware/RateLimiter.js';
import type { Response } from 'express';
import { z } from 'zod';
// Generated by `npm run gen` from this controller's `routes` getter.
// Handler request types (typed `appInfo`, `params`, `query`, body) come from here.
import type {
CreatePersonRequest,
GetPersonByIdRequest,
GetPersonRequest,
} from './Person.routes.gen.ts';
class Person extends AbstractController {
get routes() {
// A simple const config read before a literal return remains analyzable by
// route-type generation. The generated config type catches policy typos.
const { policy } = getAppInstance().getConfig('rateLimiter');
return {
get: {
'/': {
handler: this.getPerson,
},
'/:id': {
handler: this.getPersonById,
// Path params are validated like the body and query string. A
// malformed id is rejected with a 400 before the handler runs, so
// 404 keeps its real meaning: the id was fine, the person isn't there.
params: z.object({
id: z.string().regex(/^[0-9a-fA-F]{24}$/, 'must be a valid id'),
}),
},
},
post: {
'/': {
handler: this.createPerson,
// Any Standard Schema validator works as a route schema. The schema's
// inferred output becomes the typed `req.appInfo.request` (see handler).
request: z.object({
firstName: z.string(),
lastName: z.string(),
}),
// Route-local middleware can consume a typed, overridable config
// policy directly; no string lookup or duplicated options object.
middleware: [[RateLimiter, policy.personCreate]],
},
},
};
}
// POST /person — request body is validated and typed by the schema above.
async createPerson(req: CreatePersonRequest, res: Response) {
const { firstName, lastName } = req.appInfo.request;
const PersonModel = req.appInfo.app.getModel('Person');
const person = await PersonModel.create({ firstName, lastName });
return res.status(201).json({ data: person });
}
// GET /person/:id — demonstrates a `params:` schema (framework ≥ 5.3) and
// typed HTTP errors (≥ 5.1). Thrown `HttpError`s resolve to their status
// through the error-handler registry, so any depth of business logic can end
// a request cleanly without threading `res` around.
// See docs: Controllers → Routes → Params, and Controllers → Error handling.
async getPersonById(req: GetPersonByIdRequest, res: Response) {
// Validated and typed by the route's `params:` schema — no in-handler
// guard. Raw `req.params.id` is still the unvalidated string.
const { id } = req.appInfo.params;
const PersonModel = req.appInfo.app.getModel('Person');
const person = await PersonModel.findById(id);
if (!person) {
throw new NotFoundError('Person not found');
// → 404 { "message": "Person not found" }
}
return res.status(200).json({ data: person });
}
async getPerson(req: GetPersonRequest, res: Response) {
// `getModel('Person')` is typed via the generated `genTypes.d.ts` — no cast.
const PersonModel = req.appInfo.app.getModel('Person');
let person = await PersonModel.findOne({ lastName: 'Show' });
if (!person) {
// just for demo
person = await PersonModel.create({
firstName: 'Jon',
lastName: 'Snow',
});
}
try {
await person.sendCreatEmail(req.appInfo.i18n);
} catch (e) {
if (e instanceof Error) {
this.logger?.error(e.message);
} else {
this.logger?.error('An unknown error occurred');
}
}
return res.status(200).json(person);
}
static get middleware(): Map<string, TMiddleware> {
return new Map([['/{*splat}', []]]);
}
}
export default Person;