@ttoss/appsync-api 0.17.12 → 0.18.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.
package/README.md CHANGED
@@ -98,3 +98,38 @@ export const handler = createAppSyncResolverHandler({
98
98
  middlewares: [permissions],
99
99
  });
100
100
  ```
101
+
102
+ ### Custom domain name
103
+
104
+ You can add a custom domain name to your API using the `customDomain` option.
105
+
106
+ ```ts
107
+ import { createApiTemplate } from '@ttoss/appsync-api';
108
+
109
+ export const handler = createApiTemplate({
110
+ schemaComposer,
111
+ customDomain: {
112
+ domainName: 'api.example.com', // required
113
+ certificateArn: {
114
+ 'Fn::ImportValue': 'AppSyncDomainCertificateArn',
115
+ }, // required
116
+ },
117
+ });
118
+ ```
119
+
120
+ If your domain is on Route53, you can use the option `customDomain.hostedZoneName` to create the required DNS records.
121
+
122
+ ```ts
123
+ import { createApiTemplate } from '@ttoss/appsync-api';
124
+
125
+ export const template = createApiTemplate({
126
+ schemaComposer,
127
+ customDomain: {
128
+ domainName: 'api.example.com', // required
129
+ certificateArn: {
130
+ 'Fn::ImportValue': 'AppSyncDomainCertificateArn',
131
+ }, // required
132
+ hostedZoneName: 'example.com.', // optional
133
+ },
134
+ });
135
+ ```
package/dist/esm/index.js CHANGED
@@ -2,128 +2,6 @@
2
2
 
3
3
  // src/createApiTemplate.ts
4
4
  import { graphql } from "@ttoss/graphql-api";
5
-
6
- // ../../node_modules/.pnpm/tslib@2.6.2/node_modules/tslib/tslib.es6.mjs
7
- var __assign = function () {
8
- __assign = Object.assign || function __assign2(t) {
9
- for (var s, i = 1, n = arguments.length; i < n; i++) {
10
- s = arguments[i];
11
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
12
- }
13
- return t;
14
- };
15
- return __assign.apply(this, arguments);
16
- };
17
-
18
- // ../../node_modules/.pnpm/lower-case@2.0.2/node_modules/lower-case/dist.es2015/index.js
19
- function lowerCase(str) {
20
- return str.toLowerCase();
21
- }
22
-
23
- // ../../node_modules/.pnpm/no-case@3.0.4/node_modules/no-case/dist.es2015/index.js
24
- var DEFAULT_SPLIT_REGEXP = [/([a-z0-9])([A-Z])/g, /([A-Z])([A-Z][a-z])/g];
25
- var DEFAULT_STRIP_REGEXP = /[^A-Z0-9]+/gi;
26
- function noCase(input, options) {
27
- if (options === void 0) {
28
- options = {};
29
- }
30
- var _a = options.splitRegexp,
31
- splitRegexp = _a === void 0 ? DEFAULT_SPLIT_REGEXP : _a,
32
- _b = options.stripRegexp,
33
- stripRegexp = _b === void 0 ? DEFAULT_STRIP_REGEXP : _b,
34
- _c = options.transform,
35
- transform = _c === void 0 ? lowerCase : _c,
36
- _d = options.delimiter,
37
- delimiter = _d === void 0 ? " " : _d;
38
- var result = replace(replace(input, splitRegexp, "$1\0$2"), stripRegexp, "\0");
39
- var start = 0;
40
- var end = result.length;
41
- while (result.charAt(start) === "\0") start++;
42
- while (result.charAt(end - 1) === "\0") end--;
43
- return result.slice(start, end).split("\0").map(transform).join(delimiter);
44
- }
45
- function replace(input, re, value) {
46
- if (re instanceof RegExp) return input.replace(re, value);
47
- return re.reduce(function (input2, re2) {
48
- return input2.replace(re2, value);
49
- }, input);
50
- }
51
-
52
- // ../../node_modules/.pnpm/pascal-case@3.1.2/node_modules/pascal-case/dist.es2015/index.js
53
- function pascalCaseTransform(input, index) {
54
- var firstChar = input.charAt(0);
55
- var lowerChars = input.substr(1).toLowerCase();
56
- if (index > 0 && firstChar >= "0" && firstChar <= "9") {
57
- return "_" + firstChar + lowerChars;
58
- }
59
- return "" + firstChar.toUpperCase() + lowerChars;
60
- }
61
- function pascalCase(input, options) {
62
- if (options === void 0) {
63
- options = {};
64
- }
65
- return noCase(input, __assign({
66
- delimiter: "",
67
- transform: pascalCaseTransform
68
- }, options));
69
- }
70
-
71
- // ../carlin/src/deploy/lambdaLayer/getPackageLambdaLayerStackName.ts
72
- var lambdaLayerStackNamePrefix = `LambdaLayer`;
73
- var getPackageLambdaLayerStackName = packageName => {
74
- const [scopedName, version] = packageName.split("@").filter(part => {
75
- return !!part;
76
- });
77
- return [lambdaLayerStackNamePrefix, pascalCase(scopedName), version.replace(/[^0-9.]/g, "").replace(/\./g, "-")].join("-");
78
- };
79
-
80
- // package.json
81
- var package_default = {
82
- name: "@ttoss/appsync-api",
83
- version: "0.17.12",
84
- description: "A library for building GraphQL APIs for AWS AppSync.",
85
- author: "ttoss",
86
- contributors: ["Pedro Arantes <pedro@arantespp.com> (https://arantespp.com)"],
87
- repository: {
88
- type: "git",
89
- url: "https://github.com/ttoss/ttoss.git",
90
- directory: "packages/appsync-api"
91
- },
92
- main: "dist/index.js",
93
- module: "dist/esm/index.js",
94
- files: ["dist", "src"],
95
- scripts: {
96
- build: "tsup",
97
- test: "jest"
98
- },
99
- sideEffects: false,
100
- typings: "dist/index.d.ts",
101
- dependencies: {
102
- "@ttoss/cloudformation": "workspace:^"
103
- },
104
- peerDependencies: {
105
- "@ttoss/graphql-api": "workspace:^",
106
- graphql: "^16.6.0"
107
- },
108
- devDependencies: {
109
- "@ttoss/config": "workspace:^",
110
- "@ttoss/graphql-api": "workspace:^",
111
- "@ttoss/relay-amplify": "workspace:^",
112
- "@types/aws-lambda": "^8.10.130",
113
- carlin: "workspace:^",
114
- graphql: "^16.8.1",
115
- "graphql-shield": "^7.6.5",
116
- jest: "^29.7.0",
117
- tsup: "^8.0.1"
118
- },
119
- keywords: ["api", "appsync", "aws", "graphql"],
120
- publishConfig: {
121
- access: "public",
122
- provenance: true
123
- }
124
- };
125
-
126
- // src/createApiTemplate.ts
127
5
  var AppSyncGraphQLApiLogicalId = "AppSyncGraphQLApi";
128
6
  var AppSyncGraphQLSchemaLogicalId = "AppSyncGraphQLSchema";
129
7
  var AppSyncLambdaFunctionLogicalId = "AppSyncLambdaFunction";
@@ -135,7 +13,8 @@ var createApiTemplate = ({
135
13
  schemaComposer,
136
14
  dataSource,
137
15
  lambdaFunction,
138
- userPoolConfig
16
+ userPoolConfig,
17
+ customDomain
139
18
  }) => {
140
19
  const sdlWithoutComments = schemaComposer.toSDL({
141
20
  commentDescriptions: false,
@@ -158,21 +37,6 @@ var createApiTemplate = ({
158
37
  };
159
38
  });
160
39
  }).filter(Boolean);
161
- const getPeerDependenciesLambdaLayers = () => {
162
- const {
163
- peerDependencies
164
- } = package_default;
165
- const lambdaLayerStackNames = Object.entries(peerDependencies).filter(([dependencyName]) => {
166
- return ["graphql"].includes(dependencyName);
167
- }).map(([dependencyName, dependencyVersion]) => {
168
- return getPackageLambdaLayerStackName([dependencyName, dependencyVersion].join("@"));
169
- });
170
- return lambdaLayerStackNames.map(lambdaLayerStackName => {
171
- return {
172
- "Fn::ImportValue": lambdaLayerStackName
173
- };
174
- });
175
- };
176
40
  const template = {
177
41
  AWSTemplateFormatVersion: "2010-09-09",
178
42
  Parameters: {
@@ -228,7 +92,7 @@ var createApiTemplate = ({
228
92
  }
229
93
  },
230
94
  Handler: "index.handler",
231
- Layers: getPeerDependenciesLambdaLayers(),
95
+ Layers: lambdaFunction.layers,
232
96
  MemorySize: 512,
233
97
  Role: lambdaFunction.roleArn,
234
98
  Runtime: "nodejs20.x",
@@ -345,6 +209,58 @@ var createApiTemplate = ({
345
209
  Variables: lambdaFunction.environment.variables
346
210
  };
347
211
  }
212
+ if (customDomain) {
213
+ const AppSyncDomainNameLogicalId = "AppSyncDomainName";
214
+ template.Resources[AppSyncDomainNameLogicalId] = {
215
+ Type: "AWS::AppSync::DomainName",
216
+ Properties: {
217
+ CertificateArn: customDomain.certificateArn,
218
+ Description: "Custom domain for AppSync API",
219
+ DomainName: customDomain.domainName
220
+ }
221
+ };
222
+ if (customDomain.hostedZoneName) {
223
+ const hostedZoneName = customDomain.hostedZoneName.endsWith(".") ? customDomain.hostedZoneName : `${customDomain.hostedZoneName}.`;
224
+ template.Resources.AppSyncDomainNameRoute53RecordSet = {
225
+ Type: "AWS::Route53::RecordSet",
226
+ Properties: {
227
+ HostedZoneName: hostedZoneName,
228
+ Name: customDomain.domainName,
229
+ ResourceRecords: [{
230
+ "Fn::GetAtt": [AppSyncDomainNameLogicalId, "AppSyncDomainName"]
231
+ }],
232
+ TTL: "900",
233
+ Type: "CNAME"
234
+ }
235
+ };
236
+ }
237
+ template.Resources.AppSyncDomainNameApiAssociation = {
238
+ Type: "AWS::AppSync::DomainNameApiAssociation",
239
+ Properties: {
240
+ ApiId: {
241
+ "Fn::GetAtt": [AppSyncGraphQLApiLogicalId, "ApiId"]
242
+ },
243
+ DomainName: {
244
+ "Fn::GetAtt": [AppSyncDomainNameLogicalId, "DomainName"]
245
+ }
246
+ }
247
+ };
248
+ if (!template.Outputs) {
249
+ template.Outputs = {};
250
+ }
251
+ template.Outputs.DomainName = {
252
+ Description: "Custom domain name for AppSync API",
253
+ Value: {
254
+ "Fn::GetAtt": [AppSyncDomainNameLogicalId, "DomainName"]
255
+ }
256
+ };
257
+ template.Outputs.CloudFrontDomainName = {
258
+ Description: "CloudFront domain name for AppSync API",
259
+ Value: {
260
+ "Fn::GetAtt": [AppSyncDomainNameLogicalId, "AppSyncDomainName"]
261
+ }
262
+ };
263
+ }
348
264
  return template;
349
265
  };
350
266
 
package/dist/index.d.mts CHANGED
@@ -87,10 +87,14 @@ type StringOrImport = string | {
87
87
  * https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html
88
88
  */
89
89
  type AuthenticationType = 'API_KEY' | 'AWS_LAMBDA' | 'AWS_IAM' | 'OPENID_CONNECT' | 'AMAZON_COGNITO_USER_POOLS';
90
- declare const createApiTemplate: ({ additionalAuthenticationProviders, authenticationType, schemaComposer, dataSource, lambdaFunction, userPoolConfig, }: {
90
+ declare const createApiTemplate: ({ additionalAuthenticationProviders, authenticationType, schemaComposer, dataSource, lambdaFunction, userPoolConfig, customDomain, }: {
91
91
  additionalAuthenticationProviders?: AuthenticationType[] | undefined;
92
92
  authenticationType?: AuthenticationType | undefined;
93
- schemaComposer: SchemaComposer<any>;
93
+ customDomain?: {
94
+ domainName: string;
95
+ certificateArn: string;
96
+ hostedZoneName?: string | undefined;
97
+ } | undefined;
94
98
  dataSource: {
95
99
  roleArn: StringOrImport;
96
100
  };
@@ -98,8 +102,10 @@ declare const createApiTemplate: ({ additionalAuthenticationProviders, authentic
98
102
  environment?: {
99
103
  variables: Record<string, string>;
100
104
  };
105
+ layers?: any;
101
106
  roleArn: StringOrImport;
102
107
  };
108
+ schemaComposer: SchemaComposer<any>;
103
109
  userPoolConfig?: {
104
110
  appIdClientRegex: StringOrImport;
105
111
  awsRegion: StringOrImport;
package/dist/index.d.ts CHANGED
@@ -87,10 +87,14 @@ type StringOrImport = string | {
87
87
  * https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html
88
88
  */
89
89
  type AuthenticationType = 'API_KEY' | 'AWS_LAMBDA' | 'AWS_IAM' | 'OPENID_CONNECT' | 'AMAZON_COGNITO_USER_POOLS';
90
- declare const createApiTemplate: ({ additionalAuthenticationProviders, authenticationType, schemaComposer, dataSource, lambdaFunction, userPoolConfig, }: {
90
+ declare const createApiTemplate: ({ additionalAuthenticationProviders, authenticationType, schemaComposer, dataSource, lambdaFunction, userPoolConfig, customDomain, }: {
91
91
  additionalAuthenticationProviders?: AuthenticationType[] | undefined;
92
92
  authenticationType?: AuthenticationType | undefined;
93
- schemaComposer: SchemaComposer<any>;
93
+ customDomain?: {
94
+ domainName: string;
95
+ certificateArn: string;
96
+ hostedZoneName?: string | undefined;
97
+ } | undefined;
94
98
  dataSource: {
95
99
  roleArn: StringOrImport;
96
100
  };
@@ -98,8 +102,10 @@ declare const createApiTemplate: ({ additionalAuthenticationProviders, authentic
98
102
  environment?: {
99
103
  variables: Record<string, string>;
100
104
  };
105
+ layers?: any;
101
106
  roleArn: StringOrImport;
102
107
  };
108
+ schemaComposer: SchemaComposer<any>;
103
109
  userPoolConfig?: {
104
110
  appIdClientRegex: StringOrImport;
105
111
  awsRegion: StringOrImport;
package/dist/index.js CHANGED
@@ -34,128 +34,6 @@ module.exports = __toCommonJS(src_exports);
34
34
 
35
35
  // src/createApiTemplate.ts
36
36
  var import_graphql_api = require("@ttoss/graphql-api");
37
-
38
- // ../../node_modules/.pnpm/tslib@2.6.2/node_modules/tslib/tslib.es6.mjs
39
- var __assign = function () {
40
- __assign = Object.assign || function __assign2(t) {
41
- for (var s, i = 1, n = arguments.length; i < n; i++) {
42
- s = arguments[i];
43
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
44
- }
45
- return t;
46
- };
47
- return __assign.apply(this, arguments);
48
- };
49
-
50
- // ../../node_modules/.pnpm/lower-case@2.0.2/node_modules/lower-case/dist.es2015/index.js
51
- function lowerCase(str) {
52
- return str.toLowerCase();
53
- }
54
-
55
- // ../../node_modules/.pnpm/no-case@3.0.4/node_modules/no-case/dist.es2015/index.js
56
- var DEFAULT_SPLIT_REGEXP = [/([a-z0-9])([A-Z])/g, /([A-Z])([A-Z][a-z])/g];
57
- var DEFAULT_STRIP_REGEXP = /[^A-Z0-9]+/gi;
58
- function noCase(input, options) {
59
- if (options === void 0) {
60
- options = {};
61
- }
62
- var _a = options.splitRegexp,
63
- splitRegexp = _a === void 0 ? DEFAULT_SPLIT_REGEXP : _a,
64
- _b = options.stripRegexp,
65
- stripRegexp = _b === void 0 ? DEFAULT_STRIP_REGEXP : _b,
66
- _c = options.transform,
67
- transform = _c === void 0 ? lowerCase : _c,
68
- _d = options.delimiter,
69
- delimiter = _d === void 0 ? " " : _d;
70
- var result = replace(replace(input, splitRegexp, "$1\0$2"), stripRegexp, "\0");
71
- var start = 0;
72
- var end = result.length;
73
- while (result.charAt(start) === "\0") start++;
74
- while (result.charAt(end - 1) === "\0") end--;
75
- return result.slice(start, end).split("\0").map(transform).join(delimiter);
76
- }
77
- function replace(input, re, value) {
78
- if (re instanceof RegExp) return input.replace(re, value);
79
- return re.reduce(function (input2, re2) {
80
- return input2.replace(re2, value);
81
- }, input);
82
- }
83
-
84
- // ../../node_modules/.pnpm/pascal-case@3.1.2/node_modules/pascal-case/dist.es2015/index.js
85
- function pascalCaseTransform(input, index) {
86
- var firstChar = input.charAt(0);
87
- var lowerChars = input.substr(1).toLowerCase();
88
- if (index > 0 && firstChar >= "0" && firstChar <= "9") {
89
- return "_" + firstChar + lowerChars;
90
- }
91
- return "" + firstChar.toUpperCase() + lowerChars;
92
- }
93
- function pascalCase(input, options) {
94
- if (options === void 0) {
95
- options = {};
96
- }
97
- return noCase(input, __assign({
98
- delimiter: "",
99
- transform: pascalCaseTransform
100
- }, options));
101
- }
102
-
103
- // ../carlin/src/deploy/lambdaLayer/getPackageLambdaLayerStackName.ts
104
- var lambdaLayerStackNamePrefix = `LambdaLayer`;
105
- var getPackageLambdaLayerStackName = packageName => {
106
- const [scopedName, version] = packageName.split("@").filter(part => {
107
- return !!part;
108
- });
109
- return [lambdaLayerStackNamePrefix, pascalCase(scopedName), version.replace(/[^0-9.]/g, "").replace(/\./g, "-")].join("-");
110
- };
111
-
112
- // package.json
113
- var package_default = {
114
- name: "@ttoss/appsync-api",
115
- version: "0.17.12",
116
- description: "A library for building GraphQL APIs for AWS AppSync.",
117
- author: "ttoss",
118
- contributors: ["Pedro Arantes <pedro@arantespp.com> (https://arantespp.com)"],
119
- repository: {
120
- type: "git",
121
- url: "https://github.com/ttoss/ttoss.git",
122
- directory: "packages/appsync-api"
123
- },
124
- main: "dist/index.js",
125
- module: "dist/esm/index.js",
126
- files: ["dist", "src"],
127
- scripts: {
128
- build: "tsup",
129
- test: "jest"
130
- },
131
- sideEffects: false,
132
- typings: "dist/index.d.ts",
133
- dependencies: {
134
- "@ttoss/cloudformation": "workspace:^"
135
- },
136
- peerDependencies: {
137
- "@ttoss/graphql-api": "workspace:^",
138
- graphql: "^16.6.0"
139
- },
140
- devDependencies: {
141
- "@ttoss/config": "workspace:^",
142
- "@ttoss/graphql-api": "workspace:^",
143
- "@ttoss/relay-amplify": "workspace:^",
144
- "@types/aws-lambda": "^8.10.130",
145
- carlin: "workspace:^",
146
- graphql: "^16.8.1",
147
- "graphql-shield": "^7.6.5",
148
- jest: "^29.7.0",
149
- tsup: "^8.0.1"
150
- },
151
- keywords: ["api", "appsync", "aws", "graphql"],
152
- publishConfig: {
153
- access: "public",
154
- provenance: true
155
- }
156
- };
157
-
158
- // src/createApiTemplate.ts
159
37
  var AppSyncGraphQLApiLogicalId = "AppSyncGraphQLApi";
160
38
  var AppSyncGraphQLSchemaLogicalId = "AppSyncGraphQLSchema";
161
39
  var AppSyncLambdaFunctionLogicalId = "AppSyncLambdaFunction";
@@ -167,7 +45,8 @@ var createApiTemplate = ({
167
45
  schemaComposer,
168
46
  dataSource,
169
47
  lambdaFunction,
170
- userPoolConfig
48
+ userPoolConfig,
49
+ customDomain
171
50
  }) => {
172
51
  const sdlWithoutComments = schemaComposer.toSDL({
173
52
  commentDescriptions: false,
@@ -190,21 +69,6 @@ var createApiTemplate = ({
190
69
  };
191
70
  });
192
71
  }).filter(Boolean);
193
- const getPeerDependenciesLambdaLayers = () => {
194
- const {
195
- peerDependencies
196
- } = package_default;
197
- const lambdaLayerStackNames = Object.entries(peerDependencies).filter(([dependencyName]) => {
198
- return ["graphql"].includes(dependencyName);
199
- }).map(([dependencyName, dependencyVersion]) => {
200
- return getPackageLambdaLayerStackName([dependencyName, dependencyVersion].join("@"));
201
- });
202
- return lambdaLayerStackNames.map(lambdaLayerStackName => {
203
- return {
204
- "Fn::ImportValue": lambdaLayerStackName
205
- };
206
- });
207
- };
208
72
  const template = {
209
73
  AWSTemplateFormatVersion: "2010-09-09",
210
74
  Parameters: {
@@ -260,7 +124,7 @@ var createApiTemplate = ({
260
124
  }
261
125
  },
262
126
  Handler: "index.handler",
263
- Layers: getPeerDependenciesLambdaLayers(),
127
+ Layers: lambdaFunction.layers,
264
128
  MemorySize: 512,
265
129
  Role: lambdaFunction.roleArn,
266
130
  Runtime: "nodejs20.x",
@@ -377,6 +241,58 @@ var createApiTemplate = ({
377
241
  Variables: lambdaFunction.environment.variables
378
242
  };
379
243
  }
244
+ if (customDomain) {
245
+ const AppSyncDomainNameLogicalId = "AppSyncDomainName";
246
+ template.Resources[AppSyncDomainNameLogicalId] = {
247
+ Type: "AWS::AppSync::DomainName",
248
+ Properties: {
249
+ CertificateArn: customDomain.certificateArn,
250
+ Description: "Custom domain for AppSync API",
251
+ DomainName: customDomain.domainName
252
+ }
253
+ };
254
+ if (customDomain.hostedZoneName) {
255
+ const hostedZoneName = customDomain.hostedZoneName.endsWith(".") ? customDomain.hostedZoneName : `${customDomain.hostedZoneName}.`;
256
+ template.Resources.AppSyncDomainNameRoute53RecordSet = {
257
+ Type: "AWS::Route53::RecordSet",
258
+ Properties: {
259
+ HostedZoneName: hostedZoneName,
260
+ Name: customDomain.domainName,
261
+ ResourceRecords: [{
262
+ "Fn::GetAtt": [AppSyncDomainNameLogicalId, "AppSyncDomainName"]
263
+ }],
264
+ TTL: "900",
265
+ Type: "CNAME"
266
+ }
267
+ };
268
+ }
269
+ template.Resources.AppSyncDomainNameApiAssociation = {
270
+ Type: "AWS::AppSync::DomainNameApiAssociation",
271
+ Properties: {
272
+ ApiId: {
273
+ "Fn::GetAtt": [AppSyncGraphQLApiLogicalId, "ApiId"]
274
+ },
275
+ DomainName: {
276
+ "Fn::GetAtt": [AppSyncDomainNameLogicalId, "DomainName"]
277
+ }
278
+ }
279
+ };
280
+ if (!template.Outputs) {
281
+ template.Outputs = {};
282
+ }
283
+ template.Outputs.DomainName = {
284
+ Description: "Custom domain name for AppSync API",
285
+ Value: {
286
+ "Fn::GetAtt": [AppSyncDomainNameLogicalId, "DomainName"]
287
+ }
288
+ };
289
+ template.Outputs.CloudFrontDomainName = {
290
+ Description: "CloudFront domain name for AppSync API",
291
+ Value: {
292
+ "Fn::GetAtt": [AppSyncDomainNameLogicalId, "AppSyncDomainName"]
293
+ }
294
+ };
295
+ }
380
296
  return template;
381
297
  };
382
298
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/appsync-api",
3
- "version": "0.17.12",
3
+ "version": "0.18.0",
4
4
  "description": "A library for building GraphQL APIs for AWS AppSync.",
5
5
  "author": "ttoss",
6
6
  "contributors": [
@@ -1,6 +1,4 @@
1
1
  import { type SchemaComposer, graphql } from '@ttoss/graphql-api';
2
- import { getPackageLambdaLayerStackName } from 'carlin/src/deploy/lambdaLayer/getPackageLambdaLayerStackName';
3
- import packageJson from '../package.json';
4
2
 
5
3
  /**
6
4
  * Absolute path to avoid:
@@ -44,11 +42,15 @@ export const createApiTemplate = ({
44
42
  dataSource,
45
43
  lambdaFunction,
46
44
  userPoolConfig,
45
+ customDomain,
47
46
  }: {
48
47
  additionalAuthenticationProviders?: AuthenticationType[];
49
48
  authenticationType?: AuthenticationType;
50
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
- schemaComposer: SchemaComposer<any>;
49
+ customDomain?: {
50
+ domainName: string;
51
+ certificateArn: string;
52
+ hostedZoneName?: string;
53
+ };
52
54
  dataSource: {
53
55
  roleArn: StringOrImport;
54
56
  };
@@ -56,8 +58,12 @@ export const createApiTemplate = ({
56
58
  environment?: {
57
59
  variables: Record<string, string>;
58
60
  };
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ layers?: any;
59
63
  roleArn: StringOrImport;
60
64
  };
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ schemaComposer: SchemaComposer<any>;
61
67
  userPoolConfig?: {
62
68
  appIdClientRegex: StringOrImport;
63
69
  awsRegion: StringOrImport;
@@ -102,26 +108,6 @@ export const createApiTemplate = ({
102
108
  })
103
109
  .filter(Boolean) as Array<{ fieldName: string; typeName: string }>;
104
110
 
105
- const getPeerDependenciesLambdaLayers = () => {
106
- const { peerDependencies } = packageJson;
107
-
108
- const lambdaLayerStackNames = Object.entries(peerDependencies)
109
- .filter(([dependencyName]) => {
110
- return ['graphql'].includes(dependencyName);
111
- })
112
- .map(([dependencyName, dependencyVersion]) => {
113
- return getPackageLambdaLayerStackName(
114
- [dependencyName, dependencyVersion].join('@')
115
- );
116
- });
117
-
118
- return lambdaLayerStackNames.map((lambdaLayerStackName) => {
119
- return {
120
- 'Fn::ImportValue': lambdaLayerStackName,
121
- };
122
- });
123
- };
124
-
125
111
  const template: CloudFormationTemplate = {
126
112
  AWSTemplateFormatVersion: '2010-09-09',
127
113
  Parameters: {
@@ -169,7 +155,7 @@ export const createApiTemplate = ({
169
155
  S3ObjectVersion: { Ref: 'LambdaS3ObjectVersion' },
170
156
  },
171
157
  Handler: 'index.handler',
172
- Layers: getPeerDependenciesLambdaLayers(),
158
+ Layers: lambdaFunction.layers,
173
159
  MemorySize: 512,
174
160
  Role: lambdaFunction.roleArn,
175
161
  Runtime: 'nodejs20.x',
@@ -300,5 +286,75 @@ export const createApiTemplate = ({
300
286
  };
301
287
  }
302
288
 
289
+ if (customDomain) {
290
+ const AppSyncDomainNameLogicalId = 'AppSyncDomainName';
291
+
292
+ /**
293
+ * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appsync-domainname.html
294
+ */
295
+ template.Resources[AppSyncDomainNameLogicalId] = {
296
+ Type: 'AWS::AppSync::DomainName',
297
+ Properties: {
298
+ CertificateArn: customDomain.certificateArn,
299
+ Description: 'Custom domain for AppSync API',
300
+ DomainName: customDomain.domainName,
301
+ },
302
+ };
303
+
304
+ if (customDomain.hostedZoneName) {
305
+ const hostedZoneName = customDomain.hostedZoneName.endsWith('.')
306
+ ? customDomain.hostedZoneName
307
+ : `${customDomain.hostedZoneName}.`;
308
+
309
+ /**
310
+ * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-route53-recordset.html
311
+ */
312
+ template.Resources.AppSyncDomainNameRoute53RecordSet = {
313
+ Type: 'AWS::Route53::RecordSet',
314
+ Properties: {
315
+ HostedZoneName: hostedZoneName,
316
+ Name: customDomain.domainName,
317
+ ResourceRecords: [
318
+ {
319
+ 'Fn::GetAtt': [AppSyncDomainNameLogicalId, 'AppSyncDomainName'],
320
+ },
321
+ ],
322
+ TTL: '900',
323
+ Type: 'CNAME',
324
+ },
325
+ };
326
+ }
327
+
328
+ template.Resources.AppSyncDomainNameApiAssociation = {
329
+ Type: 'AWS::AppSync::DomainNameApiAssociation',
330
+ Properties: {
331
+ ApiId: {
332
+ 'Fn::GetAtt': [AppSyncGraphQLApiLogicalId, 'ApiId'],
333
+ },
334
+ DomainName: {
335
+ 'Fn::GetAtt': [AppSyncDomainNameLogicalId, 'DomainName'],
336
+ },
337
+ },
338
+ };
339
+
340
+ if (!template.Outputs) {
341
+ template.Outputs = {};
342
+ }
343
+
344
+ template.Outputs.DomainName = {
345
+ Description: 'Custom domain name for AppSync API',
346
+ Value: {
347
+ 'Fn::GetAtt': [AppSyncDomainNameLogicalId, 'DomainName'],
348
+ },
349
+ };
350
+
351
+ template.Outputs.CloudFrontDomainName = {
352
+ Description: 'CloudFront domain name for AppSync API',
353
+ Value: {
354
+ 'Fn::GetAtt': [AppSyncDomainNameLogicalId, 'AppSyncDomainName'],
355
+ },
356
+ };
357
+ }
358
+
303
359
  return template;
304
360
  };