@aws/nx-plugin 0.1.6 → 0.2.1

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 (126) hide show
  1. package/LICENSE-THIRD-PARTY +1554 -3241
  2. package/generators.json +1 -13
  3. package/package.json +14 -14
  4. package/src/cloudscape-website/app/README.md +84 -48
  5. package/src/cloudscape-website/app/__snapshots__/generator.spec.ts.snap +168 -233
  6. package/src/cloudscape-website/app/files/app/README.md.template +44 -0
  7. package/src/cloudscape-website/app/files/app/src/layouts/App/index.tsx.template +40 -43
  8. package/src/cloudscape-website/app/files/app/src/layouts/App/navitems.ts.template +3 -3
  9. package/src/cloudscape-website/app/files/app/src/layouts/Routes/index.tsx.template +4 -6
  10. package/src/cloudscape-website/app/files/app/src/main.tsx.template +7 -10
  11. package/src/cloudscape-website/app/files/app/src/pages/Home/index.tsx.template +0 -2
  12. package/src/cloudscape-website/app/files/common/constructs/src/app/static-websites/__websiteNameKebabCase__.ts.template +13 -0
  13. package/src/cloudscape-website/app/files/common/constructs/src/{__websiteNameKebabCase__ → core}/static-website.ts.template +79 -144
  14. package/src/cloudscape-website/app/generator.js +90 -74
  15. package/src/cloudscape-website/app/generator.js.map +1 -1
  16. package/src/cloudscape-website/app/schema.d.ts +3 -5
  17. package/src/cloudscape-website/app/schema.json +1 -24
  18. package/src/cloudscape-website/cognito-auth/README.md +53 -32
  19. package/src/cloudscape-website/cognito-auth/__snapshots__/generator.spec.ts.snap +162 -124
  20. package/src/cloudscape-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +53 -39
  21. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/core/user-identity.ts.template +168 -0
  22. package/src/cloudscape-website/cognito-auth/generator.js +130 -47
  23. package/src/cloudscape-website/cognito-auth/generator.js.map +1 -1
  24. package/src/cloudscape-website/cognito-auth/schema.d.ts +1 -0
  25. package/src/cloudscape-website/cognito-auth/schema.json +7 -1
  26. package/src/cloudscape-website/runtime-config/__snapshots__/generator.spec.ts.snap +20 -15
  27. package/src/cloudscape-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +7 -10
  28. package/src/cloudscape-website/runtime-config/files/app/hooks/useRuntimeConfig.tsx.template +13 -0
  29. package/src/cloudscape-website/runtime-config/generator.js +4 -2
  30. package/src/cloudscape-website/runtime-config/generator.js.map +1 -1
  31. package/src/infra/app/README.md +71 -46
  32. package/src/infra/app/__snapshots__/generator.spec.ts.snap +184 -305
  33. package/src/infra/app/files/app/README.md.template +76 -0
  34. package/src/infra/app/files/app/src/main.ts.template +18 -0
  35. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/aws-prototyping.guard +1282 -0
  36. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/cfn-nag.guard +6839 -0
  37. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/hipaa-security.guard +2807 -0
  38. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/nist-csf.guard +2585 -0
  39. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/pci-dss-3-2-1.guard +2236 -0
  40. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-reliability-pillar.guard +885 -0
  41. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-security-pillar.guard +2205 -0
  42. package/src/infra/app/files/common/constructs/src/core/cfn-guard.ts.template +63 -0
  43. package/src/infra/app/generator.js +36 -7
  44. package/src/infra/app/generator.js.map +1 -1
  45. package/src/infra/app/schema.d.ts +10 -1
  46. package/src/infra/app/schema.json +16 -8
  47. package/src/trpc/backend/README.md +102 -80
  48. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +42 -19
  49. package/src/trpc/backend/files/backend/README.md.template +33 -0
  50. package/src/trpc/backend/files/common/constructs/src/app/trpc-apis/__apiNameKebabCase__.ts.template +18 -0
  51. package/src/trpc/backend/files/common/constructs/src/{__apiNameKebabCase__/index.ts.template → core/trpc-api.ts.template} +12 -16
  52. package/src/trpc/backend/files/schema/README.md.template +33 -0
  53. package/src/trpc/backend/generator.js +30 -44
  54. package/src/trpc/backend/generator.js.map +1 -1
  55. package/src/trpc/backend/schema.d.ts +3 -1
  56. package/src/trpc/backend/schema.json +8 -13
  57. package/src/trpc/react/README.md +46 -66
  58. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +104 -65
  59. package/src/trpc/react/files/src/components/TrpcClients/IsolatedTrpcProvider.tsx.template +75 -0
  60. package/src/trpc/react/files/src/components/TrpcClients/TrpcApis.tsx.template +1 -0
  61. package/src/trpc/react/files/src/components/TrpcClients/TrpcClientProviders.tsx.template +10 -0
  62. package/src/trpc/react/files/src/components/TrpcClients/index.tsx.template +5 -0
  63. package/src/trpc/react/files/src/hooks/useSigV4.tsx.template +38 -0
  64. package/src/trpc/react/files/src/hooks/use__apiNameClassName__.tsx.template +3 -0
  65. package/src/trpc/react/generator.js +124 -25
  66. package/src/trpc/react/generator.js.map +1 -1
  67. package/src/trpc/react/schema.json +2 -2
  68. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +47 -93
  69. package/src/ts/lib/eslint.d.ts +1 -2
  70. package/src/ts/lib/eslint.js +62 -21
  71. package/src/ts/lib/eslint.js.map +1 -1
  72. package/src/ts/lib/files/README.md.template +33 -0
  73. package/src/ts/lib/generator.js +44 -5
  74. package/src/ts/lib/generator.js.map +1 -1
  75. package/src/ts/lib/schema.d.ts +1 -4
  76. package/src/ts/lib/schema.json +2 -21
  77. package/src/ts/lib/ts-project-utils.js +3 -18
  78. package/src/ts/lib/ts-project-utils.js.map +1 -1
  79. package/src/ts/lib/vitest.js +12 -0
  80. package/src/ts/lib/vitest.js.map +1 -1
  81. package/src/utils/ast.d.ts +13 -0
  82. package/src/utils/ast.js +102 -0
  83. package/src/utils/ast.js.map +1 -0
  84. package/src/utils/files/common/constructs/src/app/index.ts.template +0 -0
  85. package/src/utils/files/common/constructs/src/{runtime-config → core}/runtime-config.ts.template +3 -5
  86. package/src/utils/files/common/constructs/src/index.ts.template +2 -1
  87. package/src/utils/files/common/readme/README.md.template +33 -0
  88. package/src/utils/files/common/types/src/runtime-config.ts.template +2 -13
  89. package/src/utils/format.d.ts +1 -1
  90. package/src/utils/format.js +2 -2
  91. package/src/utils/format.js.map +1 -1
  92. package/src/utils/names.d.ts +2 -0
  93. package/src/utils/names.js +27 -0
  94. package/src/utils/names.js.map +1 -0
  95. package/src/utils/npm-scope.js.map +1 -1
  96. package/src/utils/paths.js.map +1 -1
  97. package/src/utils/shared-constructs.js +37 -4
  98. package/src/utils/shared-constructs.js.map +1 -1
  99. package/src/utils/test.d.ts +2 -0
  100. package/src/utils/test.js +19 -0
  101. package/src/utils/test.js.map +1 -0
  102. package/src/utils/versions.d.ts +15 -9
  103. package/src/utils/versions.js +14 -8
  104. package/src/utils/versions.js.map +1 -1
  105. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/cloudfront-web-acl.ts.template +0 -317
  106. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/index.ts.template +0 -4
  107. package/src/cloudscape-website/app/files/common/constructs/src/__websiteNameKebabCase__/webacl_event_handler/index.ts.template +0 -301
  108. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/index.ts.template +0 -4
  109. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/user-identity.ts.template +0 -66
  110. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/identity/userpool-with-mfa.ts.template +0 -70
  111. package/src/gitlab/generator.d.ts +0 -8
  112. package/src/gitlab/generator.js +0 -16
  113. package/src/gitlab/generator.js.map +0 -1
  114. package/src/gitlab/schema.d.ts +0 -9
  115. package/src/gitlab/schema.json +0 -52
  116. package/src/infra/app/files/src/main.ts.template +0 -37
  117. package/src/trpc/react/files/src/components/TRPCClientProvider/index.tsx.template +0 -34
  118. package/src/trpc/react/files/src/hooks/useTrpc.tsx.template +0 -5
  119. package/src/ts/cjs-to-esm/generator.d.ts +0 -12
  120. package/src/ts/cjs-to-esm/generator.js +0 -189
  121. package/src/ts/cjs-to-esm/generator.js.map +0 -1
  122. package/src/ts/cjs-to-esm/schema.d.ts +0 -9
  123. package/src/ts/cjs-to-esm/schema.json +0 -28
  124. /package/src/infra/app/files/{cdk.json → app/cdk.json} +0 -0
  125. /package/src/infra/app/files/{src → app/src}/stacks/application-stack.ts.template +0 -0
  126. /package/src/utils/files/common/constructs/src/{runtime-config → core}/index.ts.template +0 -0
