Open
Description
Use case
The TypeScript version of Powertools already has a Router
class for organizing GraphQL resolvers, but it's missing a functionality that exists in the Python version:
- Router Composition: The ability to include multiple
Router
instances in the mainAppSyncGraphQLResolver
using anincludeRouter()
method - Context Sharing: The ability to share contextual data between the main resolver and included routers using
appendContext()
Currently, developers can create Router
instances but cannot easily compose them into a main resolver, limiting the modularity benefits. The Python version already has this functionality through include_router()
and append_context()
methods.
Solution/User Experience
Add the missing router composition and context sharing functionality to match the Python implementation:
includeRouter()
method: AllowAppSyncGraphQLResolver
to include multipleRouter
instancesappendContext()
method: Enable sharing data between main resolver and routers- Context access: Allow routers to access shared context data in their handlers
Example Usage:
// userRoutes.ts
import { Router } from '@aws-lambda-powertools/event-handler/appsync-graphql';
const userRouter = new Router();
userRouter.onQuery<{ id: string }>('getUser', async ({ id }, { context }) => {
// Access shared context
const isAdmin = context?.get('isAdmin', false);
return {
id,
name: 'John Doe',
email: isAdmin ? '[email protected]' : 'hidden'
};
});
userRouter.onMutation<{ name: string; email: string }>('createUser', async ({ name, email }) => {
return { id: makeId(), name, email };
});
export { userRouter };
// todoRoutes.ts
import { Router } from '@aws-lambda-powertools/event-handler/appsync-graphql';
const todoRouter = new Router();
todoRouter.onQuery<{ id: string }>('getTodo', async ({ id }, { context }) => {
const requestId = context?.get('requestId');
logger.info('Fetching todo', { id, requestId });
return { id, title: 'Sample Todo', completed: false };
});
export { todoRouter };
// index.ts - Main Lambda handler
import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';
import { userRouter } from './userRoutes.js';
import { todoRouter } from './todoRoutes.js';
const logger = new Logger({ serviceName: 'GraphQLAPI' });
const app = new AppSyncGraphQLResolver({ logger });
// Include routers - NEW FUNCTIONALITY
app.includeRouter(userRouter);
app.includeRouter(todoRouter);
export const handler = async (event: unknown, context: Context) => {
// Share context between main app and routers - NEW FUNCTIONALITY
app.appendContext({
isAdmin: true,
requestId: context.awsRequestId,
timestamp: Date.now()
});
return app.resolve(event, context);
};
Implementation Details:
- Router Registry Merging: The
includeRouter()
method should merge the router's resolver registry with the main resolver's registry - Context Management:
- Add a context storage mechanism (Map or similar) to store key-value pairs
- Context should be accessible in resolver handlers via the options parameter
- Context should be cleared after each invocation for safety
- Type Safety: Ensure proper TypeScript types for context methods and router inclusion
Expected API:
class AppSyncGraphQLResolver extends Router {
// NEW: Include a router instance
public includeRouter(router: Router): void;
// NEW: Add context data to be shared with routers
public appendContext(data: Record<string, unknown>): void;
// NEW: Access to context (internal)
public readonly context: Map<string, unknown>;
}
// Updated resolver handler signature to include context access
type ResolverHandler<TParams = Record<string, unknown>> = (
args: TParams,
options: {
event: AppSyncResolverEvent<TParams>;
context: Context;
// NEW: Shared context access
context?: Map<string, unknown>;
}
) => Promise<unknown> | unknown;
Alternative solutions
- Manual Registry Merging: Developers could manually access the router's registry and merge it, but this exposes internal implementation details
- Inheritance Pattern: Create a base class that both Router and AppSyncGraphQLResolver extend, but this changes the current architecture
- Composition via Constructor: Pass routers during AppSyncGraphQLResolver construction, but this is less flexible than the include pattern
Benefits:
- Consistency: Matches the Python implementation's API
- Modularity: Enables better code organization across multiple files/modules
- Context Sharing: Allows passing request-scoped data between resolvers
- Backward Compatibility: Doesn't break existing Router or AppSyncGraphQLResolver usage
Acknowledgment
- This feature request meets Powertools for AWS Lambda (TypeScript) Tenets
- Should this be considered in other Powertools for AWS Lambda languages? i.e. Python, Java, and .NET
Future readers
Please react with 👍 and your use case to help us understand customer demand.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
Backlog