@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 +61 -6
- package/dist/esm/index.js +51 -3
- package/dist/index.d.mts +50 -15
- package/dist/index.d.ts +50 -15
- package/dist/index.js +51 -3
- package/package.json +4 -4
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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:
|
|
105
|
+
roleArn: CloudFormationValue<string>;
|
|
102
106
|
};
|
|
103
107
|
schemaComposer: SchemaComposer<any>;
|
|
104
108
|
userPoolConfig?: {
|
|
105
|
-
appIdClientRegex:
|
|
106
|
-
awsRegion:
|
|
109
|
+
appIdClientRegex: CloudFormationValue<string>;
|
|
110
|
+
awsRegion: CloudFormationValue<string>;
|
|
107
111
|
defaultAction: "ALLOW" | "DENY";
|
|
108
|
-
userPoolId:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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:
|
|
105
|
+
roleArn: CloudFormationValue<string>;
|
|
102
106
|
};
|
|
103
107
|
schemaComposer: SchemaComposer<any>;
|
|
104
108
|
userPoolConfig?: {
|
|
105
|
-
appIdClientRegex:
|
|
106
|
-
awsRegion:
|
|
109
|
+
appIdClientRegex: CloudFormationValue<string>;
|
|
110
|
+
awsRegion: CloudFormationValue<string>;
|
|
107
111
|
defaultAction: "ALLOW" | "DENY";
|
|
108
|
-
userPoolId:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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.
|
|
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.
|
|
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/
|
|
39
|
+
"@ttoss/graphql-api": "^0.8.17",
|
|
40
40
|
"@ttoss/ids": "^0.3.14",
|
|
41
|
-
"@ttoss/
|
|
41
|
+
"@ttoss/config": "^1.36.0"
|
|
42
42
|
},
|
|
43
43
|
"keywords": [
|
|
44
44
|
"api",
|