@@ -0,0 +1,63 @@
1
+ import * as url from 'url';
2
+ import { CfnGuardValidator as _CfnGuardValidator } from '@cdklabs/cdk-validator-cfnguard';
3
+ import { IConstruct } from 'constructs';
4
+ import { CfnResource } from 'aws-cdk-lib';
5
+
6
+ /**
7
+ * Suppresses a rule for a construct tree.
8
+ *
9
+ * @param construct The root construct to suppress the rule for.
10
+ * @param ruleName The name of the rule to suppress.
11
+ * @param predicate A predicate function that determines whether the rule should be suppressed for the given construct or any of its descendants.
12
+ *
13
+ * @example
14
+ * The following example suppresses the RULE_NAME for the given construct.
15
+ * suppressRule(construct, 'RULE_NAME')
16
+ *
17
+ * @example
18
+ * The following example suppresses the RULE_NAME for the construct or any of its descendants if it is an instance of Bucket:
19
+ * suppressRule(construct, 'RULE_NAME', (construct) => construct instanceof Bucket)
20
+ */
21
+ export const suppressRule = (
22
+ construct: IConstruct,
23
+ ruleName: string,
24
+ predicate?: (construct: IConstruct) => boolean,
25
+ ) => {
26
+ const resources = (
27
+ predicate ? construct.node.findAll().filter(predicate) : [construct]
28
+ )
29
+ .map((resource) => resource.node.defaultChild)
30
+ .filter((resource) => CfnResource.isCfnResource(resource));
31
+
32
+ resources.forEach((resource) => {
33
+ const metadata = resource.getMetadata('guard') || {};
34
+ metadata['SuppressedRules'] = [
35
+ ...(metadata['SuppressedRules'] ?? []),
36
+ ruleName,
37
+ ];
38
+ resource.addMetadata('guard', metadata);
39
+ });
40
+ };
41
+
42
+ export enum RuleSet {
43
+ AWS_PROTOTYPING = 'aws-prototyping.guard',
44
+ CFN_NAG = 'cfn-nag.guard',
45
+ HIPAA = 'hipaa-security.guard',
46
+ NIST_CSF = 'nist-csf.guard',
47
+ PCI_DSS_3_2_1 = 'pci-dss-3-2-1.guard',
48
+ WELL_ARCHITECTED_RELIABILITY = 'wa-reliability-pillar.guard',
49
+ WELL_ARCHITECTED_SECURITY = 'wa-security-pillar.guard',
50
+ }
51
+
52
+ export class CfnGuardValidator extends _CfnGuardValidator {
53
+ constructor(guardRule: RuleSet) {
54
+ super({
55
+ controlTowerRulesEnabled: false,
56
+ rules: [
57
+ url.fileURLToPath(
58
+ new URL(`./cfn-guard-rules/${guardRule}`, import.meta.url),
59
+ ),
60
+ ],
61
+ });
62
+ }
63
+ }
@@ -9,11 +9,16 @@ const tslib_1 = require("tslib");
9
9
  const devkit_1 = require("@nx/devkit");
