ts-procedures 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +3 -3
  2. package/build/implementations/http/client/index.d.ts +1 -0
  3. package/build/implementations/http/client/index.js +2 -0
  4. package/build/implementations/http/client/index.js.map +1 -0
  5. package/build/implementations/http/express/index.d.ts +2 -1
  6. package/build/implementations/http/express/index.js.map +1 -1
  7. package/build/implementations/http/express/types.d.ts +17 -0
  8. package/build/implementations/http/express/types.js +2 -0
  9. package/build/implementations/http/express/types.js.map +1 -0
  10. package/build/implementations/http/express-rpc/index.d.ts +82 -0
  11. package/build/implementations/http/express-rpc/index.js +140 -0
  12. package/build/implementations/http/express-rpc/index.js.map +1 -0
  13. package/build/implementations/http/express-rpc/index.test.d.ts +1 -0
  14. package/build/implementations/http/express-rpc/index.test.js +445 -0
  15. package/build/implementations/http/express-rpc/index.test.js.map +1 -0
  16. package/build/implementations/http/express-rpc/types.d.ts +28 -0
  17. package/build/implementations/http/express-rpc/types.js +2 -0
  18. package/build/implementations/http/express-rpc/types.js.map +1 -0
  19. package/build/implementations/types.d.ts +17 -0
  20. package/build/implementations/types.js +2 -0
  21. package/build/implementations/types.js.map +1 -0
  22. package/build/schema/parser.js +2 -1
  23. package/build/schema/parser.js.map +1 -1
  24. package/package.json +13 -7
  25. package/src/implementations/http/express-rpc/README.md +321 -0
  26. package/src/implementations/http/express-rpc/index.test.ts +614 -0
  27. package/src/implementations/http/express-rpc/index.ts +180 -0
  28. package/src/implementations/http/express-rpc/types.ts +29 -0
  29. package/src/implementations/types.ts +20 -0
  30. package/src/schema/parser.ts +5 -4
  31. package/src/schema/types.ts +0 -1
  32. package/src/implementations/http/express/README.md +0 -351
  33. package/src/implementations/http/express/example/factories.ts +0 -25
  34. package/src/implementations/http/express/example/procedures/auth.ts +0 -24
  35. package/src/implementations/http/express/example/procedures/users.ts +0 -32
  36. package/src/implementations/http/express/example/server.test.ts +0 -133
  37. package/src/implementations/http/express/example/server.ts +0 -67
  38. package/src/implementations/http/express/index.test.ts +0 -526
  39. package/src/implementations/http/express/index.ts +0 -108
