@aws/nx-plugin 0.20.0 → 0.22.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 (79) hide show
  1. package/LICENSE-THIRD-PARTY +531 -21
  2. package/README.md +3 -9
  3. package/generators.json +14 -0
  4. package/package.json +7 -7
  5. package/src/cloudscape-website/cognito-auth/__snapshots__/generator.spec.ts.snap +1 -0
  6. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/core/user-identity.ts.template +1 -0
  7. package/src/infra/app/__snapshots__/generator.spec.ts.snap +54 -12
  8. package/src/infra/app/generator.js +14 -0
  9. package/src/infra/app/generator.js.map +1 -1
  10. package/src/open-api/ts-metadata/__snapshots__/generator.spec.ts.snap +49 -0
  11. package/src/open-api/ts-metadata/files/metadata.gen.ts.template +17 -0
  12. package/src/open-api/ts-metadata/generator.d.ts +16 -0
  13. package/src/open-api/ts-metadata/generator.js +32 -0
  14. package/src/open-api/ts-metadata/generator.js.map +1 -0
  15. package/src/{utils/http-api.d.ts → open-api/ts-metadata/schema.d.ts} +5 -2
  16. package/src/open-api/ts-metadata/schema.json +20 -0
  17. package/src/open-api/utils/codegen-data.js +3 -0
  18. package/src/open-api/utils/codegen-data.js.map +1 -1
  19. package/src/open-api/utils/normalise.js +6 -0
  20. package/src/open-api/utils/normalise.js.map +1 -1
  21. package/src/preset/__snapshots__/generator.spec.ts.snap +199 -0
  22. package/src/preset/files/README.md +107 -0
  23. package/src/preset/generator.d.ts +10 -0
  24. package/src/preset/generator.js +63 -0
  25. package/src/preset/generator.js.map +1 -0
  26. package/src/preset/schema.d.ts +7 -0
  27. package/src/preset/schema.json +14 -0
  28. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +972 -0
  29. package/src/py/fast-api/files/app/__name__/init.py.template +8 -0
  30. package/src/py/fast-api/generator.js +53 -15
  31. package/src/py/fast-api/generator.js.map +1 -1
  32. package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +2 -2
  33. package/src/py/fast-api/react/files/website/components/__apiNameClassName__Provider.tsx.template +1 -1
  34. package/src/py/fast-api/react/generator.js +6 -23
  35. package/src/py/fast-api/react/generator.js.map +1 -1
  36. package/src/py/fast-api/react/open-api.d.ts +14 -0
  37. package/src/py/fast-api/react/open-api.js +53 -0
  38. package/src/py/fast-api/react/open-api.js.map +1 -0
  39. package/src/py/fast-api/schema.d.ts +3 -0
  40. package/src/py/fast-api/schema.json +8 -0
  41. package/src/setup-tests.d.ts +2 -0
  42. package/src/setup-tests.js +14 -0
  43. package/src/setup-tests.js.map +1 -0
  44. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +1061 -88
  45. package/src/trpc/backend/files/backend/src/client/index.ts.template +2 -13
  46. package/src/trpc/backend/files/backend/src/index.ts.template +1 -0
  47. package/src/trpc/backend/files/backend/src/init.ts.template +4 -4
  48. package/src/trpc/backend/files/backend/src/middleware/index.ts.template +3 -2
  49. package/src/trpc/backend/files/backend/src/router.ts.template +11 -2
  50. package/src/trpc/backend/generator.js +12 -10
  51. package/src/trpc/backend/generator.js.map +1 -1
  52. package/src/trpc/backend/schema.d.ts +1 -1
  53. package/src/trpc/backend/schema.json +8 -0
  54. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +4 -19
  55. package/src/trpc/react/files/src/components/TrpcClients/TrpcProvider.tsx.template +2 -13
  56. package/src/trpc/react/generator.js +1 -1
  57. package/src/trpc/react/generator.js.map +1 -1
  58. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +26 -9
  59. package/src/ts/lib/generator.js +9 -0
  60. package/src/ts/lib/generator.js.map +1 -1
  61. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +1 -4
  62. package/src/ts/nx-generator/generator.js +4 -3
  63. package/src/ts/nx-generator/generator.js.map +1 -1
  64. package/src/utils/api-constructs/api-constructs.d.ts +29 -0
  65. package/src/utils/api-constructs/api-constructs.js +65 -0
  66. package/src/utils/api-constructs/api-constructs.js.map +1 -0
  67. package/src/utils/api-constructs/files/app/apis/http/__apiNameKebabCase__.ts.template +135 -0
  68. package/src/utils/api-constructs/files/app/apis/rest/__apiNameKebabCase__.ts.template +156 -0
  69. package/src/utils/api-constructs/files/core/api/http/http-api.ts.template +112 -0
  70. package/src/utils/api-constructs/files/core/api/rest/rest-api.ts.template +137 -0
  71. package/src/utils/api-constructs/files/core/api/trpc/trpc-utils.ts.template +67 -0
  72. package/src/utils/api-constructs/files/core/api/utils/utils.ts.template +223 -0
  73. package/src/utils/versions.d.ts +3 -3
  74. package/src/utils/versions.js +2 -2
  75. package/src/py/fast-api/files/common/constructs/src/app/http-apis/__apiNameKebabCase__.ts.template +0 -22
  76. package/src/trpc/backend/files/common/constructs/src/app/http-apis/__apiNameKebabCase__.ts.template +0 -22
  77. package/src/utils/files/http-api/common/constructs/src/core/http-api.ts.template +0 -87
  78. package/src/utils/http-api.js +0 -51
  79. package/src/utils/http-api.js.map +0 -1