10
10
  const generator_1 = tslib_1.__importStar(require("../../ts/lib/generator"));
11
11
  const versions_1 = require("../../utils/versions");
12
+ const npm_scope_1 = require("../../utils/npm-scope");
13
+ const shared_constructs_1 = require("../../utils/shared-constructs");
14
+ const ast_1 = require("../../utils/ast");
15
+ const path_1 = tslib_1.__importDefault(require("path"));
12
16
  const format_1 = require("../../utils/format");
13
17
  function infraGenerator(tree, schema) {
14
18
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
15
19
  const lib = (0, generator_1.getTsLibDetails)(tree, schema);
16
- const tsLibGeneratorCallback = yield (0, generator_1.default)(tree, schema);
20
+ yield (0, generator_1.default)(tree, schema);
21
+ yield (0, shared_constructs_1.sharedConstructsGenerator)(tree);
17
22
  const synthDirFromRoot = `/dist/${lib.dir}/cdk.out`;
18
23
  const synthDirFromProject = lib.dir
19
24
  .split('/')
@@ -21,13 +26,28 @@ function infraGenerator(tree, schema) {
21
26
  .join('/') + synthDirFromRoot;
22
27
  const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, lib.fullyQualifiedName);
23
28
  const libraryRoot = projectConfig.root;
29
+ const npmScopePrefix = (0, npm_scope_1.getNpmScopePrefix)(tree);
30
+ const scopeAlias = (0, npm_scope_1.toScopeAlias)(npmScopePrefix);
31
+ const fullyQualifiedName = `${npmScopePrefix}${schema.name}`;
24
32
  tree.delete((0, devkit_1.joinPathFragments)(libraryRoot, 'src'));
25
33
  (0, devkit_1.generateFiles)(tree, // the virtual file system
26
- (0, devkit_1.joinPathFragments)(__dirname, './files'), // path to the file templates
27
- libraryRoot, Object.assign({ synthDir: synthDirFromProject }, schema));
34
+ (0, devkit_1.joinPathFragments)(__dirname, './files/app'), // path to the file templates
35
+ libraryRoot, Object.assign(Object.assign({ synthDir: synthDirFromProject, scopeAlias: scopeAlias, fullyQualifiedName, pkgMgrCmd: (0, devkit_1.getPackageManagerCommand)().exec }, schema), { ruleSet: schema.ruleSet.toUpperCase() }), {
36
+ overwriteStrategy: devkit_1.OverwriteStrategy.Overwrite,
37
+ });
38
+ (0, devkit_1.generateFiles)(tree, // the virtual file system
39
+ (0, devkit_1.joinPathFragments)(__dirname, 'files', shared_constructs_1.SHARED_CONSTRUCTS_DIR, 'src', 'core'), (0, devkit_1.joinPathFragments)(shared_constructs_1.PACKAGES_DIR, shared_constructs_1.SHARED_CONSTRUCTS_DIR, 'src', 'core'), Object.assign({ synthDir: synthDirFromProject, scopeAlias: (0, npm_scope_1.toScopeAlias)((0, npm_scope_1.getNpmScopePrefix)(tree)) }, schema), {
40
+ overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting,
41
+ });
28
42
  (0, devkit_1.updateJson)(tree, `${libraryRoot}/project.json`, (config) => {
43
+ var _a;
29
44
  config.projectType = 'application';
30
- config.targets.build = {
45
+ config.targets.build.dependsOn = [
46
+ ...((_a = config.targets.build.dependsOn) !== null && _a !== void 0 ? _a : []),
47
+ 'synth',
48
+ ];
49
+ config.targets.compile.options.main = (0, devkit_1.joinPathFragments)(libraryRoot, 'src', 'main.ts');
50
+ config.targets.synth = {
31
51
  cache: true,
32
52
  executor: 'nx:run-commands',
33
53
  outputs: [`{workspaceRoot}${synthDirFromRoot}`],
@@ -46,16 +66,25 @@ function infraGenerator(tree, schema) {
46
66
  };
47
67
  return config;
48
68
  });
69
+ (0, ast_1.addStarExport)(tree, (0, devkit_1.joinPathFragments)(shared_constructs_1.PACKAGES_DIR, shared_constructs_1.SHARED_CONSTRUCTS_DIR, 'src', 'core', 'index.ts'), './cfn-guard.js');
49
70
  (0, devkit_1.addDependenciesToPackageJson)(tree, (0, versions_1.withVersions)([
50
- '@aws/pdk',
71
+ '@cdklabs/cdk-validator-cfnguard',
51
72
  'aws-cdk-lib',
52
73
  'aws-cdk',
53
74
  'esbuild',
54
75
  'constructs',
55
76
  'source-map-support',
56
77
  ]), (0, versions_1.withVersions)(['tsx']));
57
- yield (0, format_1.formatFilesInSubtree)(tree, libraryRoot);
58
- return tsLibGeneratorCallback;
78
+ (0, devkit_1.updateJson)(tree, `${libraryRoot}/tsconfig.json`, (tsConfig) => (Object.assign(Object.assign({}, tsConfig), { references: [
79
+ ...(tsConfig.references || []),
80
+ {
81
+ path: `${path_1.default.relative(libraryRoot, `${tree.root}/${shared_constructs_1.PACKAGES_DIR}`)}/${shared_constructs_1.SHARED_CONSTRUCTS_DIR}/tsconfig.json`,
82
+ },
83
+ ] })));
84
+ yield (0, format_1.formatFilesInSubtree)(tree);
85
+ return () => {
86
+ (0, devkit_1.installPackagesTask)(tree);
87
+ };
59
88
  });
