@ttoss/appsync-api 0.22.15 → 0.23.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
@@ -69,6 +69,58 @@ The `createAppSyncResolverHandler` function adds the `context` object to the res
69
69
  - `request` - AppSync request object (see [Request section](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)).
70
70
  - `identity` - AppSync identity object (see [Identity section](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)).
71
71
 
72
+ ### createContext
73
+
74
+ 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:
75
+
76
+ ```ts
77
+ import { createAppSyncResolverHandler } from '@ttoss/appsync-api';
78
+ import { schemaComposer } from './schemaComposer';
79
+ import { getUserIdFromCognitoSub } from './auth';
80
+
81
+ export const handler = createAppSyncResolverHandler({
82
+ schemaComposer,
83
+ createContext: async ({ identity }) => ({
84
+ userId: await getUserIdFromCognitoSub(identity?.sub),
85
+ }),
86
+ });
87
+ ```
88
+
89
+ Every resolver then receives `context.userId` without having to derive it individually.
90
+
91
+ ### Middlewares
92
+
93
+ 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**.
94
+
95
+ In AppSync, each Lambda invocation handles a single field, so a middleware runs exactly once per request.
96
+ Use `middlewares` for authorization rules or cross-cutting logic (logging, tracing). Combine with `createContext` for per-request context enrichment:
97
+
98
+ | | `createContext` | `middlewares` |
99
+ | ------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------ |
100
+ | Runs | Once per request | Once per resolver call |
101
+ | Purpose | Enrich context (e.g. `userId`) | Auth rules, logging, before/after logic |
102
+ | Can block execution | On error (request fails if `createContext` rejects/throws) | Yes (can conditionally block by not calling `resolve` or throwing) |
103
+
104
+ #### Authorization with GraphQL Shield
105
+
106
+ Use [GraphQL Shield](https://the-guild.dev/graphql/shield) to add authorization rules:
107
+
108
+ ```ts
109
+ import { allow, deny, shield } from '@ttoss/graphql-api/shield';
110
+
111
+ const permissions = shield(
112
+ {
113
+ Query: { '*': deny, me: allow },
114
+ },
115
+ { fallbackRule: deny }
116
+ );
117
+
118
+ export const handler = createAppSyncResolverHandler({
119
+ schemaComposer,
120
+ middlewares: [permissions],
121
+ });
122
+ ```
123
+
72
124
  ### Custom domain name
73
125
 
74
126
  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]: {
@@ -281,6 +284,7 @@ var createApiTemplate = /* @__PURE__ */__name(({
281
284
  // src/createAppSyncResolverHandler.ts
282
285
  import { buildSchema } from "@ttoss/graphql-api";
283
286
  var createAppSyncResolverHandler = /* @__PURE__ */__name(({
287
+ createContext,
284
288
  ...buildSchemaInput
285
289
  }) => {
286
290
  return async (event, appSyncHandlerContext) => {
@@ -297,11 +301,15 @@ var createAppSyncResolverHandler = /* @__PURE__ */__name(({
297
301
  parentTypeName,
298
302
  fieldName
299
303
  } = info;
300
- const context = {
304
+ const baseContext = {
301
305
  handler: appSyncHandlerContext,
302
306
  request,
303
307
  identity: event.identity
304
308
  };
309
+ const context = createContext ? {
310
+ ...baseContext,
311
+ ...(await createContext(baseContext))
312
+ } : baseContext;
305
313
  const schema = buildSchema(buildSchemaInput);
306
314
  const parentType = schema.getType(parentTypeName);
307
315
  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 = {
@@ -110,7 +110,38 @@ declare const createApiTemplate: ({ additionalAuthenticationProviders, authentic
110
110
  }) => CloudFormationTemplate;
111
111
 
112
112
  type AppSyncResolverHandler<TArguments, TResult, TSource = Record<string, any> | null> = AppSyncResolverHandler$1<TArguments, TResult, TSource>;
113
- declare const createAppSyncResolverHandler: ({ ...buildSchemaInput }: BuildSchemaInput) => AppSyncResolverHandler<any, any, any>;
113
+ /**
114
+ * The base context object passed to all AppSync resolvers.
115
+ */
116
+ type BaseAppSyncContext = {
117
+ /** The raw Lambda invocation context. */
118
+ handler: Context;
119
+ /** The AppSync request object (includes headers). */
120
+ request: any;
121
+ /** The caller's identity (Cognito, IAM, Lambda, or OIDC). Null when using API key auth. */
122
+ identity: AppSyncIdentity | null | undefined;
123
+ };
124
+ declare const createAppSyncResolverHandler: ({ createContext, ...buildSchemaInput }: BuildSchemaInput & {
125
+ /**
126
+ * Optional async function called once per request to enrich the resolver
127
+ * context. The returned object is shallow-merged into the base context and
128
+ * made available to every resolver.
129
+ *
130
+ * Use this for per-request setup such as resolving a `userId` from Cognito.
131
+ * For authorization rules or before/after resolver logic, prefer `middlewares`.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * createAppSyncResolverHandler({
136
+ * schemaComposer,
137
+ * createContext: async ({ identity }) => ({
138
+ * userId: await getUserIdFromCognitoSub(identity?.sub),
139
+ * }),
140
+ * });
141
+ * ```
142
+ */
143
+ createContext?: (baseContext: BaseAppSyncContext) => Promise<Record<string, any>> | Record<string, any>;
144
+ }) => AppSyncResolverHandler<any, any, any>;
114
145
 
115
146
  /** AWS AppSync scalar for JSON data. Represents a JSON object or array. */
116
147
  declare const AWSJSONTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
@@ -131,4 +162,4 @@ declare const AWSPhoneTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
131
162
  /** AWS AppSync scalar for IPv4 and IPv6 addresses. */
132
163
  declare const AWSIPAddressTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
133
164
 
134
- export { AWSDateTC, AWSDateTimeTC, AWSEmailTC, AWSIPAddressTC, AWSJSONTC, AWSPhoneTC, AWSTimeTC, AWSTimestampTC, AWSURLTC, type AppSyncResolverHandler, createApiTemplate, createAppSyncResolverHandler };
165
+ 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 = {
@@ -110,7 +110,38 @@ declare const createApiTemplate: ({ additionalAuthenticationProviders, authentic
110
110
  }) => CloudFormationTemplate;
111
111
 
112
112
  type AppSyncResolverHandler<TArguments, TResult, TSource = Record<string, any> | null> = AppSyncResolverHandler$1<TArguments, TResult, TSource>;
113
- declare const createAppSyncResolverHandler: ({ ...buildSchemaInput }: BuildSchemaInput) => AppSyncResolverHandler<any, any, any>;
113
+ /**
114
+ * The base context object passed to all AppSync resolvers.
115
+ */
116
+ type BaseAppSyncContext = {
117
+ /** The raw Lambda invocation context. */
118
+ handler: Context;
119
+ /** The AppSync request object (includes headers). */
120
+ request: any;
121
+ /** The caller's identity (Cognito, IAM, Lambda, or OIDC). Null when using API key auth. */
122
+ identity: AppSyncIdentity | null | undefined;
123
+ };
124
+ declare const createAppSyncResolverHandler: ({ createContext, ...buildSchemaInput }: BuildSchemaInput & {
125
+ /**
126
+ * Optional async function called once per request to enrich the resolver
127
+ * context. The returned object is shallow-merged into the base context and
128
+ * made available to every resolver.
129
+ *
130
+ * Use this for per-request setup such as resolving a `userId` from Cognito.
131
+ * For authorization rules or before/after resolver logic, prefer `middlewares`.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * createAppSyncResolverHandler({
136
+ * schemaComposer,
137
+ * createContext: async ({ identity }) => ({
138
+ * userId: await getUserIdFromCognitoSub(identity?.sub),
139
+ * }),
140
+ * });
141
+ * ```
142
+ */
143
+ createContext?: (baseContext: BaseAppSyncContext) => Promise<Record<string, any>> | Record<string, any>;
144
+ }) => AppSyncResolverHandler<any, any, any>;
114
145
 
115
146
  /** AWS AppSync scalar for JSON data. Represents a JSON object or array. */
116
147
  declare const AWSJSONTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
@@ -131,4 +162,4 @@ declare const AWSPhoneTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
131
162
  /** AWS AppSync scalar for IPv4 and IPv6 addresses. */
132
163
  declare const AWSIPAddressTC: _ttoss_graphql_api.ScalarTypeComposer<any>;
133
164
 
134
- export { AWSDateTC, AWSDateTimeTC, AWSEmailTC, AWSIPAddressTC, AWSJSONTC, AWSPhoneTC, AWSTimeTC, AWSTimestampTC, AWSURLTC, type AppSyncResolverHandler, createApiTemplate, createAppSyncResolverHandler };
165
+ 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]: {
@@ -321,6 +324,7 @@ var createApiTemplate = /* @__PURE__ */__name(({
321
324
  // src/createAppSyncResolverHandler.ts
322
325
  var import_graphql_api2 = require("@ttoss/graphql-api");
323
326
  var createAppSyncResolverHandler = /* @__PURE__ */__name(({
327
+ createContext,
324
328
  ...buildSchemaInput
325
329
  }) => {
326
330
  return async (event, appSyncHandlerContext) => {
@@ -337,11 +341,15 @@ var createAppSyncResolverHandler = /* @__PURE__ */__name(({
337
341
  parentTypeName,
338
342
  fieldName
339
343
  } = info;
340
- const context = {
344
+ const baseContext = {
341
345
  handler: appSyncHandlerContext,
342
346
  request,
343
347
  identity: event.identity
344
348
  };
349
+ const context = createContext ? {
350
+ ...baseContext,
351
+ ...(await createContext(baseContext))
352
+ } : baseContext;
345
353
  const schema = (0, import_graphql_api2.buildSchema)(buildSchemaInput);
346
354
  const parentType = schema.getType(parentTypeName);
347
355
  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.0",
4
4
  "description": "A library for building GraphQL APIs for AWS AppSync.",
5
5
  "license": "MIT",
6
6
  "author": "ttoss",
@@ -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/graphql-api": "^0.8.17",
39
40
  "@ttoss/config": "^1.36.0",
40
- "@ttoss/ids": "^0.3.14",
41
- "@ttoss/graphql-api": "^0.8.17"
41
+ "@ttoss/ids": "^0.3.14"
42
42
  },
43
43
  "keywords": [
44
44
  "api",