@@ -0,0 +1,135 @@
1
+ import { Construct } from 'constructs';
2
+ import * as url from 'url';
3
+ import {
4
+ Code,
5
+ Runtime,
6
+ Function,
7
+ FunctionProps,
8
+ Tracing,
9
+ } from 'aws-cdk-lib/aws-lambda';
10
+ import { Duration } from 'aws-cdk-lib';
11
+ import { CorsHttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
12
+ import { HttpIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
13
+ import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
14
+ import {
15
+ HttpApiIntegration,
16
+ IntegrationBuilder,
17
+ } from '../../core/api/utils.js';
18
+ import { HttpApi } from '../../core/api/http-api.js';
19
+ <%_ if (backend.type === 'trpc') { _%>
20
+ import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';
21
+ import { AppRouter, appRouter } from '<%= backend.projectAlias %>';
22
+
23
+ // String union type for all API operation names
24
+ type Operations = Procedures<AppRouter>;
25
+ <%_ } else if (backend.type === 'fastapi') { _%>
26
+ import {
27
+ OPERATION_DETAILS,
28
+ Operations,
29
+ } from '../../generated/<%- apiNameKebabCase %>/metadata.gen.js';
30
+ <%_ } _%>
31
+
32
+ /**
33
+ * Properties for creating a <%= apiNameClassName %> construct
34
+ *
35
+ * @template TIntegrations - Map of operation names to their integrations
36
+ */
37
+ export interface <%= apiNameClassName %>Props<
38
+ TIntegrations extends Record<Operations, HttpApiIntegration>,
39
+ > {
40
+ /**
41
+ * Map of operation names to their API Gateway integrations
42
+ */
43
+ integrations: TIntegrations;
44
+ }
45
+
46
+ /**
47
+ * A CDK construct that creates and configures an AWS API Gateway HTTP API
48
+ * specifically for <%= apiNameClassName %>.
49
+ * @template TIntegrations - Map of operation names to their integrations
50
+ */
51
+ export class <%= apiNameClassName %><
52
+ TIntegrations extends Record<Operations, HttpApiIntegration>,
53
+ > extends HttpApi<Operations, TIntegrations> {
54
+ /**
55
+ * Creates default integrations for all operations, which implement each operation as
56
+ * its own individual lambda function.
57
+ *
58
+ * @param scope - The CDK construct scope
59
+ * @returns An IntegrationBuilder with default lambda integrations
60
+ */
61
+ public static defaultIntegrations = (scope: Construct) => {
62
+ return IntegrationBuilder.http({
63
+ <%_ if (backend.type === 'trpc') { _%>
64
+ operations: routerToOperations(appRouter),
65
+ <%_ } else if (backend.type === 'fastapi') { _%>
66
+ operations: OPERATION_DETAILS,
67
+ <%_ } _%>
68
+ defaultIntegrationOptions: {
69
+ <%_ if (backend.type === 'trpc') { _%>
70
+ runtime: Runtime.NODEJS_LATEST,
71
+ handler: 'index.handler',
72
+ code: Code.fromAsset(
73
+ url.fileURLToPath(
74
+ new URL(
75
+ '../../../../../../dist/<%- backend.dir %>/bundle',
76
+ import.meta.url,
77
+ ),
78
+ ),
79
+ ),
80
+ <%_ } else if (backend.type === 'fastapi') { _%>
81
+ runtime: Runtime.PYTHON_3_12,
82
+ handler: '<%= backend.apiNameSnakeCase %>.main.handler',
83
+ code: Code.fromAsset(
84
+ url.fileURLToPath(
85
+ new URL(
86
+ '../../../../../../dist/<%- backend.dir %>/bundle',
87
+ import.meta.url,
88
+ ),
89
+ ),
90
+ ),
91
+ <%_ } _%>
92
+ timeout: Duration.seconds(30),
93
+ tracing: Tracing.ACTIVE,
94
+ environment: {
95
+ AWS_CONNECTION_REUSE_ENABLED: '1',
96
+ },
97
+ } satisfies FunctionProps,
98
+ buildDefaultIntegration: (op, props: FunctionProps) => {
99
+ const handler = new Function(scope, `<%= apiNameClassName %>${op}Handler`, props);
100
+ return {
101
+ handler,
102
+ integration: new HttpLambdaIntegration(`<%= apiNameClassName %>${op}Integration`, handler),
103
+ };
104
+ },
105
+ });
106
+ };
107
+
108
+ constructor(
109
+ scope: Construct,
110
+ id: string,
111
+ props: <%= apiNameClassName %>Props<TIntegrations>,
112
+ ) {
113
+ super(scope, id, {
114
+ apiName: '<%= apiNameClassName %>',
115
+ corsPreflight: {
116
+ allowOrigins: ['*'],
117
+ allowMethods: [CorsHttpMethod.ANY],
118
+ allowHeaders: [
119
+ 'authorization',
120
+ 'content-type',
121
+ 'x-amz-content-sha256',
122
+ 'x-amz-date',
123
+ 'x-amz-security-token',
124
+ ],
125
+ },
126
+ defaultAuthorizer: new HttpIamAuthorizer(),
127
+ <%_ if (backend.type === 'trpc') { _%>
128
+ operations: routerToOperations(appRouter),
129
+ <%_ } else if (backend.type === 'fastapi') { _%>
130
+ operations: OPERATION_DETAILS,
131
+ <%_ } _%>
132
+ ...props,
133
+ });
134
+ }
135
+ }
@@ -0,0 +1,156 @@
1
+ import { Construct } from 'constructs';
2
+ import * as url from 'url';
3
+ import {
4
+ Code,
5
+ Runtime,
6
+ Function,
7
+ FunctionProps,
8
+ Tracing,
9
+ } from 'aws-cdk-lib/aws-lambda';
10
+ import {
11
+ AuthorizationType,
12
+ Cors,
13
+ LambdaIntegration,
14
+ } from 'aws-cdk-lib/aws-apigateway';
15
+ import { Duration, Stack } from 'aws-cdk-lib';
16
+ import {
17
+ PolicyDocument,
18
+ PolicyStatement,
19
+ Effect,
20
+ AccountPrincipal,
21
+ AnyPrincipal,
22
+ } from 'aws-cdk-lib/aws-iam';
23
+ import {
24
+ IntegrationBuilder,
25
+ RestApiIntegration,
26
+ } from '../../core/api/utils.js';
27
+ import { RestApi } from '../../core/api/rest-api.js';
28
+ <%_ if (backend.type === 'trpc') { _%>
29
+ import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';
30
+ import { AppRouter, appRouter } from '<%= backend.projectAlias %>';
31
+
32
+ // String union type for all API operation names
33
+ type Operations = Procedures<AppRouter>;
34
+ <%_ } else if (backend.type === 'fastapi') { _%>
35
+ import {
36
+ OPERATION_DETAILS,
37
+ Operations,
38
+ } from '../../generated/<%- apiNameKebabCase %>/metadata.gen.js';
39
+ <%_ } _%>
40
+
41
+ /**
42
+ * Properties for creating a <%= apiNameClassName %> construct
43
+ *
44
+ * @template TIntegrations - Map of operation names to their integrations
45
+ */
46
+ export interface <%= apiNameClassName %>Props<
47
+ TIntegrations extends Record<Operations, RestApiIntegration>,
48
+ > {
49
+ /**
50
+ * Map of operation names to their API Gateway integrations
51
+ */
52
+ integrations: TIntegrations;
53
+ }
54
+
55
+ /**
56
+ * A CDK construct that creates and configures an AWS API Gateway REST API
57
+ * specifically for <%= apiNameClassName %>.
58
+ * @template TIntegrations - Map of operation names to their integrations
59
+ */
60
+ export class <%= apiNameClassName %><
61
+ TIntegrations extends Record<Operations, RestApiIntegration>,
62
+ > extends RestApi<Operations, TIntegrations> {
63
+ /**
64
+ * Creates default integrations for all operations, which implement each operation as
65
+ * its own individual lambda function.
66
+ *
67
+ * @param scope - The CDK construct scope
68
+ * @returns An IntegrationBuilder with default lambda integrations
69
+ */
70
+ public static defaultIntegrations = (scope: Construct) => {
71
+ return IntegrationBuilder.rest({
72
+ <%_ if (backend.type === 'trpc') { _%>
73
+ operations: routerToOperations(appRouter),
74
+ <%_ } else if (backend.type === 'fastapi') { _%>
75
+ operations: OPERATION_DETAILS,
76
+ <%_ } _%>
77
+ defaultIntegrationOptions: {
78
+ <%_ if (backend.type === 'trpc') { _%>
79
+ runtime: Runtime.NODEJS_LATEST,
80
+ handler: 'index.handler',
81
+ code: Code.fromAsset(
82
+ url.fileURLToPath(
83
+ new URL(
84
+ '../../../../../../dist/<%- backend.dir %>/bundle',
85
+ import.meta.url,
86
+ ),
87
+ ),
88
+ ),
89
+ <%_ } else if (backend.type === 'fastapi') { _%>
90
+ runtime: Runtime.PYTHON_3_12,
91
+ handler: '<%= backend.apiNameSnakeCase %>.main.handler',
92
+ code: Code.fromAsset(
93
+ url.fileURLToPath(
94
+ new URL(
95
+ '../../../../../../dist/<%- backend.dir %>/bundle',
96
+ import.meta.url,
97
+ ),
98
+ ),
99
+ ),
100
+ <%_ } _%>
101
+ timeout: Duration.seconds(30),
102
+ tracing: Tracing.ACTIVE,
103
+ environment: {
104
+ AWS_CONNECTION_REUSE_ENABLED: '1',
105
+ },
106
+ } satisfies FunctionProps,
107
+ buildDefaultIntegration: (op, props: FunctionProps) => {
108
+ const handler = new Function(scope, `<%= apiNameClassName %>${op}Handler`, props);
109
+ return { handler, integration: new LambdaIntegration(handler) };
110
+ },
111
+ });
112
+ };
113
+
114
+ constructor(
115
+ scope: Construct,
116
+ id: string,
117
+ props: <%= apiNameClassName %>Props<TIntegrations>,
118
+ ) {
119
+ super(scope, id, {
120
+ apiName: '<%= apiNameClassName %>',
121
+ defaultMethodOptions: {
122
+ authorizationType: AuthorizationType.IAM,
123
+ },
124
+ defaultCorsPreflightOptions: {
125
+ allowOrigins: Cors.ALL_ORIGINS,
126
+ allowMethods: Cors.ALL_METHODS,
127
+ },
128
+ policy: new PolicyDocument({
129
+ statements: [
130
+ // Here we grant any AWS credentials from the account that the project is deployed in to call the api.
131
+ // Machine to machine fine-grained access can be defined here using more specific principals (eg roles or
132
+ // users) and resources (eg which api paths may be invoked by which principal) if required.
133
+ new PolicyStatement({
134
+ effect: Effect.ALLOW,
135
+ principals: [new AccountPrincipal(Stack.of(scope).account)],
136
+ actions: ['execute-api:Invoke'],
137
+ resources: ['execute-api:/*'],
138
+ }),
139
+ // Open up OPTIONS to allow browsers to make unauthenticated preflight requests
140
+ new PolicyStatement({
141
+ effect: Effect.ALLOW,
142
+ principals: [new AnyPrincipal()],
143
+ actions: ['execute-api:Invoke'],
144
+ resources: ['execute-api:/*/OPTIONS/*'],
145
+ }),
146
+ ],
147
+ }),
148
+ <%_ if (backend.type === 'trpc') { _%>
149
+ operations: routerToOperations(appRouter),
150
+ <%_ } else if (backend.type === 'fastapi') { _%>
151
+ operations: OPERATION_DETAILS,
152
+ <%_ } _%>
153
+ ...props,
154
+ });
155
+ }
156
+ }
@@ -0,0 +1,112 @@
1
+ import { Construct } from 'constructs';
2
+ import { RuntimeConfig } from '../runtime-config.js';
3
+ import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
4
+ import { HttpApiIntegration, OperationDetails } from './utils.js';
5
+ import { CfnOutput } from 'aws-cdk-lib';
6
+ import {
7
+ HttpApi as _HttpApi,
8
+ HttpApiProps as _HttpApiProps,
9
+ HttpMethod,
10
+ } from 'aws-cdk-lib/aws-apigatewayv2';
11
+
12
+ /**
13
+ * Properties for creating an HttpApi construct.
14
+ *
15
+ * @template TIntegrations - Record mapping operation names to their integrations
16
+ * @template TOperation - String literal type representing operation names
17
+ */
18
+ export interface HttpApiProps<
19
+ TIntegrations extends Record<TOperation, HttpApiIntegration>,
20
+ TOperation extends string,
21
+ > extends _HttpApiProps {
22
+ /**
23
+ * Unique name for the API, used in runtime configuration
24
+ */
25
+ readonly apiName: string;
26
+ /**
27
+ * Map of operation names to their API path and HTTP method details
28
+ */
29
+ readonly operations: Record<TOperation, OperationDetails>;
30
+ /**
31
+ * Map of operation names to their API Gateway integrations
32
+ */
33
+ readonly integrations: TIntegrations;
34
+ }
35
+
36
+ /**
37
+ * A CDK construct that creates and configures an AWS API Gateway HTTP API.
38
+ *
39
+ * This class extends the base CDK HttpApi with additional functionality:
40
+ * - Type-safe operation and integration management
41
+ * - Automatic resource creation based on path patterns
42
+ * - Integration with runtime configuration for client discovery
43
+ *
44
+ * @template TOperation - String literal type representing operation names
45
+ * @template TIntegrations - Record mapping operation names to their integrations
46
+ */
47
+ export class HttpApi<
48
+ TOperation extends string,
49
+ TIntegrations extends Record<TOperation, HttpApiIntegration>,
50
+ > extends Construct {
51
+ /** The underlying CDK HttpApi instance */
52
+ public readonly api: _HttpApi;
53
+
54
+ /** Map of operation names to their API Gateway integrations */
55
+ public readonly integrations: TIntegrations;
56
+
57
+ constructor(
58
+ scope: Construct,
59
+ id: string,
60
+ {
61
+ apiName,
62
+ operations,
63
+ integrations,
64
+ ...props
65
+ }: HttpApiProps<TIntegrations, TOperation>,
66
+ ) {
67
+ super(scope, id);
68
+ this.integrations = integrations;
69
+
70
+ // Create the API Gateway REST API with logging enabled
71
+ this.api = new _HttpApi(this, 'Api', {
72
+ ...props,
73
+ });
74
+
75
+ // Create API resources and methods for each operation
76
+ (Object.entries(operations) as [TOperation, OperationDetails][]).map(
77
+ ([op, details]) => {
78
+ this.api.addRoutes({
79
+ path: details.path.startsWith('/')
80
+ ? details.path
81
+ : `/${details.path}`,
82
+ methods: [details.method as HttpMethod],
83
+ integration: integrations[op].integration,
84
+ ...integrations[op].options,
85
+ });
86
+ },
87
+ );
88
+
89
+ new CfnOutput(this, `${apiName}Url`, {
90
+ value: this.api.url!,
91
+ });
92
+
93
+ // Register the API URL in runtime configuration for client discovery
94
+ RuntimeConfig.ensure(this).config.apis = {
95
+ ...RuntimeConfig.ensure(this).config.apis!,
96
+ [apiName]: this.api.url!,
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Grants IAM permissions to invoke any method on this API.
102
+ *
103
+ * @param grantee - The IAM principal to grant permissions to
104
+ */
105
+ public grantInvokeAccess(grantee: IGrantable) {
106
+ Grant.addToPrincipal({
107
+ grantee,
108
+ actions: ['execute-api:Invoke'],
109
+ resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
110
+ });
111
+ }
112
+ }
@@ -0,0 +1,137 @@
1
+ import { Construct } from 'constructs';
2
+ import {
3
+ RestApi as _RestApi,
4
+ RestApiProps as _RestApiProps,
5
+ AccessLogFormat,
6
+ IResource,
7
+ LogGroupLogDestination,
8
+ MethodLoggingLevel,
9
+ } from 'aws-cdk-lib/aws-apigateway';
10
+ import { LogGroup } from 'aws-cdk-lib/aws-logs';
11
+ import { RuntimeConfig } from '../runtime-config.js';
12
+ import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
13
+ import { OperationDetails, RestApiIntegration } from './utils.js';
14
+
15
+ /**
16
+ * Properties for creating a RestApi construct.
17
+ *
18
+ * @template TIntegrations - Record mapping operation names to their integrations
19
+ * @template TOperation - String literal type representing operation names
20
+ */
21
+ export interface RestApiProps<
22
+ TIntegrations extends Record<TOperation, RestApiIntegration>,
23
+ TOperation extends string,
24
+ > extends _RestApiProps {
25
+ /**
26
+ * Unique name for the API, used in runtime configuration
27
+ */
28
+ readonly apiName: string;
29
+ /**
30
+ * Map of operation names to their API path and HTTP method details
31
+ */
32
+ readonly operations: Record<TOperation, OperationDetails>;
33
+ /**
34
+ * Map of operation names to their API Gateway integrations
35
+ */
36
+ readonly integrations: TIntegrations;
37
+ }
38
+
39
+ /**
40
+ * A CDK construct that creates and configures an AWS API Gateway REST API.
41
+ *
42
+ * This class extends the base CDK RestApi with additional functionality:
43
+ * - Type-safe operation and integration management
44
+ * - Automatic resource creation based on path patterns
45
+ * - Integration with runtime configuration for client discovery
46
+ *
47
+ * @template TOperation - String literal type representing operation names
48
+ * @template TIntegrations - Record mapping operation names to their integrations
49
+ */
50
+ export class RestApi<
51
+ TOperation extends string,
52
+ TIntegrations extends Record<TOperation, RestApiIntegration>,
53
+ > extends Construct {
54
+ /** The underlying CDK RestApi instance */
55
+ public readonly api: _RestApi;
56
+
57
+ /** Map of operation names to their API Gateway integrations */
58
+ public readonly integrations: TIntegrations;
59
+
60
+ constructor(
61
+ scope: Construct,
62
+ id: string,
63
+ {
64
+ apiName,
65
+ operations,
66
+ integrations,
67
+ ...props
68
+ }: RestApiProps<TIntegrations, TOperation>,
69
+ ) {
70
+ super(scope, id);
71
+ this.integrations = integrations;
72
+
73
+ // Create the API Gateway REST API with logging enabled
74
+ this.api = new _RestApi(this, 'Api', {
75
+ deployOptions: {
76
+ accessLogDestination: new LogGroupLogDestination(
77
+ new LogGroup(this, 'AccessLogs'),
78
+ ),
79
+ accessLogFormat: AccessLogFormat.clf(),
80
+ loggingLevel: MethodLoggingLevel.INFO,
81
+ },
82
+ ...props,
83
+ });
84
+
85
+ // Create API resources and methods for each operation
86
+ (Object.entries(operations) as [TOperation, OperationDetails][]).map(
87
+ ([op, details]) => {
88
+ const resource = this.getOrCreateResource(
89
+ this.api.root,
90
+ (details.path.startsWith('/')
91
+ ? details.path.slice(1)
92
+ : details.path
93
+ ).split('/'),
94
+ );
95
+ resource.addMethod(details.method, integrations[op].integration, integrations[op].options);
96
+ },
97
+ );
98
+
99
+ // Register the API URL in runtime configuration for client discovery
100
+ RuntimeConfig.ensure(this).config.apis = {
101
+ ...RuntimeConfig.ensure(this).config.apis!,
102
+ [apiName]: this.api.url!,
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Recursively creates or retrieves API Gateway resources based on a path pattern.
108
+ *
109
+ * @param resource - The parent API Gateway resource
110
+ * @param pathParts - Array of path segments to create or retrieve
111
+ * @returns The API Gateway resource at the end of the path
112
+ */
113
+ private getOrCreateResource(
114
+ resource: IResource,
115
+ [nextPathPart, ...pathParts]: string[],
116
+ ): IResource {
117
+ if (!nextPathPart) {
118
+ return resource;
119
+ }
120
+ const childResource =
121
+ resource.getResource(nextPathPart) ?? resource.addResource(nextPathPart);
122
+ return this.getOrCreateResource(childResource, pathParts);
123
+ }
124
+
125
+ /**
126
+ * Grants IAM permissions to invoke any method on this API.
127
+ *
128
+ * @param grantee - The IAM principal to grant permissions to
129
+ */
130
+ public grantInvokeAccess(grantee: IGrantable) {
131
+ Grant.addToPrincipal({
132
+ grantee,
133
+ actions: ['execute-api:Invoke'],
134
+ resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
135
+ });
136
+ }
137
+ }
@@ -0,0 +1,67 @@
1
+ import { TRPCRouterRecord, AnyTRPCRouter } from '@trpc/server';
2
+ import { OperationDetails } from './utils.js';
3
+ import { HttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
4
+
5
+ /**
6
+ * Helper type that recursively extracts procedure names from a tRPC router.
7
+ * This type traverses the router structure and builds fully qualified procedure names
8
+ * with dot notation for nested routers.
9
+ *
10
+ * @template T - The tRPC router record type
11
+ * @template Prefix - The current path prefix for nested procedures
12
+ */
13
+ type _Procedures<T extends TRPCRouterRecord, Prefix extends string = ''> = {
14
+ [K in keyof T]: K extends string
15
+ ? T[K] extends TRPCRouterRecord
16
+ ? _Procedures<T[K], `${Prefix}${K}.`>
17
+ : `${Prefix}${K}`
18
+ : never;
19
+ }[keyof T];
20
+
21
+ /**
22
+ * Extracts all procedure names from a tRPC router as a union of string literals.
23
+ * This type is used to provide type-safe access to procedure names throughout the API.
24
+ *
25
+ * @template TRouter - The tRPC router type
26
+ */
27
+ export type Procedures<TRouter extends AnyTRPCRouter> = _Procedures<
28
+ TRouter['_def']['record']
29
+ >;
30
+
31
+ /**
32
+ * Converts a tRPC router to a map of API operations.
33
+ * This method recursively traverses the router structure and creates operation details
34
+ * for each procedure, mapping queries to GET methods and mutations to POST methods.
35
+ *
36
+ * @param router - The tRPC router to convert
37
+ * @param prefix - The current path prefix for nested procedures
38
+ * @returns A map of procedure names to their API operation details
39
+ */
40
+ export const routerToOperations = <TRouter extends AnyTRPCRouter>(
41
+ router: TRouter,
42
+ prefix = '',
43
+ ): Record<Procedures<TRouter>, OperationDetails> => {
44
+ return Object.fromEntries(
45
+ Object.entries(router._def.procedures).flatMap(
46
+ ([op, procedureOrRouter]: [string, any]) => {
47
+ const fullPath = prefix ? `${prefix}.${op}` : op;
48
+ return procedureOrRouter._def?.router
49
+ ? Object.entries(
50
+ routerToOperations<TRouter>(procedureOrRouter, fullPath),
51
+ )
52
+ : [
53
+ [
54
+ fullPath,
55
+ {
56
+ path: fullPath,
57
+ method:
58
+ procedureOrRouter._def.type === 'query'
59
+ ? HttpMethod.GET
60
+ : HttpMethod.POST,
61
+ },
62
+ ],
63
+ ];
64
+ },
65
+ ),
66
+ ) as Record<Procedures<TRouter>, OperationDetails>;
67
+ };