60
89
  }
61
90
  exports.default = infraGenerator;
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/infra/app/generator.ts"],"names":[],"mappings":";;AAmBA,wCAuEC;;AA1FD;;;GAGG;AACH,uCASoB;AAEpB,4EAAyE;AACzE,mDAAoD;AACpD,+CAA0D;AAE1D,SAAsB,cAAc,CAClC,IAAU,EACV,MAA4B;;QAE5B,MAAM,GAAG,GAAG,IAAA,2BAAe,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,sBAAsB,GAAG,MAAM,IAAA,mBAAc,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAElE,MAAM,gBAAgB,GAAG,SAAS,GAAG,CAAC,GAAG,UAAU,CAAC;QACpD,MAAM,mBAAmB,GACvB,GAAG,CAAC,GAAG;aACJ,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;aACf,IAAI,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAAC;QAClC,MAAM,aAAa,GAAG,IAAA,iCAAwB,EAAC,IAAI,EAAE,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAC7E,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,IAAA,0BAAiB,EAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;QACnD,IAAA,sBAAa,EACX,IAAI,EAAE,0BAA0B;QAChC,IAAA,0BAAiB,EAAC,SAAS,EAAE,SAAS,CAAC,EAAE,6BAA6B;QACtE,WAAW,kBAET,QAAQ,EAAE,mBAAmB,IAC1B,MAAM,EAEZ,CAAC;QAEF,IAAA,mBAAU,EACR,IAAI,EACJ,GAAG,WAAW,eAAe,EAC7B,CAAC,MAA4B,EAAE,EAAE;YAC/B,MAAM,CAAC,WAAW,GAAG,aAAa,CAAC;YAEnC,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG;gBACrB,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE,iBAAiB;gBAC3B,OAAO,EAAE,CAAC,kBAAkB,gBAAgB,EAAE,CAAC;gBAC/C,SAAS,EAAE,CAAC,QAAQ,CAAC;gBACrB,OAAO,EAAE;oBACP,GAAG,EAAE,WAAW;oBAChB,OAAO,EAAE,WAAW;iBACrB;aACF,CAAC;YAEF,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG;gBACtB,QAAQ,EAAE,iBAAiB;gBAC3B,OAAO,EAAE;oBACP,GAAG,EAAE,WAAW;oBAChB,OAAO,EAAE,6CAA6C,mBAAmB,EAAE;iBAC5E;aACF,CAAC;YAEF,OAAO,MAAM,CAAC;QAChB,CAAC,CACF,CAAC;QAEF,IAAA,qCAA4B,EAC1B,IAAI,EACJ,IAAA,uBAAY,EAAC;YACX,UAAU;YACV,aAAa;YACb,SAAS;YACT,SAAS;YACT,YAAY;YACZ,oBAAoB;SACrB,CAAC,EACF,IAAA,uBAAY,EAAC,CAAC,KAAK,CAAC,CAAC,CACtB,CAAC;QAEF,MAAM,IAAA,6BAAoB,EAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAE9C,OAAO,sBAAsB,CAAC;IAChC,CAAC;CAAA;AAED,kBAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/infra/app/generator.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,16 @@
4
4
  */