@@ -0,0 +1,180 @@
1
+ import express from 'express'
2
+ import { kebabCase } from 'es-toolkit/string'
3
+ import { Procedures, TProcedureRegistration } from '../../../index.js'
4
+ import { RPCConfig, RPCHttpRouteDoc } from '../../types.js'
5
+ import { castArray } from 'es-toolkit/compat'
6
+ import { ExpressFactoryItem, ExtractContext, ProceduresFactory } from './types.js'
7
+
8
+ export type { RPCConfig, RPCHttpRouteDoc }
9
+ /**
10
+ * Builder class for creating an Express application with RPC routes.
11
+ *
12
+ * Usage:
13
+ * const PublicRPC = Procedures<PublicRPCContext, RPCConfig>()
14
+ * const ProtectedRPC = Procedures<ProtectedRPCContext, RPCConfig>()
15
+ *
16
+ * const rpcApp = new ExpressRPCAppBuilder()
17
+ * .register(PublicRPC, (req): Promise<PublicRPCContext> => { /* context resolution logic * / })
18
+ * .register(ProtectedRPC, (req): Promise<ProtectedRPCContext> => { /* context resolution logic * / })
19
+ * .build();
20
+ *
21
+ * const app = rpcApp.app; // Express application
22
+ * const docs = rpcApp.docs; // RPC route documentation
23
+ */
24
+ export class ExpressRPCAppBuilder {
25
+ /**
26
+ * Constructor for ExpressRPCAppBuilder.
27
+ *
28
+ * @param config
29
+ */
30
+ constructor(
31
+ readonly config?: {
32
+ /**
33
+ * An existing Express application instance to use.
34
+ * When provided, ensure to set up necessary middleware (e.g., json/body parser) beforehand.
35
+ * If not provided, a new instance will be created.
36
+ */
37
+ app?: express.Express
38
+ onRequestStart?: (req: express.Request) => void
39
+ onRequestEnd?: (req: express.Request, res: express.Response) => void
40
+ onSuccess?: (
41
+ procedure: TProcedureRegistration,
42
+ req: express.Request,
43
+ res: express.Response
44
+ ) => void
45
+ error?: (
46
+ procedure: TProcedureRegistration,
47
+ req: express.Request,
48
+ res: express.Response,
49
+ error: Error
50
+ ) => void
51
+ }
52
+ ) {
53
+ if (config?.app) {
54
+ this._app = config.app
55
+ } else {
56
+ // Default middleware if no app is provided
57
+ this._app.use(express.json())
58
+ }
59
+
60
+ if (config?.onRequestStart) {
61
+ this._app.use((req, res, next) => {
62
+ config.onRequestStart!(req)
63
+ next()
64
+ })
65
+ }
66
+
67
+ if (config?.onRequestEnd) {
68
+ this._app.use((req, res, next) => {
69
+ res.on('finish', () => {
70
+ config.onRequestEnd!(req, res)
71
+ })
72
+ next()
73
+ })
74
+ }
75
+ }
76
+
77
+ private factories: ExpressFactoryItem<any>[] = []
78
+
79
+ private _app: express.Express = express()
80
+ private _docs: RPCHttpRouteDoc[] = []
81
+
82
+ get app(): express.Express {
83
+ return this._app
84
+ }
85
+
86
+ get docs(): RPCHttpRouteDoc[] {
87
+ return this._docs
88
+ }
89
+
90
+ /**
91
+ * Registers a procedure factory with its context resolver.
92
+ * @param factory
93
+ * @param contextResolver
94
+ */
95
+ register<TFactory extends ProceduresFactory>(
96
+ factory: TFactory,
97
+ contextResolver: (req: express.Request) => ExtractContext<TFactory>
98
+ ): this {
99
+ this.factories.push({ factory, contextResolver } as ExpressFactoryItem<any>)
100
+ return this
101
+ }
102
+
103
+ /**
104
+ * Builds and returns the Express application with registered RPC routes.
105
+ * @return express.Application
106
+ */
107
+ build(): express.Application {
108
+ this.factories.forEach(({ factory, contextResolver }) => {
109
+ factory.getProcedures().map((procedure: TProcedureRegistration<any, RPCConfig>) => {
110
+ const route = this.buildRpcHttpRouteDoc(procedure)
111
+
112
+ this._docs.push(route)
113
+
114
+ this._app[route.method](route.path, async (req, res) => {
115
+ try {
116
+ res.json(await procedure.handler(contextResolver(req), req.body))
117
+ if (this.config?.onSuccess) {
118
+ this.config.onSuccess(procedure, req, res)
119
+ }
120
+ // if status not set, set to 200
121
+ if (!res.status) {
122
+ res.status(200)
123
+ }
124
+ } catch (error) {
125
+ if (this.config?.error) {
126
+ this.config.error(procedure, req, res, error as Error)
127
+ return
128
+ }
129
+ if (!res.status) {
130
+ res.status(500)
131
+ }
132
+ // if no res.json set, set default error message
133
+ if (!res.headersSent) {
134
+ res.json({ error: (error as Error).message })
135
+ }
136
+ }
137
+ })
138
+ })
139
+ })
140
+
141
+ return this._app
142
+ }
143
+
144
+ /**
145
+ * Generates the RPC HTTP route for the given procedure.
146
+ * @param procedure
147
+ */
148
+ buildRpcHttpRouteDoc(procedure: TProcedureRegistration<any, RPCConfig>): RPCHttpRouteDoc {
149
+ const { config } = procedure
150
+ const path = this.makeRPCHttpRoutePath(config)
151
+ const method = 'post' // RPCs use POST method
152
+ const jsonSchema: { body?: object; response?: object } = {}
153
+
154
+ if (config.schema?.params) {
155
+ jsonSchema.body = config.schema.params
156
+ }
157
+ if (config.schema?.returnType) {
158
+ jsonSchema.response = config.schema.returnType
159
+ }
160
+
161
+ return {
162
+ path,
163
+ method,
164
+ jsonSchema,
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Generates the RPC route path based on the RPC configuration.
170
+ * The RPCConfig name can be a string or an array of strings to form nested paths.
171
+ *
172
+ * Example
173
+ * name: ['string', 'string-string', 'string']
174
+ * path: /rpc/string/string-string/string/version
175
+ * @param config
176
+ */
177
+ makeRPCHttpRoutePath(config: RPCConfig) {
178
+ return `/rpc/${castArray(config.name).map(kebabCase).join('/')}/${String(config.version).trim()}`
179
+ }
180
+ }
@@ -0,0 +1,29 @@
1
+ import { RPCConfig } from '../../types.js'
2
+ import { Procedures } from '../../../index.js'
3
+ import express from 'express'
4
+
5
+ /**
6
+ * Extracts the TContext type from a Procedures factory return type.
7
+ * Uses the first parameter of the handler function to infer the context type.
8
+ */
9
+ export type ExtractContext<TFactory> = TFactory extends {
10
+ getProcedures: () => Array<{ handler: (ctx: infer TContext, ...args: any[]) => any }>
11
+ } ? TContext : never
12
+
13
+ /**
14
+ * Minimal structural type for a Procedures factory.
15
+ * Uses explicit `any` types to avoid variance issues with generic constraints.
16
+ */
17
+ export type ProceduresFactory = {
18
+ getProcedures: () => Array<{
19
+ name: string
20
+ config: any
21
+ handler: (ctx: any, params?: any) => Promise<any>
22
+ }>
23
+ Create: (...args: any[]) => any
24
+ }
25
+
26
+ export type ExpressFactoryItem<TFactory = ReturnType<typeof Procedures<any, RPCConfig>>> = {
27
+ factory: TFactory
28
+ contextResolver: (req: express.Request) => ExtractContext<TFactory>
29
+ }
@@ -0,0 +1,20 @@
1
+ import { Procedures } from '../index.js'
2
+
3
+ export interface RPCConfig {
4
+ name: string | string[]
5
+ version: number
6
+ }
7
+
8
+ export type FactoryItem<C> = {
9
+ factory: ReturnType<typeof Procedures<C, RPCConfig>>
10
+ contextResolver: (req: Request) => C
11
+ }
12
+
13
+ export interface RPCHttpRouteDoc {
14
+ path: string
15
+ method: 'post'
16
+ jsonSchema: {
17
+ body?: object
18
+ response?: object
19
+ }
20
+ }
@@ -1,4 +1,4 @@
1
- import {default as addFormats} from 'ajv-formats'
1
+ import { default as addFormats } from 'ajv-formats'
2
2
  import * as AJV from 'ajv'
3
3
  import { extractJsonSchema } from './extract-json-schema.js'
4
4
  import { TJSONSchema } from './types.js'
@@ -10,18 +10,19 @@ export type TSchemaParsed = {
10
10
 
11
11
  export type TSchemaValidationError = AJV.ErrorObject
12
12
 
13
- // @ts-ignore
13
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
14
+ // @ts-expect-error
14
15
  const ajv = addFormats(
15
16
  new AJV.Ajv({
16
17
  allErrors: true,
17
18
  coerceTypes: true,
18
19
  removeAdditional: true,
19
- }),
20
+ })
20
21
  )
21
22
 
22
23
  export function schemaParser(
23
24
  schema: { params?: unknown; returnType?: unknown },
24
- onParseError: (errors: { params?: string; returnType?: string }) => void,
25
+ onParseError: (errors: { params?: string; returnType?: string }) => void
25
26
  ): TSchemaParsed {
26
27
  const jsonSchema: TSchemaParsed['jsonSchema'] = {}
27
28
  const validation: TSchemaParsed['validation'] = {}
@@ -1,4 +1,3 @@
1
- /* eslint-disable @typescript-eslint/ban-types */
2
1
  import { CoreValidator, TypeOf } from 'suretype'
3
2
  import { Static, TSchema } from 'typebox'
4
3
 
@@ -1,351 +0,0 @@
1
- # Express Integration Guide
2
-
3
- ## Overview
4
-
5
- `registerExpressRoutes` is a utility function that simplifies setting up an Express server with ts-procedures. It handles:
6
-
7
- - **Automatic parameter merging** - Combines path params (`:id`), query params, and body into a single `params` object
8
- - **Schema validation** - Validates merged params against your procedure's schema
9
- - **Error handling** - Provides hooks for custom error responses
10
- - **Context injection** - Creates procedure context from Express request/response
11
-
12
- ## Quick Start
13
-
14
- ```typescript
15
- import express from 'express'
16
- import { Procedures } from 'ts-procedures'
17
- import { registerExpressRoutes } from 'ts-procedures/express'
18
- import { Type } from 'typebox'
19
-
20
- // Define extended config for HTTP routes
21
- interface HTTPRouteConfig {
22
- path: string
23
- method: 'get' | 'post' | 'put' | 'delete' | 'patch'
24
- }
25
-
26
- // Create factory with route config
27
- const { Create, getProcedures } = Procedures<{}, HTTPRouteConfig>()
28
-
29
- // Define a procedure
30
- Create('GetUser', {
31
- path: '/users/:id',
32
- method: 'get',
33
- schema: {
34
- params: Type.Object({ id: Type.String() }),
35
- returnType: Type.Object({ id: Type.String(), name: Type.String() })
36
- }
37
- }, async (ctx, params) => {
38
- return { id: params.id, name: 'John Doe' }
39
- })
40
-
41
- // Register routes
42
- const app = express()
43
- app.use(express.json())
44
-
45
- registerExpressRoutes(
46
- app,
47
- { getContext: async (req, res) => ({}) },
48
- getProcedures()
49
- )
50
-
51
- app.listen(3000)
52
- ```
53
-
54
- ## Architecture Pattern
55
-
56
- ### Public vs Protected Factories
57
-
58
- A common pattern is to separate procedures that require authentication from public endpoints:
59
-
60
- ```typescript
61
- // factories.ts
62
- import { Procedures } from 'ts-procedures'
63
-
64
- export interface ProceduresContext {
65
- headers: Record<string, string>
66
- ipAddress: string
67
- requestId: string
68
- }
69
-
70
- export interface ProtectedContext extends ProceduresContext {
71
- userId: string // Required - user must be authenticated
72
- }
73
-
74
- export interface PublicContext extends ProceduresContext {
75
- userId?: undefined // Optional - may or may not be authenticated
76
- }
77
-
78
- export interface HTTPRouteConfig {
79
- path: string
80
- method: 'get' | 'post' | 'put' | 'delete' | 'patch'
81
- }
82
-
83
- export const ProtectedFactory = Procedures<ProtectedContext, HTTPRouteConfig>()
84
- export const PublicFactory = Procedures<PublicContext, HTTPRouteConfig>()
85
- ```
86
-
87
- ### File Organization
88
-
89
- ```
90
- src/
91
- ├── factories.ts # Context interfaces and factory setup
92
- ├── procedures/
93
- │ ├── auth.ts # Public endpoints (login, register)
94
- │ └── users.ts # Protected endpoints (profile, settings)
95
- └── server.ts # Express app and route registration
96
- ```
97
-
98
- ## Complete Example
99
-
100
- ### factories.ts
101
-
102
- ```typescript
103
- import { Procedures } from 'ts-procedures'
104
-
105
- export interface ProceduresContext {
106
- headers: Record<string, string>
107
- ipAddress: string
108
- requestId: string
109
- }
110
-
111
- export interface ProtectedContext extends ProceduresContext {
112
- userId: string
113
- }
114
-
115
- export interface PublicContext extends ProceduresContext {
116
- userId?: undefined
117
- }
118
-
119
- export interface HTTPRouteConfig {
120
- path: string
121
- method: 'get' | 'post' | 'put' | 'delete' | 'patch'
122
- }
123
-
124
- export const ProtectedFactory = Procedures<ProtectedContext, HTTPRouteConfig>()
125
- export const PublicFactory = Procedures<PublicContext, HTTPRouteConfig>()
126
- ```
127
-
128
- ### procedures/auth.ts
129
-
130
- ```typescript
131
- import { PublicFactory } from '../factories.js'
132
- import { Type } from 'typebox'
133
-
134
- PublicFactory.Create('Authenticate', {
135
- path: '/authenticate',
136
- method: 'post',
137
- schema: {
138
- params: Type.Object({
139
- username: Type.String({ minLength: 3 }),
140
- password: Type.String({ minLength: 6 }),
141
- }),
142
- returnType: Type.Object({
143
- token: Type.String(),
144
- })
145
- },
146
- description: 'Authenticate as a user and obtain a token',
147
- }, async (ctx, params) => {
148
- // Verify credentials and return token
149
- return { token: 'jwt-token-here' }
150
- })
151
- ```
152
-
153
- ### procedures/users.ts
154
-
155
- ```typescript
156
- import { ProtectedFactory } from '../factories.js'
157
- import { Type } from 'typebox'
158
-
159
- ProtectedFactory.Create('GetUserProfile', {
160
- path: '/users/user-profile/:id',
161
- method: 'get',
162
- schema: {
163
- params: Type.Object({
164
- id: Type.String(), // Mapped from :id in path
165
- }),
166
- returnType: Type.Object({
167
- user: Type.Object({
168
- id: Type.String(),
169
- name: Type.String(),
170
- email: Type.String(),
171
- }),
172
- })
173
- },
174
- description: 'Get the profile of a specific user',
175
- }, async (ctx, params) => {
176
- // ctx.userId is guaranteed to exist (ProtectedContext)
177
- return {
178
- user: {
179
- id: params.id,
180
- name: 'Jane Doe',
181
- email: 'jane@example.com'
182
- }
183
- }
184
- })
185
- ```
186
-
187
- ### server.ts
188
-
189
- ```typescript
190
- import express from 'express'
191
- import { PublicFactory, ProtectedFactory } from './factories.js'
192
- import { registerExpressRoutes } from 'ts-procedures/express'
193
-
194
- // Import procedures for side effects (registers with factories)
195
- import './procedures/auth.js'
196
- import './procedures/users.js'
197
-
198
- const app = express()
199
- app.use(express.json())
200
-
201
- // Register public routes (no auth required)
202
- registerExpressRoutes(
203
- app,
204
- {
205
- getContext: async (req, res) => ({
206
- ipAddress: req.ip || '',
207
- requestId: req.headers['x-request-id']?.toString() || crypto.randomUUID(),
208
- headers: req.headers as Record<string, string>,
209
- })
210
- },
211
- PublicFactory.getProcedures()
212
- )
213
-
214
- // Register protected routes (auth required)
215
- registerExpressRoutes(
216
- app,
217
- {
218
- getContext: async (req, res) => {
219
- const token = req.headers['authorization']?.replace('Bearer ', '')
220
- const user = await validateToken(token) // Your auth logic
221
-
222
- if (!user) {
223
- res.status(401).json({ error: 'Unauthorized' })
224
- throw new Error('Unauthorized')
225
- }
226
-
227
- return {
228
- userId: user.id,
229
- ipAddress: req.ip || '',
230
- requestId: req.headers['x-request-id']?.toString() || crypto.randomUUID(),
231
- headers: req.headers as Record<string, string>,
232
- }
233
- }
234
- },
235
- ProtectedFactory.getProcedures()
236
- )
237
-
238
- app.listen(3000)
239
- ```
240
-
241
- ## API Reference
242
-
243
- ```typescript
244
- registerExpressRoutes<ProceduresContext>(
245
- app: express.Application,
246
- callbacks: {
247
- /** Create procedure context from Express request */
248
- getContext: (req: express.Request, res: express.Response) => Promise<ProceduresContext>
249
-
250
- /** Optional handler for procedure errors (default: 500 with error message) */
251
- onHandlerError?: (error: Error, req: express.Request, res: express.Response) => void
252
-
253
- /** Optional handler for validation errors (default: 422 with error details) */
254
- onValidationError?: (
255
- errors: Array<{ message: string; path: string[] }>,
256
- req: express.Request,
257
- res: express.Response
258
- ) => void
259
- },
260
- procedures: Array<TProcedureRegistration<ProceduresContext, HTTPRouteConfig>>
261
- ): void
262
- ```
263
-
264
- ## Features
265
-
266
- ### Path Parameter Mapping
267
-
268
- Path parameters defined with `:paramName` are automatically extracted and merged into the params object:
269
-
270
- ```typescript
271
- // Route: /users/:id/posts/:postId
272
- // URL: /users/123/posts/456
273
- // Params: { id: '123', postId: '456' }
274
- ```
275
-
276
- ### Parameter Merging
277
-
278
- Parameters are merged in this order (later sources override earlier):
279
-
280
- 1. Path parameters (`:id`)
281
- 2. Query parameters (`?foo=bar`)
282
- 3. Body parameters (JSON body)
283
-
284
- ### Schema Validation
285
-
286
- When validation fails, the default behavior returns a 422 response:
287
-
288
- ```json
289
- {
290
- "error": "Validation Error",
291
- "details": [
292
- { "message": "must be string", "path": ["username"] }
293
- ]
294
- }
295
- ```
296
-
297
- ### Custom Error Handlers
298
-
299
- ```typescript
300
- registerExpressRoutes(
301
- app,
302
- {
303
- getContext: async (req, res) => ({}),
304
-
305
- onValidationError: (errors, req, res) => {
306
- res.status(400).json({
307
- code: 'INVALID_INPUT',
308
- errors: errors.map(e => e.message)
309
- })
310
- },
311
-
312
- onHandlerError: (error, req, res) => {
313
- if (error instanceof ProcedureError) {
314
- res.status(error.code || 500).json({ message: error.message })
315
- } else {
316
- res.status(500).json({ message: 'Internal Server Error' })
317
- }
318
- }
319
- },
320
- getProcedures()
321
- )
322
- ```
323
-
324
- ### Authentication Pattern
325
-
326
- The `getContext` callback is the ideal place to handle authentication:
327
-
328
- ```typescript
329
- getContext: async (req, res) => {
330
- const token = req.headers['authorization']
331
-
332
- if (!token) {
333
- res.status(401).json({ error: 'No token provided' })
334
- throw new Error('Unauthorized')
335
- }
336
-
337
- const user = await verifyToken(token)
338
-
339
- if (!user) {
340
- res.status(401).json({ error: 'Invalid token' })
341
- throw new Error('Unauthorized')
342
- }
343
-
344
- return {
345
- userId: user.id,
346
- // ... other context
347
- }
348
- }
349
- ```
350
-
351
- When `getContext` throws, the route handler stops execution. This allows you to respond with an error and prevent the procedure from running.
@@ -1,25 +0,0 @@
1
- import { Procedures } from '../../../../index.js'
2
-
3
- export interface ProceduresContext {
4
- headers: Record<string, string>
5
- ipAddress: string
6
- requestId: string
7
- }
8
-
9
- export interface ProtectedContext extends ProceduresContext {
10
- userId: string
11
- }
12
-
13
- export interface PublicContext extends ProceduresContext {
14
- // An authenticated client can call public endpoints so userId is optional
15
- userId?: undefined
16
- }
17
-
18
- export const ProtectedFactory = Procedures<ProtectedContext,HTTPRouteConfig>()
19
-
20
- export const PublicFactory = Procedures<PublicContext,HTTPRouteConfig>()
21
-
22
- export interface HTTPRouteConfig {
23
- path: string
24
- method: 'get' | 'post' | 'put' | 'delete' | 'patch'
25
- }
@@ -1,24 +0,0 @@
1
- import { PublicFactory } from '../factories.js'
2
- import { Type } from 'typebox'
3
-
4
- PublicFactory.Create('Authenticate', {
5
- path: '/authenticate',
6
- method: 'post',
7
- schema: {
8
- params: Type.Object({
9
- username: Type.String({ minLength: 3 }),
10
- password: Type.String({ minLength: 6 }),
11
- }),
12
- returnType: Type.Object({
13
- token: Type.String(),
14
- })
15
- },
16
- description: 'Authenticate as a user and obtain a token',
17
- },
18
- async (ctx, params) => {
19
- // In a real implementation, you would verify user credentials here, ie: ctx.services.userService.authenticate(params.username, params.password)
20
-
21
- return {
22
- token: 'fake-jwt-token',
23
- }
24
- })
@@ -1,32 +0,0 @@
1
- import { ProtectedFactory } from '../factories.js'
2
- import { Type } from 'typebox'
3
-
4
- ProtectedFactory.Create('Get User Profile', {
5
- path: '/users/user-profile/:id',
6
- method: 'get',
7
- schema: {
8
- params: Type.Object({
9
- // Our router in this example will map :id to this param
10
- id: Type.String(),
11
- }),
12
- returnType: Type.Object({
13
- user: Type.Object({
14
- id: Type.String(),
15
- name: Type.String(),
16
- email: Type.String(),
17
- }),
18
- })
19
- },
20
- description: 'Get the profile of a specific user',
21
- },
22
- async (ctx, params) => {
23
- // In a real implementation, you would fetch the user profile from a database or service here, ie: ctx.services.userService.getUserProfile(params.userId)
24
-
25
- return {
26
- user: {
27
- id: params.id,
28
- name: 'Jane Doe',
29
- email: 'test@gmail.com'
30
- },
31
- }
32
- })