@aws/nx-plugin 0.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 (142) hide show
  1. package/README.md +63 -0
  2. package/generators.json +52 -0
  3. package/package.json +27 -0
  4. package/src/cloudscape-website/app/README.md +253 -0
  5. package/src/cloudscape-website/app/__snapshots__/generator.spec.ts.snap +539 -0
  6. package/src/cloudscape-website/app/files/app/src/config.ts.template +4 -0
  7. package/src/cloudscape-website/app/files/app/src/layouts/App/index.tsx.template +132 -0
  8. package/src/cloudscape-website/app/files/app/src/layouts/App/navitems.ts.template +8 -0
  9. package/src/cloudscape-website/app/files/app/src/layouts/Routes/index.tsx.template +18 -0
  10. package/src/cloudscape-website/app/files/app/src/main.tsx.template +22 -0
  11. package/src/cloudscape-website/app/files/app/src/pages/Home/index.tsx.template +25 -0
  12. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/cloudfront-web-acl.ts.template +317 -0
  13. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/index.ts.template +4 -0
  14. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/static-website.ts.template +237 -0
  15. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/webacl_event_handler/index.ts.template +301 -0
  16. package/src/cloudscape-website/app/files/e2e/cypress/src/e2e/app.cy.ts.template +13 -0
  17. package/src/cloudscape-website/app/files/e2e/cypress/src/support/app.po.ts.template +1 -0
  18. package/src/cloudscape-website/app/files/e2e/playwright/src/example.spec.ts.template +6 -0
  19. package/src/cloudscape-website/app/generator.d.ts +4 -0
  20. package/src/cloudscape-website/app/generator.js +177 -0
  21. package/src/cloudscape-website/app/generator.js.map +1 -0
  22. package/src/cloudscape-website/app/schema.d.js +6 -0
  23. package/src/cloudscape-website/app/schema.d.js.map +1 -0
  24. package/src/cloudscape-website/app/schema.d.ts +35 -0
  25. package/src/cloudscape-website/app/schema.json +189 -0
  26. package/src/cloudscape-website/cognito-auth/README.md +172 -0
  27. package/src/cloudscape-website/cognito-auth/__snapshots__/generator.spec.ts.snap +238 -0
  28. package/src/cloudscape-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +50 -0
  29. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/index.ts.template +4 -0
  30. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/user-identity.ts.template +69 -0
  31. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/userpool-with-mfa.ts.template +70 -0
  32. package/src/cloudscape-website/cognito-auth/generator.d.ts +4 -0
  33. package/src/cloudscape-website/cognito-auth/generator.js +100 -0
  34. package/src/cloudscape-website/cognito-auth/generator.js.map +1 -0
  35. package/src/cloudscape-website/cognito-auth/schema.d.js +6 -0
  36. package/src/cloudscape-website/cognito-auth/schema.d.js.map +1 -0
  37. package/src/cloudscape-website/cognito-auth/schema.d.ts +4 -0
  38. package/src/cloudscape-website/cognito-auth/schema.json +36 -0
  39. package/src/cloudscape-website/runtime-config/__snapshots__/generator.spec.ts.snap +112 -0
  40. package/src/cloudscape-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +46 -0
  41. package/src/cloudscape-website/runtime-config/generator.d.ts +4 -0
  42. package/src/cloudscape-website/runtime-config/generator.js +74 -0
  43. package/src/cloudscape-website/runtime-config/generator.js.map +1 -0
  44. package/src/cloudscape-website/runtime-config/schema.d.js +6 -0
  45. package/src/cloudscape-website/runtime-config/schema.d.js.map +1 -0
  46. package/src/cloudscape-website/runtime-config/schema.d.ts +3 -0
  47. package/src/cloudscape-website/runtime-config/schema.json +19 -0
  48. package/src/gitlab/files/.gitlab-ci.yml.template +26 -0
  49. package/src/gitlab/generator.d.ts +4 -0
  50. package/src/gitlab/generator.js +26 -0
  51. package/src/gitlab/generator.js.map +1 -0
  52. package/src/gitlab/schema.d.js +6 -0
  53. package/src/gitlab/schema.d.js.map +1 -0
  54. package/src/gitlab/schema.d.ts +5 -0
  55. package/src/gitlab/schema.json +52 -0
  56. package/src/index.d.ts +0 -0
  57. package/src/index.js +3 -0
  58. package/src/index.js.map +1 -0
  59. package/src/infra/app/README.md +175 -0
  60. package/src/infra/app/__snapshots__/generator.spec.ts.snap +864 -0
  61. package/src/infra/app/files/cdk.json +67 -0
  62. package/src/infra/app/files/src/main.ts.template +37 -0
  63. package/src/infra/app/files/src/stacks/application-stack.ts.template +10 -0
  64. package/src/infra/app/generator.d.ts +4 -0
  65. package/src/infra/app/generator.js +75 -0
  66. package/src/infra/app/generator.js.map +1 -0
  67. package/src/infra/app/schema.d.js +6 -0
  68. package/src/infra/app/schema.d.js.map +1 -0
  69. package/src/infra/app/schema.d.ts +6 -0
  70. package/src/infra/app/schema.json +35 -0
  71. package/src/trpc/backend/README.md +549 -0
  72. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +110 -0
  73. package/src/trpc/backend/files/backend/src/index.ts.template +1 -0
  74. package/src/trpc/backend/files/backend/src/lambdas/index.ts.template +1 -0
  75. package/src/trpc/backend/files/backend/src/lambdas/middleware.ts.template +146 -0
  76. package/src/trpc/backend/files/backend/src/lambdas/router.ts.template +36 -0
  77. package/src/trpc/backend/files/common/constructs/src/__apiNameKebabCase__/index.ts.template +64 -0
  78. package/src/trpc/backend/files/schema/src/index.ts.template +7 -0
  79. package/src/trpc/backend/generator.d.ts +4 -0
  80. package/src/trpc/backend/generator.js +128 -0
  81. package/src/trpc/backend/generator.js.map +1 -0
  82. package/src/trpc/backend/schema.d.js +6 -0
  83. package/src/trpc/backend/schema.d.js.map +1 -0
  84. package/src/trpc/backend/schema.d.ts +8 -0
  85. package/src/trpc/backend/schema.json +44 -0
  86. package/src/trpc/react/README.md +320 -0
  87. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +98 -0
  88. package/src/trpc/react/files/src/components/TRPCClientProvider/index.tsx.template +34 -0
  89. package/src/trpc/react/files/src/hooks/useTrpc.tsx.template +5 -0
  90. package/src/trpc/react/generator.d.ts +4 -0
  91. package/src/trpc/react/generator.js +81 -0
  92. package/src/trpc/react/generator.js.map +1 -0
  93. package/src/trpc/react/schema.d.js +6 -0
  94. package/src/trpc/react/schema.d.js.map +1 -0
  95. package/src/trpc/react/schema.d.ts +5 -0
  96. package/src/trpc/react/schema.json +32 -0
  97. package/src/ts/cjs-to-esm/generator.d.ts +8 -0
  98. package/src/ts/cjs-to-esm/generator.js +201 -0
  99. package/src/ts/cjs-to-esm/generator.js.map +1 -0
  100. package/src/ts/cjs-to-esm/schema.d.js +6 -0
  101. package/src/ts/cjs-to-esm/schema.d.js.map +1 -0
  102. package/src/ts/cjs-to-esm/schema.d.ts +5 -0
  103. package/src/ts/cjs-to-esm/schema.json +28 -0
  104. package/src/ts/lib/README.md +149 -0
  105. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +260 -0
  106. package/src/ts/lib/eslint.d.ts +3 -0
  107. package/src/ts/lib/eslint.js +41 -0
  108. package/src/ts/lib/eslint.js.map +1 -0
  109. package/src/ts/lib/files/src/index.ts.template +3 -0
  110. package/src/ts/lib/generator.d.ts +21 -0
  111. package/src/ts/lib/generator.js +61 -0
  112. package/src/ts/lib/generator.js.map +1 -0
  113. package/src/ts/lib/schema.d.js +6 -0
  114. package/src/ts/lib/schema.d.js.map +1 -0
  115. package/src/ts/lib/schema.d.ts +13 -0
  116. package/src/ts/lib/schema.json +46 -0
  117. package/src/ts/lib/ts-project-utils.d.ts +6 -0
  118. package/src/ts/lib/ts-project-utils.js +107 -0
  119. package/src/ts/lib/ts-project-utils.js.map +1 -0
  120. package/src/ts/lib/types.d.ts +10 -0
  121. package/src/ts/lib/types.js +6 -0
  122. package/src/ts/lib/types.js.map +1 -0
  123. package/src/ts/lib/vitest.d.ts +3 -0
  124. package/src/ts/lib/vitest.js +67 -0
  125. package/src/ts/lib/vitest.js.map +1 -0
  126. package/src/utils/files/common/constructs/src/index.ts.template +1 -0
  127. package/src/utils/files/common/constructs/src/runtime-config/index.ts.template +1 -0
  128. package/src/utils/files/common/constructs/src/runtime-config/runtime-config.ts.template +33 -0
  129. package/src/utils/files/common/types/src/index.ts.template +1 -0
  130. package/src/utils/files/common/types/src/runtime-config.ts.template +13 -0
  131. package/src/utils/npm-scope.d.ts +7 -0
  132. package/src/utils/npm-scope.js +37 -0
  133. package/src/utils/npm-scope.js.map +1 -0
  134. package/src/utils/paths.d.ts +3 -0
  135. package/src/utils/paths.js +32 -0
  136. package/src/utils/paths.js.map +1 -0
  137. package/src/utils/shared-constructs.d.ts +7 -0
  138. package/src/utils/shared-constructs.js +72 -0
  139. package/src/utils/shared-constructs.js.map +1 -0
  140. package/src/utils/versions.d.ts +31 -0
  141. package/src/utils/versions.js +49 -0
  142. package/src/utils/versions.js.map +1 -0