5
5
  export interface InfraGeneratorSchema {
6
6
  name: string;
7
+ ruleSet:
8
+ | 'aws_prototyping'
9
+ | 'cfn_nag'
10
+ | 'hipaa'
11
+ | 'nist_csf'
12
+ | 'pci_dss_3_2_1'
13
+ | 'well_architected_reliability'
14
+ | 'well_architected_security';
7
15
  directory?: string;
8
- unitTestRunner?: 'jest' | 'vitest' | 'none';
16
+ // unitTestRunner?: 'jest' | 'vitest' | 'none';
17
+ // linter?: Linter;
9
18
  skipInstall?: boolean;
10
19
  }
@@ -14,6 +14,22 @@
14
14
  "x-prompt": "What name would you like to use for the application?",
15
15
  "pattern": "^[a-zA-Z][^:]*$"
16
16
  },
17
+ "ruleSet": {
18
+ "type": "string",
19
+ "enum": [
20
+ "aws_prototyping",
21
+ "cfn_nag",
22
+ "hipaa",
23
+ "nist_csf",
24
+ "pci_dss_3_2_1",
25
+ "well_architected_reliability",
26
+ "well_architected_security"
27
+ ],
28
+ "x-priority": "important",
29
+ "x-prompt": "What rule set do you want to validate your AWS resources with?",
30
+ "description": "Rule set to validate your AWS resources with.",
31
+ "default": "aws_prototyping"
32
+ },
17
33
  "directory": {
18
34
  "description": "The directory of the new application.",
19
35
  "type": "string",
@@ -21,14 +37,6 @@
21
37
  "x-priority": "important",
22
38
  "default": "packages",
23
39
  "x-prompt": "What directory would you like to store your application in?"
24
- },
25
- "unitTestRunner": {
26
- "type": "string",
27
- "enum": ["jest", "vitest", "none"],
28
- "x-priority": "important",
29
- "x-prompt": "What unit test runner would you like to use?",
30
- "description": "Test runner to use for unit tests.",
31
- "default": "vitest"
32
40
  }
33
41
  },
34
42
  "required": ["name"]
@@ -1,6 +1,7 @@
1
1
  # tRPC Backend Generator
2
2
 
3
3
  ## Overview
4
+
4
5
  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
 
6
7
  ## Usage
@@ -10,12 +11,14 @@ You can generate a new tRPC backend in two ways:
10
11
  ### 1. Using VSCode IDE
11
12
 
12
13
  First, install the NX Console extension for VSCode:
14
+
13
15
  1. Open VSCode
14
16
  2. Go to Extensions (Ctrl+Shift+X / Cmd+Shift+X)
15
17
  3. Search for "Nx Console"
16
18
  4. Install [Nx Console](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console)
17
19
 
18
20
  Then generate your API:
21
+
19
22
  1. Open the NX Console in VSCode
20
23
  2. Click on "Generate"
21
24
  3. Search for "trpc#backend"
@@ -25,33 +28,35 @@ Then generate your API:
25
28
  ### 2. Using CLI
26
29
 
27
30
  Generate the API:
31
+
28
32
  ```bash
29
- nx g @aws/nx-plugin:trpc#backend my-api --apiNamespace=@myorg --directory=apps/api
33
+ nx g @aws/nx-plugin:trpc#backend my-api --directory=apps/api
30
34
  ```
31
35
 
32
36
  You can also perform a dry-run to see what files would be generated without actually creating them:
37
+
33
38
  ```bash
34
- nx g @aws/nx-plugin:trpc#backend my-api --apiNamespace=@myorg --directory=apps/api --dry-run
39
+ nx g @aws/nx-plugin:trpc#backend my-api --directory=apps/api --dry-run
35
40
  ```
36
41
 
37
42
  Both methods will create a new tRPC backend API in the specified directory with all the necessary configuration and infrastructure code.
38
43
 
39
44
  ## Input Parameters
