@ttoss/appsync-api 0.22.15 → 0.23.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.
package/README.md CHANGED
@@ -14,22 +14,25 @@ You can create and deploy an AppSync API in four steps:
14
14
 
15
15
  1. Create a `schemaComposer` object using [`graphql-compose`](https://graphql-compose.github.io/docs/intro/quick-start.html), that the next steps will use to create the API.
16
16
 
17
- 2. Create a `cloudformation.ts` file that exports a CloudFormation template using `createApiTemplate`:
17
+ 2. Create a `cloudformation.ts` file that exports a CloudFormation template using `createApiTemplate`. Use `importValueFromParameter` from `@ttoss/cloudformation` to import cross-stack values whose export names come from template parameters:
18
18
 
19
19
  ```typescript
20
+ import { importValueFromParameter } from '@ttoss/cloudformation';
20
21
  import { createApiTemplate } from '@ttoss/appsync-api';
21
22
  import { schemaComposer } from './schemaComposer';
22
23
 
23
24
  const template = createApiTemplate({
24
25
  schemaComposer,
25
26
  dataSource: {
26
- roleArn: {
27
- 'Fn::ImportValue': 'AppSyncLambdaDataSourceIAMRoleArn',
28
- },
27
+ roleArn: importValueFromParameter('AppSyncLambdaDataSourceIAMRoleArn'),
29
28
  },
30
29
  lambdaFunction: {
31
- roleArn: {
32
- 'Fn::ImportValue': 'AppSyncLambdaFunctionIAMRoleArn',
30
+ roleArn: importValueFromParameter('AppSyncLambdaFunctionIAMRoleArn'),
31
+ environment: {
32
+ variables: {
33
+ TABLE_NAME: { Ref: 'DynamoTableName' },
34
+ SHARED_SECRET: importValueFromParameter('SharedSecretExportedName'),
35
+ },
33
36
  },
34
37
  },
35
38
  });
@@ -69,6 +72,58 @@ The `createAppSyncResolverHandler` function adds the `context` object to the res
69
72
  - `request` - AppSync request object (see [Request section](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)).
70
73
  - `identity` - AppSync identity object (see [Identity section](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)).
71
74
 
75
+ ### createContext
76
+
77
+ Use `createContext` to enrich the resolver context once per request. Its return value is shallow-merged into the base context, making it available to every resolver. This is the recommended way to resolve per-request values like a `userId` from Cognito:
78
+
79
+ ```ts
80
+ import { createAppSyncResolverHandler } from '@ttoss/appsync-api';
81
+ import { schemaComposer } from './schemaComposer';
82
+ import { getUserIdFromCognitoSub } from './auth';
83
+
84
+ export const handler = createAppSyncResolverHandler({
85
+ schemaComposer,
86
+ createContext: async ({ identity }) => ({
87
+ userId: await getUserIdFromCognitoSub(identity?.sub),
88
+ }),
89
+ });
90
+ ```
91
+
92
+ Every resolver then receives `context.userId` without having to derive it individually.
93
+
94
+ ### Middlewares
95
+
96
+ You can use [`graphql-middleware`](https://github.com/dimatill/graphql-middleware)-compatible middlewares via the `middlewares` option. Each middleware wraps the resolver — code before `resolve()` runs **before** the resolver, code after runs **after**.
97
+
98
+ In AppSync, each Lambda invocation handles a single field, so a middleware runs exactly once per request.
99
+ Use `middlewares` for authorization rules or cross-cutting logic (logging, tracing). Combine with `createContext` for per-request context enrichment:
100
+
101
+ | | `createContext` | `middlewares` |
102
+ | ------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------ |
103
+ | Runs | Once per request | Once per resolver call |
104
+ | Purpose | Enrich context (e.g. `userId`) | Auth rules, logging, before/after logic |
105
+ | Can block execution | On error (request fails if `createContext` rejects/throws) | Yes (can conditionally block by not calling `resolve` or throwing) |
106
+
107
+ #### Authorization with GraphQL Shield
108
+
109
+ Use [GraphQL Shield](https://the-guild.dev/graphql/shield) to add authorization rules:
110
+
111
+ ```ts
112
+ import { allow, deny, shield } from '@ttoss/graphql-api/shield';
113
+
114
+ const permissions = shield(
115
+ {
116
+ Query: { '*': deny, me: allow },
117
+ },
118
+ { fallbackRule: deny }
119
+ );
120
+
121
+ export const handler = createAppSyncResolverHandler({
122
+ schemaComposer,
123
+ middlewares: [permissions],
124
+ });
125
+ ```
126
+
72
127
  ### Custom domain name
73
128
 
74
129
  You can add a custom domain name to your API using the `customDomain` option.
package/dist/esm/index.js CHANGED
@@ -100,12 +100,15 @@ var createApiTemplate = /* @__PURE__ */__name(({
100
100
  Layers: lambdaFunction.layers,
101
101
  MemorySize: 512,
102
102
  Role: lambdaFunction.roleArn,
103
- Runtime: "nodejs22.x",
103
+ /**
104
+ * https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtimes-supported
105
+ */
106
+ Runtime: "nodejs24.x",
104
107
  /**
105
108
  * https://docs.aws.amazon.com/general/latest/gr/appsync.html
106
109
  * Request execution time for mutations, queries, and subscriptions: 30 seconds
107
110
  */
108
- Timeout: 29
111
+ Timeout: 30
109
112
  }
110
113
  },
111
114
  [AppSyncLambdaFunctionAppSyncDataSourceLogicalId]: {
@@ -225,8 +228,44 @@ var createApiTemplate = /* @__PURE__ */__name(({
225
228
  }
226
229
  if (customDomain) {
227
230
  const AppSyncDomainNameLogicalId = "AppSyncDomainName";
231
+ const HasCustomDomainCondition = "HasCustomDomain";
232
+ const domainNameRef = typeof customDomain.domainName === "object" && "Ref" in customDomain.domainName ? customDomain.domainName.Ref : null;
233
+ const certificateArnRef = typeof customDomain.certificateArn === "object" && "Ref" in customDomain.certificateArn ? customDomain.certificateArn.Ref : null;
234
+ if (domainNameRef || certificateArnRef) {
235
+ if (!template.Parameters) {
236
+ template.Parameters = {};
237
+ }
238
+ }
239
+ if (domainNameRef && !template.Parameters?.[domainNameRef]) {
240
+ template.Parameters[domainNameRef] = {
241
+ Default: "",
242
+ Type: "String"
243
+ };
244
+ }
245
+ if (certificateArnRef && !template.Parameters?.[certificateArnRef]) {
246
+ template.Parameters[certificateArnRef] = {
247
+ Default: "",
248
+ Type: "String"
249
+ };
250
+ }
251
+ if (domainNameRef) {
252
+ if (!template.Conditions) {
253
+ template.Conditions = {};
254
+ }
255
+ template.Conditions[HasCustomDomainCondition] = {
256
+ "Fn::Not": [{
257
+ "Fn::Equals": [{
258
+ Ref: domainNameRef
259
+ }, ""]
260
+ }]
261
+ };
262
+ }
263
+ const customDomainCondition = domainNameRef ? {
264
+ Condition: HasCustomDomainCondition
265
+ } : {};
228
266
  template.Resources[AppSyncDomainNameLogicalId] = {
229
267
  Type: "AWS::AppSync::DomainName",
268
+ ...customDomainCondition,
230
269
  Properties: {
231
270
  CertificateArn: customDomain.certificateArn,
232
271
  Description: "Custom domain for AppSync API",
@@ -237,6 +276,7 @@ var createApiTemplate = /* @__PURE__ */__name(({
237
276
  const hostedZoneName = customDomain.hostedZoneName.endsWith(".") ? customDomain.hostedZoneName : `${customDomain.hostedZoneName}.`;
238
277
  template.Resources.AppSyncDomainNameRoute53RecordSet = {
239
278
  Type: "AWS::Route53::RecordSet",
279
+ ...customDomainCondition,
240
280
  Properties: {
241
281
  HostedZoneName: hostedZoneName,
242
282
  Name: customDomain.domainName,
@@ -250,6 +290,7 @@ var createApiTemplate = /* @__PURE__ */__name(({
250
290
  }
251
291
  template.Resources.AppSyncDomainNameApiAssociation = {
252
292
  Type: "AWS::AppSync::DomainNameApiAssociation",
293
+ ...customDomainCondition,
253
294
  Properties: {
254
295
  ApiId: {
255
296
  "Fn::GetAtt": [AppSyncGraphQLApiLogicalId, "ApiId"]
@@ -264,12 +305,14 @@ var createApiTemplate = /* @__PURE__ */__name(({
264
305
  }
265
306
  template.Outputs.DomainName = {
266
307
  Description: "Custom domain name for AppSync API",
308
+ ...customDomainCondition,
267
309
  Value: {
268
310
  "Fn::GetAtt": [AppSyncDomainNameLogicalId, "DomainName"]
269
311
  }
270
312
  };
271
313
  template.Outputs.CloudFrontDomainName = {
272
314
  Description: "CloudFront domain name for AppSync API",
315
+ ...customDomainCondition,
273
316
  Value: {
274
317
  "Fn::GetAtt": [AppSyncDomainNameLogicalId, "AppSyncDomainName"]
275
318
  }
@@ -281,6 +324,7 @@ var createApiTemplate = /* @__PURE__ */__name(({
281
324
  // src/createAppSyncResolverHandler.ts
282
325
  import { buildSchema } from "@ttoss/graphql-api";
283
326
  var createAppSyncResolverHandler = /* @__PURE__ */__name(({
327
+ createContext,
284
328
  ...buildSchemaInput
285
329
  }) => {
286
330
  return async (event, appSyncHandlerContext) => {
@@ -297,11 +341,15 @@ var createAppSyncResolverHandler = /* @__PURE__ */__name(({
297
341
  parentTypeName,
298
342
  fieldName
299
343
  } = info;
300
- const context = {
344
+ const baseContext = {
301
345
  handler: appSyncHandlerContext,
302
346
  request,
303
347
  identity: event.identity
304
348
  };
349
+ const context = createContext ? {
350
+ ...baseContext,
351
+ ...(await createContext(baseContext))
352
+ } : baseContext;
305
353
  const schema = buildSchema(buildSchemaInput);
306
354
  const parentType = schema.getType(parentTypeName);
307
355
  if (!parentType) {
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _ttoss_graphql_api from '@ttoss/graphql-api';
2
2
  import { SchemaComposer, BuildSchemaInput } from '@ttoss/graphql-api';
3
- import { AppSyncResolverHandler as AppSyncResolverHandler$1 } from 'aws-lambda';
3
+ import { AppSyncResolverHandler as AppSyncResolverHandler$1, Context, AppSyncIdentity } from 'aws-lambda';
4
4
  export { AppSyncIdentityCognito } from 'aws-lambda';
5
5
 
6
6
  type CloudFormationRef = {
@@ -21,7 +21,10 @@ type CloudFormationSelect = {
21
21
  type CloudFormationSplit = {
22
22
  'Fn::Split': [string, string];
23
23
  };
24
- type CloudFormationIntrinsic = CloudFormationRef | CloudFormationGetAtt | CloudFormationJoin | CloudFormationSub | CloudFormationSelect | CloudFormationSplit;
24
+ type CloudFormationImportValue = {
25
+ 'Fn::ImportValue': string | CloudFormationSub;
26
+ };
27
+ type CloudFormationIntrinsic = CloudFormationRef | CloudFormationGetAtt | CloudFormationJoin | CloudFormationSub | CloudFormationSelect | CloudFormationSplit | CloudFormationImportValue;
25
28
  type CloudFormationValue<T = any> = T | CloudFormationIntrinsic;
26
29
  type Parameter = {
27
30
  AllowedValues?: string[];
@@ -75,9 +78,6 @@ type CloudFormationTemplate = {
75
78
  Outputs?: Outputs;
76
79
  };
77
80
 
78
- type StringOrImport = string | {
79
- 'Fn::ImportValue': string;
80
- };
81
81
  /**
82
82
  * https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html
83
83
  */
@@ -86,31 +86,66 @@ declare const createApiTemplate: ({ additionalAuthenticationProviders, authentic
86
86
  additionalAuthenticationProviders?: AuthenticationType[];
87
87
  authenticationType?: AuthenticationType;
88
88
  customDomain?: {
89
- domainName: string;
90
- certificateArn: string;
89
+ domainName: string | {
90
+ Ref: string;
91
+ };
92
+ certificateArn: string | {
93
+ Ref: string;
94
+ };
91
95
  hostedZoneName?: string;
92
96
  };
93
97
  dataSource: {
94
- roleArn: StringOrImport;
98
+ roleArn: CloudFormationValue<string>;
95
99
  };
96
100
  lambdaFunction: {
97
101
  environment?: {
98
- variables: Record<string, string>;
102
+ variables: Record<string, CloudFormationValue<string>>;
99
103
  };
100
104
  layers?: any;
101
- roleArn: StringOrImport;
105
+ roleArn: CloudFormationValue<string>;
102
106
  };
103
107
  schemaComposer: SchemaComposer<any>;
104
108
  userPoolConfig?: {
105
- appIdClientRegex: StringOrImport;
106
- awsRegion: StringOrImport;
109
+ appIdClientRegex: CloudFormationValue<string>;
110
+ awsRegion: CloudFormationValue<string>;
107
111
  defaultAction: "ALLOW" | "DENY";
108
- userPoolId: StringOrImport;
112
+ userPoolId: CloudFormationValue<string>;
109
113
  };
110
114
  }) => CloudFormationTemplate;
111
115
 
112
116
  type AppSyncResolverHandler<TArguments, TResult, TSource = Record<string, any> | null> = AppSyncResolverHandler$1<TArguments, TResult, TSource>;
113
- declare const createAppSyncResolverHandler: ({ ...buildSchemaInput }: BuildSchemaInput) => AppSyncResolverHandler<any, any, any>;
117
+ /**
118
+ * The base context object passed to all AppSync resolvers.
119
+ */
120
+ type BaseAppSyncContext = {
121
+ /** The raw Lambda invocation context. */
122
+ handler: Context;
123
+ /** The AppSync request object (includes headers). */
124
+ request: any;
125
+ /** The caller's identity (Cognito, IAM, Lambda, or OIDC). Null when using API key auth. */
126
+ identity: AppSyncIdentity | null | undefined;
127
+ };
128
+ declare const createAppSyncResolverHandler: ({ createContext, ...buildSchemaInput }: BuildSchemaInput & {
129
+ /**
130
+ * Optional async function called once per request to enrich the resolver
131
+ * context. The returned object is shallow-merged into the base context and
132
+ * made available to every resolver.
133
+ *
134
+ * Use this for per-request setup such as resolving a `userId` from Cognito.
135
+ * For authorization rules or before/after resolver logic, prefer `middlewares`.
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * createAppSyncResolverHandler({
140
+ * schemaComposer,
141
+ * createContext: async ({ identity }) => ({
142
+ * userId: await getUserIdFromCognitoSub(identity?.sub),
143
+ * }),
144
+ * });
145
+ * ```
146
+ */
147
+ createContext?: (baseContext: BaseAppSyncContext) => Promise<Record<string, any>> | Record<string, any>;
148
+ }) => AppSyncResolverHandler<any, any, any>;
114
149
 