@@ -0,0 +1,549 @@
1
+ # tRPC Backend Generator
2
+
3
+ ## Overview
4
+ This generator creates a new tRPC backend application with AWS CDK infrastructure setup. The generated backend uses AWS Lambda for serverless deployment and includes schema validation using Zod. The codebase is structured using ES Modules (ESM) for modern JavaScript module system compatibility. It sets up a complete type-safe API using tRPC with AWS Lambda integration, AWS X-Ray tracing, and AWS Lambda Powertools for observability.
5
+
6
+ ## Usage
7
+
8
+ You can generate a new tRPC backend in two ways:
9
+
10
+ ### 1. Using VSCode IDE
11
+
12
+ First, install the NX Console extension for VSCode:
13
+ 1. Open VSCode
14
+ 2. Go to Extensions (Ctrl+Shift+X / Cmd+Shift+X)
15
+ 3. Search for "Nx Console"
16
+ 4. Install [Nx Console](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console)
17
+
18
+ Then generate your API:
19
+ 1. Open the NX Console in VSCode
20
+ 2. Click on "Generate"
21
+ 3. Search for "trpc#backend"
22
+ 4. Fill in the required parameters in the form
23
+ 5. Click "Run"
24
+
25
+ ### 2. Using CLI
26
+
27
+ Generate the API:
28
+ ```bash
29
+ nx g @aws/nx-plugin:trpc#backend my-api --apiNamespace=@myorg --directory=apps/api
30
+ ```
31
+
32
+ You can also perform a dry-run to see what files would be generated without actually creating them:
33
+ ```bash
34
+ nx g @aws/nx-plugin:trpc#backend my-api --apiNamespace=@myorg --directory=apps/api --dry-run
35
+ ```
36
+
37
+ Both methods will create a new tRPC backend API in the specified directory with all the necessary configuration and infrastructure code.
38
+
39
+ ## Input Parameters
40
+
41
+ | Parameter | Type | Default | Description |
42
+ |-----------|------|---------|-------------|
43
+ | apiName* | string | - | The name of the API (required). Used to generate class names and file paths. |
44
+ | apiNamespace* | string | - | The namespace for the API (required). Must be in the format @scope or @scope/subscore. |
45
+ | directory | string | "packages" | The directory to store the application in. |
46
+ | unitTestRunner | string | "vitest" | Test runner for unit tests. Options: jest, vitest, none |
47
+
48
+ *Required parameter
49
+
50
+ ## Expected Output
51
+
52
+ The generator creates three main components:
53
+
54
+ ### 1. Backend API Code
55
+ ```
56
+ <directory>/<api-name>/backend/
57
+ ├── src/
58
+ │ ├── index.ts # Backend entry point with tRPC setup
59
+ │ └── lambdas/ # Lambda function handlers
60
+ │ ├── index.ts # Lambda exports
61
+ │ ├── router.ts # tRPC router definition
62
+ │ └── middleware.ts # Middleware plugins
63
+ ├── tsconfig.json # TypeScript configuration
64
+ └── project.json # Project configuration and build targets
65
+ ```
66
+
67
+ ### 2. Schema Code
68
+ ```
69
+ <directory>/<api-name>/schema/
70
+ ├── src/
71
+ │ └── index.ts # Shared schema definitions using Zod
72
+ ├── tsconfig.json # TypeScript configuration
73
+ └── project.json # Project configuration and build targets
74
+ ```
75
+
76
+ ### 3. Infrastructure Code
77
+ ```
78
+ common/constructs/
79
+ ├── src/
80
+ │ ├── <api-name>/ # Infrastructure specific to this API
81
+ │ │ └── index.ts # API infrastructure stack
82
+ │ └── index.ts # Exports for all constructs
83
+ ├── tsconfig.json # TypeScript configuration
84
+ └── project.json # Project configuration and build targets
85
+ ```
86
+
87
+ Additionally, it:
88
+ 1. Configures build settings for production deployment
89
+ 2. Installs required dependencies:
90
+ - @trpc/server
91
+ - zod
92
+ - aws-xray-sdk-core
93
+ - aws-cdk-lib
94
+ - constructs
95
+ - @aws-lambda-powertools/logger
96
+ - @aws-lambda-powertools/metrics
97
+ - @aws-lambda-powertools/tracer
98
+
99
+ ## Router and Middleware Setup
100
+
101
+ The generator creates a powerful tRPC router setup with integrated middleware for observability and error handling.
102
+
103
+ ### Router Configuration
104
+
105
+ The router is configured in `router.ts` with AWS Lambda integration:
106
+
107
+ ```typescript
108
+ import { initTRPC } from '@trpc/server';
109
+ import { awsLambdaRequestHandler } from '@trpc/server/adapters/aws-lambda';
110
+ import {
111
+ createErrorPlugin,
112
+ createLoggerPlugin,
113
+ createMetricsPlugin,
114
+ createTracerPlugin,
115
+ IMiddlewareContext,
116
+ } from './middleware.js';
117
+
118
+ // Initialize tRPC with context type
119
+ export type Context = IMiddlewareContext;
120
+ const t = initTRPC.context<Context>().create();
121
+
122
+ // Create base router and procedure which automatically instruments all middleware
123
+ export const router = t.router;
124
+ export const publicProcedure = t.procedure
125
+ .unstable_concat(createLoggerPlugin().loggerPlugin)
126
+ .unstable_concat(createTracerPlugin().tracerPlugin)
127
+ .unstable_concat(createMetricsPlugin().metricsPlugin)
128
+ .unstable_concat(createErrorPlugin().errorPlugin);
129
+
130
+ // Define your procedures here
131
+ const appRouter = router({
132
+ echo: publicProcedure
133
+ .input(z.string())
134
+ .output(EchoSchema)
135
+ .query((opts) => ({ result: opts.input })),
136
+ });
137
+
138
+ // Create Lambda handler
139
+ export const handler = awsLambdaRequestHandler({
140
+ router: appRouter
141
+ });
142
+
143
+ // Import this type in the frontend when setting up your integration
144
+ export type AppRouter = typeof appRouter;
145
+ ```
146
+
147
+ ### Middleware and Context
148
+
149
+ The generator includes four powerful middleware plugins whcih are automatically instrumented:
150
+
151
+ 1. **Logger Plugin**
152
+ - Automatically logs procedure execution
153
+ - Captures errors with detailed context
154
+ - Uses structured logging format
155
+
156
+ 2. **Metrics Plugin**
157
+ - Captures cold start metrics
158
+ - Tracks request counts
159
+ - Records success/error metrics
160
+ - Automatically publishes metrics to CloudWatch
161
+
162
+ 3. **Tracer Plugin**
163
+ - Integrates with AWS X-Ray
164
+ - Creates subsegments for each procedure
165
+ - Annotates cold starts
166
+ - Adds error metadata automatically
167
+
168
+ 4. **Error Plugin**
169
+ - Standardizes error handling
170
+ - Converts internal errors to tRPC errors
171
+ - Maintains error context for debugging
172
+
173
+ ### Using Context in Procedures
174
+
175
+ You can access the context in your procedures to utilize the observability tools:
176
+
177
+ ```typescript
178
+ const appRouter = router({
179
+ getData: publicProcedure
180
+ .input(z.string())
181
+ .query(async (opts) => {
182
+ // Access logger
183
+ opts.ctx.logger.info('Processing getData request', {
184
+ input: opts.input
185
+ });
186
+
187
+ // Add custom metrics
188
+ opts.ctx.metrics.addMetric('getData.calls', MetricUnit.Count, 1);
189
+
190
+ // Use tracer for subsegments
191
+ return opts.ctx.tracer.captureMethod('getData.process', async () => {
192
+ // Your business logic here
193
+ return { data: 'result' };
194
+ });
195
+ }),
196
+ });
197
+ ```
198
+
199
+ ## Infrastructure Architecture
200
+
201
+ ```mermaid
202
+ graph TD
203
+ subgraph AWS Cloud
204
+ APIGW[API Gateway] --> Lambda[Lambda Functions]
205
+ Lambda --> XRay[X-Ray Tracing]
206
+ Lambda --> CW[CloudWatch Logs]
207
+ Lambda --> Metrics[CloudWatch Metrics]
208
+ end
209
+ ```
210
+
211
+ The infrastructure stack deploys:
212
+ 1. **API Gateway**
213
+ - HTTP API endpoint
214
+ - Request validation
215
+ - CORS configuration
216
+
217
+ 2. **Lambda Functions**
218
+ - Serverless compute
219
+ - Auto-scaling
220
+ - Pay-per-use pricing
221
+
222
+ 3. **Observability**
223
+ - X-Ray distributed tracing
224
+ - CloudWatch Logs integration
225
+ - CloudWatch Metrics via Lambda Powertools
226
+ - Structured logging with Lambda Powertools
227
+
228
+
229
+ ## Using the Generated CDK Constructs
230
+
231
+ After generating your tRPC backend, you'll find a CDK construct in the `common/constructs` directory. Here's how to use it in your infrastructure:
232
+
233
+ ### Basic Usage
234
+
235
+ ```typescript
236
+ import { Stack } from 'aws-cdk-lib';
237
+ import { MyApi } from ':my-org/common-constructs';
238
+ import { HttpNoneAuthorizer } from '@aws-cdk-lib/aws-apigatewayv2-authorizers';
239
+
240
+ export class MyStack extends Stack {
241
+ constructor(scope: App, id: string) {
242
+ super(scope, id);
243
+
244
+ // Create the API with no authentication
245
+ const api = new MyApi(this, 'MyApi', {
246
+ defaultAuthorizer: new HttpNoneAuthorizer(),
247
+ allowedOrigins: ['http://localhost:4200']
248
+ });
249
+ }
250
+ }
251
+ ```
252
+
253
+ ### With Cognito Authentication
254
+
255
+ ```typescript
256
+ import * as cdk from 'aws-cdk-lib';
257
+ import { Construct } from 'constructs';
258
+ import { UserIdentity, MyApi } from ':my-org/common-constructs'
259
+ import { HttpIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
260
+
261
+ export class ApplicationStack extends cdk.Stack {
262
+ constructor(scope: Construct, id: string, props?: cdk.StackProps) {
263
+ super(scope, id, props);
264
+
265
+ const identity = new UserIdentity(this, 'UserIdentity');
266
+ const myapi = new MyApi(this, 'MyApi', {
267
+ defaultAuthorizer: new HttpIamAuthorizer(),
268
+ });
269
+ myapi.grantInvokeAccess(identity.identityPool.authenticatedRole);
270
+ }
271
+ }
272
+ ```
273
+
274
+ ### Granting Access to Other Services
275
+
276
+ ```typescript
277
+ import { Stack } from 'aws-cdk-lib';
278
+ import { MyApi } from ':my-org/common-constructs';
279
+ import { Role } from '@aws-cdk-lib/aws-iam';
280
+
281
+ export class MyStack extends Stack {
282
+ constructor(scope: App, id: string) {
283
+ super(scope, id);
284
+
285
+ const api = new MyApi(this, 'MyApi', {
286
+ // ... configuration
287
+ });
288
+
289
+ // Grant access to other roles if needed
290
+ const consumerRole = new Role(this, 'ConsumerRole', {
291
+ // ... role configuration
292
+ });
293
+
294
+ api.grantInvokeAccess(consumerRole);
295
+ }
296
+ }
297
+ ```
298
+
299
+ The API URL will be automatically registered in the RuntimeConfig system and can be accessed in your frontend application.
300
+
301
+ ## Schema Code and Zod
302
+
303
+ The generator creates a separate schema package that uses [Zod](https://zod.dev), a TypeScript-first schema declaration and validation library. This package can be shared between your backend and frontend code to ensure type safety across your entire application.
304
+
305
+ ### Introduction to Zod
306
+
307
+ Zod is a schema declaration and validation library designed specifically for TypeScript. It allows you to:
308
+ - Define schemas with a fluent API
309
+ - Automatically infer TypeScript types from schemas
310
+ - Validate data at runtime
311
+ - Create complex nested schemas
312
+ - Transform data during validation
313
+
314
+ For complete documentation, visit the [Zod documentation](https://zod.dev).
315
+
316
+ ### Defining Schemas
317
+
318
+ The generator creates a basic schema structure that you can extend:
319
+
320
+ ```typescript
321
+ import { z } from 'zod';
322
+
323
+ // Basic object schema
324
+ export const UserSchema = z.object({
325
+ id: z.string().uuid(),
326
+ email: z.string().email(),
327
+ name: z.string().min(2),
328
+ age: z.number().min(0).optional(),
329
+ });
330
+
331
+ // Infer the TypeScript type
332
+ export type User = z.infer<typeof UserSchema>;
333
+
334
+ // Input schema for creating a user
335
+ export const CreateUserSchema = UserSchema.omit({ id: true });
336
+
337
+ // Input schema for updating a user
338
+ export const UpdateUserSchema = CreateUserSchema.partial();
339
+
340
+ // Response schema for a list of users
341
+ export const UserListSchema = z.array(UserSchema);
342
+ ```
343
+
344
+ ### Common Schema Patterns
345
+
346
+ #### Nested Objects
347
+
348
+ ```typescript
349
+ import { z } from 'zod';
350
+
351
+ export const AddressSchema = z.object({
352
+ street: z.string(),
353
+ city: z.string(),
354
+ country: z.string(),
355
+ postalCode: z.string(),
356
+ });
357
+
358
+ export const CustomerSchema = z.object({
359
+ id: z.string().uuid(),
360
+ name: z.string(),
361
+ address: AddressSchema,
362
+ shippingAddresses: z.array(AddressSchema),
363
+ });
364
+ ```
365
+
366
+ #### Enums and Unions
367
+
368
+ ```typescript
369
+ import { z } from 'zod';
370
+
371
+ export const OrderStatusSchema = z.enum([
372
+ 'pending',
373
+ 'processing',
374
+ 'shipped',
375
+ 'delivered'
376
+ ]);
377
+
378
+ export const PaymentMethodSchema = z.union([
379
+ z.object({ type: z.literal('credit_card'), cardNumber: z.string() }),
380
+ z.object({ type: z.literal('paypal'), email: z.string().email() }),
381
+ ]);
382
+
383
+ export const OrderSchema = z.object({
384
+ id: z.string().uuid(),
385
+ status: OrderStatusSchema,
386
+ payment: PaymentMethodSchema,
387
+ });
388
+ ```
389
+
390
+ #### Request/Response Schemas
391
+
392
+ ```typescript
393
+ import { z } from 'zod';
394
+
395
+ // Pagination parameters
396
+ export const PaginationSchema = z.object({
397
+ page: z.number().min(1),
398
+ limit: z.number().min(1).max(100),
399
+ });
400
+
401
+ // Query parameters
402
+ export const SearchParamsSchema = z.object({
403
+ query: z.string().min(1),
404
+ filters: z.record(z.string()).optional(),
405
+ });
406
+
407
+ // API Response wrapper
408
+ export const ApiResponseSchema = <T extends z.ZodType>(dataSchema: T) =>
409
+ z.object({
410
+ success: z.boolean(),
411
+ data: dataSchema,
412
+ error: z.string().optional(),
413
+ metadata: z.record(z.unknown()).optional(),
414
+ });
415
+
416
+ // Usage example
417
+ export const GetUsersResponseSchema = ApiResponseSchema(
418
+ z.object({
419
+ users: z.array(UserSchema),
420
+ total: z.number(),
421
+ })
422
+ );
423
+ ```
424
+
425
+ ### Using Schemas with tRPC
426
+
427
+ Your schemas can be used directly in your tRPC procedures for input validation and type safety:
428
+
429
+ ```typescript
430
+ import { router, publicProcedure } from './router';
431
+ import {
432
+ UserSchema,
433
+ CreateUserSchema,
434
+ UpdateUserSchema,
435
+ SearchParamsSchema
436
+ } from ':my-org/schema';
437
+
438
+ export const userRouter = router({
439
+ // Create user with input validation
440
+ create: publicProcedure
441
+ .input(CreateUserSchema)
442
+ .output(UserSchema)
443
+ .mutation(async (opts) => {
444
+ // Input is fully typed and validated
445
+ const userData = opts.input;
446
+ // ... create user logic
447
+ }),
448
+
449
+ // Search users with pagination
450
+ search: publicProcedure
451
+ .input(SearchParamsSchema.merge(PaginationSchema))
452
+ .output(GetUsersResponseSchema)
453
+ .query(async (opts) => {
454
+ const { query, filters, page, limit } = opts.input;
455
+ // ... search logic
456
+ }),
457
+
458
+ // Update user with partial data
459
+ update: publicProcedure
460
+ .input(z.object({
461
+ id: z.string().uuid(),
462
+ data: UpdateUserSchema,
463
+ }))
464
+ .output(UserSchema)
465
+ .mutation(async (opts) => {
466
+ const { id, data } = opts.input;
467
+ // ... update logic
468
+ }),
469
+ });
470
+ ```
471
+
472
+ ### Schema Best Practices
473
+
474
+ 1. **Keep Schemas Centralized**: Store all schemas in the schema package to ensure they're easily shared between frontend and backend.
475
+
476
+ 2. **Use Type Inference**: Let TypeScript infer types from your schemas instead of maintaining separate type definitions:
477
+ ```typescript
478
+ // Do this:
479
+ export const UserSchema = z.object({ ... });
480
+ export type User = z.infer<typeof UserSchema>;
481
+
482
+ // Don't do this:
483
+ export interface User { ... }
484
+ export const UserSchema: z.ZodType<User> = z.object({ ... });
485
+ ```
486
+
487
+ 3. **Compose Schemas**: Build complex schemas by composing simpler ones:
488
+ ```typescript
489
+ const BaseUserSchema = z.object({
490
+ email: z.string().email(),
491
+ name: z.string(),
492
+ });
493
+
494
+ const AdminSchema = BaseUserSchema.extend({
495
+ permissions: z.array(z.string()),
496
+ });
497
+ ```
498
+
499
+ 4. **Version Your Schemas**: When making breaking changes, consider versioning your schemas:
500
+ ```typescript
501
+ export const UserSchemaV1 = z.object({ ... });
502
+ export const UserSchemaV2 = UserSchemaV1.extend({ ... });
503
+ ```
504
+
505
+ 5. **Document Your Schemas**: Add JSDoc comments to explain complex validation rules:
506
+ ```typescript
507
+ export const ConfigSchema = z.object({
508
+ /**
509
+ * API key must be in format: prefix_<32 chars>
510
+ * Example: myapp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
511
+ */
512
+ apiKey: z.string().regex(/^[a-z]+_[a-f0-9]{32}$/),
513
+ });
514
+ ```
515
+
516
+ ## Building the Application
517
+
518
+ To create a production build:
519
+ ```bash
520
+ nx build @my-org/my-api
521
+ ```
522
+
523
+ All built code is located in the `dist` folder at the root of your workspace. For example:
524
+ - Backend code: `dist/apps/api/my-api/backend`
525
+ - Schema code: `dist/apps/api/my-api/schema`
526
+
527
+ The production build:
528
+ - Bundles Lambda functions for optimal cold start performance
529
+ - Generates TypeScript declaration files
530
+ - Creates source maps for debugging
531
+ - Optimizes dependencies for AWS Lambda environment
532
+
533
+ ## Troubleshooting
534
+
535
+ ### `SyntaxError: Named export 'ListInferenceProfilesCommand' not found` or `TypeError: import_client_bedrock2.ListInferenceProfilesCommand is not a constructor`
536
+
537
+ If you see this error in a Lambda function related to `@aws-sdk`, it usually means that you are trying to use an operation which does not exist at runtime. This usually occurs when you are using the `@aws-sdk` provided by the Node Runtime. To bundle the required `@aws-sdk`, you can simply specify it in your `NodeJsFunction` as follows:
538
+
539
+ ```typescript
540
+ new NodejsFunction(this, 'MyApiHandler', {
541
+ ...,
542
+ bundling: {
543
+ bundleAwsSDK: true
544
+ },
545
+ });
546
+ ```
547
+
548
+ This will ensure that whichever `@aws-sdk` version you have installed locally will be the one that is used in the Lambda.
549
+
@@ -0,0 +1,110 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`trpc backend generator > should generate backend and schema projects > backend-index.ts 1`] = `
4
+ "export * from './lambdas/index.js';
5
+ "
6
+ `;
7
+
8
+ exports[`trpc backend generator > should generate backend and schema projects > schema-index.ts 1`] = `
9
+ "import { z } from 'zod';
10
+
11
+ export const EchoSchema = z.object({
12
+ result: z.string(),
13
+ });
14
+
15
+ export type IEcho = z.TypeOf<typeof EchoSchema>;
16
+ "
17
+ `;
18
+
19
+ exports[`trpc backend generator > should set up shared constructs > shared-constructs.ts 1`] = `
20
+ "import { Construct } from 'constructs';
21
+ import { Runtime, RuntimeFamily } from 'aws-cdk-lib/aws-lambda';
22
+ import { CfnOutput, Duration } from 'aws-cdk-lib';
23
+ import * as url from 'url';
24
+ import {
25
+ CorsHttpMethod,
26
+ HttpApi,
27
+ HttpMethod,
28
+ IHttpRouteAuthorizer,
29
+ } from 'aws-cdk-lib/aws-apigatewayv2';
30
+ import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
31
+ import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
32
+ import { Effect, IRole, PolicyStatement } from 'aws-cdk-lib/aws-iam';
33
+ import { RuntimeConfig } from '../runtime-config/index.js';
34
+
35
+ export interface TestApiProps {
36
+ readonly defaultAuthorizer: IHttpRouteAuthorizer;
37
+ readonly allowedOrigins?: string[];
38
+ }
39
+
40
+ export class TestApi extends Construct {
41
+ public readonly api: HttpApi;
42
+
43
+ constructor(scope: Construct, id: string, props: TestApiProps) {
44
+ super(scope, id);
45
+
46
+ const routerFunction = new NodejsFunction(this, 'TestApiHandler', {
47
+ timeout: Duration.seconds(30),
48
+ runtime: new Runtime('nodejs20.x', RuntimeFamily.NODEJS),
49
+ handler: 'handler',
50
+ entry: url.fileURLToPath(
51
+ new URL(
52
+ '../../../../../apps/test-api/backend/src/lambdas/router.ts',
53
+ import.meta.url
54
+ )
55
+ ),
56
+ environment: {
57
+ AWS_CONNECTION_REUSE_ENABLED: '1',
58
+ },
59
+ });
60
+
61
+ this.api = new HttpApi(this, 'TestApi', {
62
+ corsPreflight: {
63
+ allowOrigins: props.allowedOrigins ?? ['*'],
64
+ allowMethods: [CorsHttpMethod.ANY],
65
+ allowHeaders: [
66
+ 'authorization',
67
+ 'content-type',
68
+ 'x-amz-content-sha256',
69
+ 'x-amz-date',
70
+ 'x-amz-security-token',
71
+ ],
72
+ },
73
+ defaultAuthorizer: props.defaultAuthorizer,
74
+ });
75
+
76
+ this.api.addRoutes({
77
+ path: '/{proxy+}',
78
+ methods: [
79
+ HttpMethod.GET,
80
+ HttpMethod.DELETE,
81
+ HttpMethod.POST,
82
+ HttpMethod.PUT,
83
+ HttpMethod.PATCH,
84
+ HttpMethod.HEAD,
85
+ ],
86
+ integration: new HttpLambdaIntegration(
87
+ 'RouterIntegration',
88
+ routerFunction
89
+ ),
90
+ });
91
+
92
+ new CfnOutput(this, 'TestApiUrl', { value: this.api.url! });
93
+
94
+ RuntimeConfig.ensure(this).config.trpcApis = {
95
+ TestApi: this.api.url!,
96
+ };
97
+ }
98
+
99
+ public grantInvokeAccess(role: IRole) {
100
+ role.addToPrincipalPolicy(
101
+ new PolicyStatement({
102
+ effect: Effect.ALLOW,
103
+ actions: ['execute-api:Invoke'],
104
+ resources: [this.api.arnForExecuteApi('*', '/*', '*')],
105
+ })
106
+ );
107
+ }
108
+ }
109
+ "
110
+ `;
@@ -0,0 +1 @@
1
+ export * from './lambdas/index.js';
@@ -0,0 +1 @@
1
+ export * from './router.js';