40
45
 
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 |
46
+ | Parameter | Type | Default | Description |
47
+ | -------------- | ------ | ---------- | ---------------------------------------------------------------------------- |
48
+ | apiName\* | string | - | The name of the API (required). Used to generate class names and file paths. |
49
+ | directory | string | "packages" | The directory to store the application in. |
50
+ | unitTestRunner | string | "vitest" | Test runner for unit tests. Options: jest, vitest, none |
47
51
 
48
- *Required parameter
52
+ \*Required parameter
49
53
 
50
54
  ## Expected Output
51
55
 
52
56
  The generator creates three main components:
53
57
 
54
58
  ### 1. Backend API Code
59
+
55
60
  ```
56
61
  <directory>/<api-name>/backend/
57
62
  ├── src/
@@ -65,6 +70,7 @@ The generator creates three main components:
65
70
  ```
66
71
 
67
72
  ### 2. Schema Code
73
+
68
74
  ```
69
75
  <directory>/<api-name>/schema/
70
76
  ├── src/
@@ -74,17 +80,29 @@ The generator creates three main components:
74
80
  ```
75
81
 
76
82
  ### 3. Infrastructure Code
83
+
77
84
  ```
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
85
+ common/
86
+ | └── constructs/
87
+ | | └── src/
88
+ | ├── app/ # Infrastructure specific to this API
89
+ | └── index.ts
90
+ | │ │ └── trpc-apis
91
+ | │ │ └── index.ts
92
+ | │ │ └── <name>.ts # Application specific cdk construct i.e: MyApi
93
+ | │ ├── core/ # Infrastructure specific to this API
94
+ | │ │ └── index.ts
95
+ | │ │ └── trpc-api.ts # Generic Trpc API construct
96
+ | │ └── index.ts # Exports for all constructs
97
+ | └── types/
98
+ | └── src/
99
+ | └── runtime-config.ts # Updates IRuntimeConfig to add trpcApis
83
100
  ├── tsconfig.json # TypeScript configuration
84
101
  └── project.json # Project configuration and build targets
85
102
  ```
86
103
 
87
104
  Additionally, it:
105
+
88
106
  1. Configures build settings for production deployment
89
107
  2. Installs required dependencies:
90
108
  - @trpc/server
@@ -107,13 +125,7 @@ The router is configured in `router.ts` with AWS Lambda integration:
107
125
  ```typescript
108
126
  import { initTRPC } from '@trpc/server';
109
127
  import { awsLambdaRequestHandler } from '@trpc/server/adapters/aws-lambda';
110
- import {
111
- createErrorPlugin,
112
- createLoggerPlugin,
113
- createMetricsPlugin,
114
- createTracerPlugin,
115
- IMiddlewareContext,
116
- } from './middleware.js';
128
+ import { createErrorPlugin, createLoggerPlugin, createMetricsPlugin, createTracerPlugin, IMiddlewareContext } from './middleware.js';
117
129
 
118
130
  // Initialize tRPC with context type
119
131
  export type Context = IMiddlewareContext;
@@ -121,11 +133,7 @@ const t = initTRPC.context<Context>().create();
121
133
 
122
134
  // Create base router and procedure which automatically instruments all middleware
123
135
  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);
136
+ export const publicProcedure = t.procedure.unstable_concat(createLoggerPlugin().loggerPlugin).unstable_concat(createTracerPlugin().tracerPlugin).unstable_concat(createMetricsPlugin().metricsPlugin).unstable_concat(createErrorPlugin().errorPlugin);
129
137
 
130
138
  // Define your procedures here
131
139
  const appRouter = router({
@@ -137,7 +145,7 @@ const appRouter = router({
137
145
 
138
146
  // Create Lambda handler
139
147
  export const handler = awsLambdaRequestHandler({
140
- router: appRouter
148
+ router: appRouter,
141
149
  });
142
150
 
143
151
  // Import this type in the frontend when setting up your integration
@@ -149,17 +157,20 @@ export type AppRouter = typeof appRouter;
149
157
  The generator includes four powerful middleware plugins whcih are automatically instrumented:
150
158
 
151
159
  1. **Logger Plugin**
160
+
152
161
  - Automatically logs procedure execution
153
162
  - Captures errors with detailed context
154
163
  - Uses structured logging format
155
164
 
156
165
  2. **Metrics Plugin**
166
+
157
167
  - Captures cold start metrics
158
168
  - Tracks request counts
159
169
  - Records success/error metrics
160
170
  - Automatically publishes metrics to CloudWatch
161
171
 
162
172
  3. **Tracer Plugin**
173
+
163
174
  - Integrates with AWS X-Ray
164
175
  - Creates subsegments for each procedure
165
176
  - Annotates cold starts
@@ -176,23 +187,21 @@ You can access the context in your procedures to utilize the observability tools
176
187
 
177
188
  ```typescript
178
189
  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