115
150
  /** AWS AppSync scalar for JSON data. Represents a JSON object or array. */
116
151
  declare const AWSJSONTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
@@ -131,4 +166,4 @@ declare const AWSPhoneTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
131
166
  /** AWS AppSync scalar for IPv4 and IPv6 addresses. */
132
167
  declare const AWSIPAddressTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
133
168
 
134
- export { AWSDateTC, AWSDateTimeTC, AWSEmailTC, AWSIPAddressTC, AWSJSONTC, AWSPhoneTC, AWSTimeTC, AWSTimestampTC, AWSURLTC, type AppSyncResolverHandler, createApiTemplate, createAppSyncResolverHandler };
169
+ export { AWSDateTC, AWSDateTimeTC, AWSEmailTC, AWSIPAddressTC, AWSJSONTC, AWSPhoneTC, AWSTimeTC, AWSTimestampTC, AWSURLTC, type AppSyncResolverHandler, type BaseAppSyncContext, createApiTemplate, createAppSyncResolverHandler };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _ttoss_graphql_api from '@ttoss/graphql-api';
2
2
  import { SchemaComposer, BuildSchemaInput } from '@ttoss/graphql-api';
3
- import { AppSyncResolverHandler as AppSyncResolverHandler$1 } from 'aws-lambda';
3
+ import { AppSyncResolverHandler as AppSyncResolverHandler$1, Context, AppSyncIdentity } from 'aws-lambda';
4
4
  export { AppSyncIdentityCognito } from 'aws-lambda';