- }),
190
+ getData: publicProcedure.input(z.string()).query(async (opts) => {
191
+ // Access logger
192
+ opts.ctx.logger.info('Processing getData request', {
193
+ input: opts.input,
194
+ });
195
+
196
+ // Add custom metrics
197
+ opts.ctx.metrics.addMetric('getData.calls', MetricUnit.Count, 1);
198
+
199
+ // Use tracer for subsegments
200
+ return opts.ctx.tracer.captureMethod('getData.process', async () => {
201
+ // Your business logic here
202
+ return { data: 'result' };
203
+ });
204
+ }),
196
205
  });
197
206
  ```
198
207
 
@@ -201,7 +210,7 @@ const appRouter = router({
201
210
  ```mermaid
202
211
  graph TD
203
212
  subgraph AWS Cloud
204
- APIGW[API Gateway] --> Lambda[Lambda Functions]
213
+ APIGW[API Gateway] --> Lambda[Lambda Function]
205
214
  Lambda --> XRay[X-Ray Tracing]
206
215
  Lambda --> CW[CloudWatch Logs]
207
216
  Lambda --> Metrics[CloudWatch Metrics]
@@ -209,12 +218,15 @@ graph TD
209
218
  ```
210
219
 
211
220
  The infrastructure stack deploys:
221
+
212
222
  1. **API Gateway**
223
+
213
224
  - HTTP API endpoint
214
225
  - Request validation
215
226
  - CORS configuration
216
227
 
217
228
  2. **Lambda Functions**
229
+
218
230
  - Serverless compute
219
231
  - Auto-scaling
220
232
  - Pay-per-use pricing
@@ -225,7 +237,6 @@ The infrastructure stack deploys:
225
237
  - CloudWatch Metrics via Lambda Powertools
226
238
  - Structured logging with Lambda Powertools
227
239
 
228
-
229
240
  ## Using the Generated CDK Constructs
230
241
 
231
242
  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:
@@ -242,10 +253,7 @@ export class MyStack extends Stack {
242
253
  super(scope, id);
243
254
 
244
255
  // Create the API with no authentication
245
- const api = new MyApi(this, 'MyApi', {
246
- defaultAuthorizer: new HttpNoneAuthorizer(),
247
- allowedOrigins: ['http://localhost:4200']
248
- });
256
+ const api = new MyApi(this, 'MyApi');
249
257
  }
250
258
  }
251
259
  ```
@@ -255,7 +263,7 @@ export class MyStack extends Stack {
255
263
  ```typescript
256
264
  import * as cdk from 'aws-cdk-lib';
257
265
  import { Construct } from 'constructs';
258
- import { UserIdentity, MyApi } from ':my-org/common-constructs'
266
+ import { UserIdentity, MyApi } from ':my-org/common-constructs';
259
267
  import { HttpIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
260
268
 
261
269
  export class ApplicationStack extends cdk.Stack {
@@ -263,9 +271,7 @@ export class ApplicationStack extends cdk.Stack {
263
271
  super(scope, id, props);
264
272
 
265
273
  const identity = new UserIdentity(this, 'UserIdentity');
266
- const myapi = new MyApi(this, 'MyApi', {
267
- defaultAuthorizer: new HttpIamAuthorizer(),
268
- });
274
+ const myapi = new MyApi(this, 'MyApi');
269
275
  myapi.grantInvokeAccess(identity.identityPool.authenticatedRole);
270
276
  }
271
277
  }
@@ -282,15 +288,13 @@ export class MyStack extends Stack {
282
288
  constructor(scope: App, id: string) {
283
289
  super(scope, id);
284
290
 
285
- const api = new MyApi(this, 'MyApi', {
286
- // ... configuration
287
- });
291
+ const api = new MyApi(this, 'MyApi');
288
292
 
289
293
  // Grant access to other roles if needed
290
294
  const consumerRole = new Role(this, 'ConsumerRole', {
291
295
  // ... role configuration
292
296
  });
293
-
297
+
294
298
  api.grantInvokeAccess(consumerRole);
295
299
  }
296
300
  }
@@ -298,6 +302,29 @@ export class MyStack extends Stack {
298
302
 
299
303
  The API URL will be automatically registered in the RuntimeConfig system and can be accessed in your frontend application.
300
304
 
305
+ ### Updating CORS configuration
306
+
307
+ To update the CORS configuration, you can do this directly by modifying the generated APi construct located in `common/constructs/src/app/trpc-apis`.
308
+
309
+ For example:
310
+
311
+ ```typescript
312
+ import { Construct } from 'constructs';
313
+ import * as url from 'url';
314
+ import { TrpcApi } from '../../core/trpc-api.js';
315
+ import { HttpIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
316
+
317
+ export class MyApi extends TrpcApi {
318
+ constructor(scope: Construct, id: string) {
319
+ super(scope, id, {
320
+ defaultAuthorizer: new HttpIamAuthorizer(),
321
+ handlerFilePath: url.fileURLToPath(new URL('../../../../../../packages/my-api/backend/src/lambdas/router.ts', import.meta.url)),
322
+ allowedOrigins: ['http://localhost:4200/'], // Allow CORS from local vite dev server
323
+ });
324
+ }
325
+ }
326
+ ```
327
+
301
328
  ## Schema Code and Zod
302
329
 
303
330
  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.
@@ -305,6 +332,7 @@ The generator creates a separate schema package that uses [Zod](https://zod.dev)
305
332
  ### Introduction to Zod
306
333
 
307
334
  Zod is a schema declaration and validation library designed specifically for TypeScript. It allows you to:
335
+
308
336
  - Define schemas with a fluent API
309
337
  - Automatically infer TypeScript types from schemas
310
338
  - Validate data at runtime
@@ -368,17 +396,9 @@ export const CustomerSchema = z.object({
368
396
  ```typescript
369
397
  import { z } from 'zod';
370
398
 
371
- export const OrderStatusSchema = z.enum([
372
- 'pending',
373
- 'processing',
374
- 'shipped',
375
- 'delivered'
376
- ]);
399
+ export const OrderStatusSchema = z.enum(['pending', 'processing', 'shipped', 'delivered']);
377
400
 
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
- ]);
401
+ export const PaymentMethodSchema = z.union([z.object({ type: z.literal('credit_card'), cardNumber: z.string() }), z.object({ type: z.literal('paypal'), email: z.string().email() })]);
382
402
 