5
5
 
6
6
  type CloudFormationRef = {
@@ -21,7 +21,10 @@ type CloudFormationSelect = {
21
21
  type CloudFormationSplit = {
22
22
  'Fn::Split': [string, string];
23
23
  };
24
- type CloudFormationIntrinsic = CloudFormationRef | CloudFormationGetAtt | CloudFormationJoin | CloudFormationSub | CloudFormationSelect | CloudFormationSplit;
24
+ type CloudFormationImportValue = {
25
+ 'Fn::ImportValue': string | CloudFormationSub;
26
+ };
27
+ type CloudFormationIntrinsic = CloudFormationRef | CloudFormationGetAtt | CloudFormationJoin | CloudFormationSub | CloudFormationSelect | CloudFormationSplit | CloudFormationImportValue;
25
28
  type CloudFormationValue<T = any> = T | CloudFormationIntrinsic;
26
29
  type Parameter = {
27
30
  AllowedValues?: string[];
@@ -75,9 +78,6 @@ type CloudFormationTemplate = {
75
78
  Outputs?: Outputs;
76
79
  };
77
80
 
78
- type StringOrImport = string | {
79
- 'Fn::ImportValue': string;
80
- };
81
81
  /**
82
82
  * https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html
83
83
  */
@@ -86,31 +86,66 @@ declare const createApiTemplate: ({ additionalAuthenticationProviders, authentic
86
86
  additionalAuthenticationProviders?: AuthenticationType[];
87
87
  authenticationType?: AuthenticationType;
88
88
  customDomain?: {
89
- domainName: string;
90
- certificateArn: string;
89
+ domainName: string | {
90
+ Ref: string;
91
+ };
92
+ certificateArn: string | {
93
+ Ref: string;
94
+ };
91
95
  hostedZoneName?: string;
92
96
  };
93
97
  dataSource: {
94
- roleArn: StringOrImport;
98
+ roleArn: CloudFormationValue<string>;
95
99
  };
96
100
  lambdaFunction: {
97
101
  environment?: {
98
- variables: Record<string, string>;
102
+ variables: Record<string, CloudFormationValue<string>>;
99
103
  };
100
104
  layers?: any;
101
- roleArn: StringOrImport;
105
+ roleArn: CloudFormationValue<string>;
102
106
  };
103
107
  schemaComposer: SchemaComposer<any>;
104
108
  userPoolConfig?: {
105
- appIdClientRegex: StringOrImport;
106
- awsRegion: StringOrImport;
109
+ appIdClientRegex: CloudFormationValue<string>;
110
+ awsRegion: CloudFormationValue<string>;
107
111
  defaultAction: "ALLOW" | "DENY";
108
- userPoolId: StringOrImport;
112
+ userPoolId: CloudFormationValue<string>;
109
113
  };
110
114
  }) => CloudFormationTemplate;
111
115
 
112
116
  type AppSyncResolverHandler<TArguments, TResult, TSource = Record<string, any> | null> = AppSyncResolverHandler$1<TArguments, TResult, TSource>;
113
- declare const createAppSyncResolverHandler: ({ ...buildSchemaInput }: BuildSchemaInput) => AppSyncResolverHandler<any, any, any>;
117
+ /**
118
+ * The base context object passed to all AppSync resolvers.
119
+ */
120
+ type BaseAppSyncContext = {
121
+ /** The raw Lambda invocation context. */
122
+ handler: Context;
123
+ /** The AppSync request object (includes headers). */
124
+ request: any;
125
+ /** The caller's identity (Cognito, IAM, Lambda, or OIDC). Null when using API key auth. */
126
+ identity: AppSyncIdentity | null | undefined;
127
+ };
128
+ declare const createAppSyncResolverHandler: ({ createContext, ...buildSchemaInput }: BuildSchemaInput & {
129
+ /**
130
+ * Optional async function called once per request to enrich the resolver
131
+ * context. The returned object is shallow-merged into the base context and
132
+ * made available to every resolver.
133
+ *
134
+ * Use this for per-request setup such as resolving a `userId` from Cognito.
135
+ * For authorization rules or before/after resolver logic, prefer `middlewares`.
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * createAppSyncResolverHandler({
140
+ * schemaComposer,
141
+ * createContext: async ({ identity }) => ({
142
+ * userId: await getUserIdFromCognitoSub(identity?.sub),
143
+ * }),
144
+ * });
145
+ * ```
146
+ */
147
+ createContext?: (baseContext: BaseAppSyncContext) => Promise<Record<string, any>> | Record<string, any>;
148
+ }) => AppSyncResolverHandler<any, any, any>;
114
149
 
115
150
  /** AWS AppSync scalar for JSON data. Represents a JSON object or array. */
116
151
  declare const AWSJSONTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
@@ -131,4 +166,4 @@ declare const AWSPhoneTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
131
166
  /** AWS AppSync scalar for IPv4 and IPv6 addresses. */
132
167
  declare const AWSIPAddressTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
133
168
 
134
- export { AWSDateTC, AWSDateTimeTC, AWSEmailTC, AWSIPAddressTC, AWSJSONTC, AWSPhoneTC, AWSTimeTC, AWSTimestampTC, AWSURLTC, type AppSyncResolverHandler, createApiTemplate, createAppSyncResolverHandler };
169
+ export { AWSDateTC, AWSDateTimeTC, AWSEmailTC, AWSIPAddressTC, AWSJSONTC, AWSPhoneTC, AWSTimeTC, AWSTimestampTC, AWSURLTC, type AppSyncResolverHandler, type BaseAppSyncContext, createApiTemplate, createAppSyncResolverHandler };
package/dist/index.js CHANGED
@@ -140,12 +140,15 @@ var createApiTemplate = /* @__PURE__ */__name(({
140
140
  Layers: lambdaFunction.layers,
141
141
  MemorySize: 512,
142
142
  Role: lambdaFunction.roleArn,
143
- Runtime: "nodejs22.x",
143
+ /**
144
+ * https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtimes-supported
145
+ */
146
+ Runtime: "nodejs24.x",
144
147
  /**
145
148
  * https://docs.aws.amazon.com/general/latest/gr/appsync.html
146
149
  * Request execution time for mutations, queries, and subscriptions: 30 seconds
147
150
  */
148
- Timeout: 29
151
+ Timeout: 30
149
152
  }
150
153
  },
151
154
  [AppSyncLambdaFunctionAppSyncDataSourceLogicalId]: {
@@ -265,8 +268,44 @@ var createApiTemplate = /* @__PURE__ */__name(({
265
268
  }
266
269
  if (customDomain) {
267
270
  const AppSyncDomainNameLogicalId = "AppSyncDomainName";
271
+ const HasCustomDomainCondition = "HasCustomDomain";
272
+ const domainNameRef = typeof customDomain.domainName === "object" && "Ref" in customDomain.domainName ? customDomain.domainName.Ref : null;
273
+ const certificateArnRef = typeof customDomain.certificateArn === "object" && "Ref" in customDomain.certificateArn ? customDomain.certificateArn.Ref : null;
274
+ if (domainNameRef || certificateArnRef) {
275
+ if (!template.Parameters) {
276
+ template.Parameters = {};
277
+ }
278
+ }
279
+ if (domainNameRef && !template.Parameters?.[domainNameRef]) {
280
+ template.Parameters[domainNameRef] = {
281
+ Default: "",
282
+ Type: "String"
283
+ };
284
+ }
285
+ if (certificateArnRef && !template.Parameters?.[certificateArnRef]) {
286
+ template.Parameters[certificateArnRef] = {
287
+ Default: "",
288
+ Type: "String"
289
+ };
290
+ }
291
+ if (domainNameRef) {
292
+ if (!template.Conditions) {
293
+ template.Conditions = {};
294
+ }
295
+ template.Conditions[HasCustomDomainCondition] = {
296
+ "Fn::Not": [{
297
+ "Fn::Equals": [{
298
+ Ref: domainNameRef
299
+ }, ""]
300
+ }]
301
+ };
302
+ }
303
+ const customDomainCondition = domainNameRef ? {
304
+ Condition: HasCustomDomainCondition
305
+ } : {};
268
306
  template.Resources[AppSyncDomainNameLogicalId] = {
269
307
  Type: "AWS::AppSync::DomainName",
308
+ ...customDomainCondition,
270
309
  Properties: {
271
310
  CertificateArn: customDomain.certificateArn,
272
311
  Description: "Custom domain for AppSync API",
@@ -277,6 +316,7 @@ var createApiTemplate = /* @__PURE__ */__name(({
277
316
  const hostedZoneName = customDomain.hostedZoneName.endsWith(".") ? customDomain.hostedZoneName : `${customDomain.hostedZoneName}.`;
278
317
  template.Resources.AppSyncDomainNameRoute53RecordSet = {
279
318
  Type: "AWS::Route53::RecordSet",
319
+ ...customDomainCondition,
280
320
  Properties: {
281
321
  HostedZoneName: hostedZoneName,
282
322
  Name: customDomain.domainName,
@@ -290,6 +330,7 @@ var createApiTemplate = /* @__PURE__ */__name(({
290
330
  }
291
331
  template.Resources.AppSyncDomainNameApiAssociation = {
292
332
  Type: "AWS::AppSync::DomainNameApiAssociation",
333
+ ...customDomainCondition,
293
334
  Properties: {
294
335
  ApiId: {
295
336
  "Fn::GetAtt": [AppSyncGraphQLApiLogicalId, "ApiId"]
@@ -304,12 +345,14 @@ var createApiTemplate = /* @__PURE__ */__name(({
304
345
  }
305
346
  template.Outputs.DomainName = {
306
347
  Description: "Custom domain name for AppSync API",
348
+ ...customDomainCondition,
307
349
  Value: {
308
350
  "Fn::GetAtt": [AppSyncDomainNameLogicalId, "DomainName"]
309
351
  }
310
352
  };
311
353
  template.Outputs.CloudFrontDomainName = {
312
354
  Description: "CloudFront domain name for AppSync API",
355
+ ...customDomainCondition,
313
356
  Value: {
314
357
  "Fn::GetAtt": [AppSyncDomainNameLogicalId, "AppSyncDomainName"]
315
358
  }
@@ -321,6 +364,7 @@ var createApiTemplate = /* @__PURE__ */__name(({
321
364
  // src/createAppSyncResolverHandler.ts
322
365
  var import_graphql_api2 = require("@ttoss/graphql-api");
323
366
  var createAppSyncResolverHandler = /* @__PURE__ */__name(({
367
+ createContext,
324
368
  ...buildSchemaInput
325
369
  }) => {
326
370
  return async (event, appSyncHandlerContext) => {
@@ -337,11 +381,15 @@ var createAppSyncResolverHandler = /* @__PURE__ */__name(({
337
381
  parentTypeName,
338
382
  fieldName
339
383
  } = info;
340
- const context = {
384
+ const baseContext = {
341
385
  handler: appSyncHandlerContext,
342
386
  request,
343
387
  identity: event.identity
344
388
  };
389
+ const context = createContext ? {
390
+ ...baseContext,
391
+ ...(await createContext(baseContext))
392
+ } : baseContext;
345
393
  const schema = (0, import_graphql_api2.buildSchema)(buildSchemaInput);
346
394
  const parentType = schema.getType(parentTypeName);
347
395
  if (!parentType) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/appsync-api",
3
- "version": "0.22.15",
3
+ "version": "0.23.1",
4
4
  "description": "A library for building GraphQL APIs for AWS AppSync.",
5
5
  "license": "MIT",
6
6
  "author": "ttoss",
@@ -24,7 +24,7 @@
24
24
  ],
25
25
  "sideEffects": false,
26
26
  "dependencies": {
27
- "@ttoss/cloudformation": "^0.12.0"
27
+ "@ttoss/cloudformation": "^0.12.1"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "graphql": "^16.6.0",
@@ -36,9 +36,9 @@
36
36
  "graphql-shield": "^7.6.5",
37
37
  "jest": "^30.2.0",
38
38
  "tsup": "^8.5.1",
39
- "@ttoss/config": "^1.36.0",
39
+ "@ttoss/graphql-api": "^0.8.17",
40
40
  "@ttoss/ids": "^0.3.14",
41
- "@ttoss/graphql-api": "^0.8.17"
41
+ "@ttoss/config": "^1.36.0"
42
42
  },
43
43
  "keywords": [
44
44
  "api",