383
403
  export const OrderSchema = z.object({
384
404
  id: z.string().uuid(),
@@ -428,12 +448,7 @@ Your schemas can be used directly in your tRPC procedures for input validation a
428
448
 
429
449
  ```typescript
430
450
  import { router, publicProcedure } from './router';
431
- import {
432
- UserSchema,
433
- CreateUserSchema,
434
- UpdateUserSchema,
435
- SearchParamsSchema
436
- } from ':my-org/schema';
451
+ import { UserSchema, CreateUserSchema, UpdateUserSchema, SearchParamsSchema } from ':my-org/schema';
437
452
 
438
453
  export const userRouter = router({
439
454
  // Create user with input validation
@@ -457,10 +472,12 @@ export const userRouter = router({
457
472
 
458
473
  // Update user with partial data
459
474
  update: publicProcedure
460
- .input(z.object({
461
- id: z.string().uuid(),
462
- data: UpdateUserSchema,
463
- }))
475
+ .input(
476
+ z.object({
477
+ id: z.string().uuid(),
478
+ data: UpdateUserSchema,
479
+ })
480
+ )
464
481
  .output(UserSchema)
465
482
  .mutation(async (opts) => {
466
483
  const { id, data } = opts.input;
@@ -474,6 +491,7 @@ export const userRouter = router({
474
491
  1. **Keep Schemas Centralized**: Store all schemas in the schema package to ensure they're easily shared between frontend and backend.
475
492
 
476
493
  2. **Use Type Inference**: Let TypeScript infer types from your schemas instead of maintaining separate type definitions:
494
+
477
495
  ```typescript
478
496
  // Do this:
479
497
  export const UserSchema = z.object({ ... });
@@ -485,6 +503,7 @@ export const userRouter = router({
485
503
  ```
486
504
 
487
505
  3. **Compose Schemas**: Build complex schemas by composing simpler ones:
506
+
488
507
  ```typescript
489
508
  const BaseUserSchema = z.object({
490
509
  email: z.string().email(),
@@ -497,6 +516,7 @@ export const userRouter = router({
497
516
  ```
498
517
 
499
518
  4. **Version Your Schemas**: When making breaking changes, consider versioning your schemas:
519
+
500
520
  ```typescript
501
521
  export const UserSchemaV1 = z.object({ ... });
502
522
  export const UserSchemaV2 = UserSchemaV1.extend({ ... });
@@ -505,7 +525,7 @@ export const userRouter = router({
505
525
  5. **Document Your Schemas**: Add JSDoc comments to explain complex validation rules:
506
526
  ```typescript
507
527
  export const ConfigSchema = z.object({
508
- /**
528
+ /**
509
529
  * API key must be in format: prefix_<32 chars>
510
530
  * Example: myapp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
511
531
  */
@@ -516,15 +536,18 @@ export const userRouter = router({
516
536
  ## Building the Application
517
537
 
518
538
  To create a production build:
539
+
519
540
  ```bash
520
541
  nx build @my-org/my-api
521
542
  ```
522
543
 
523
544
  All built code is located in the `dist` folder at the root of your workspace. For example:
545
+
524
546
  - Backend code: `dist/apps/api/my-api/backend`
525
547
  - Schema code: `dist/apps/api/my-api/schema`
526
548
 
527
549
  The production build:
550
+
528
551
  - Bundles Lambda functions for optimal cold start performance
529
552
  - Generates TypeScript declaration files
530
553
  - Creates source maps for debugging
@@ -546,4 +569,3 @@ new NodejsFunction(this, 'MyApiHandler', {
546
569
  ```
547
570
 
548
571
  This will ensure that whichever `@aws-sdk` version you have installed locally will be the one that is used in the Lambda.
549
-