Skip to main content

@ttoss/appsync-api

This package provides a opinionated way to create an AppSync API using @ttoss/graphql-api API.

Installation

pnpm add @ttoss/appsync-api @ttoss/graphql-api graphql

Getting Started

You can create and deploy an AppSync API in four steps:

  1. Create a schemaComposer object using graphql-compose, that the next steps will use to create the API.

  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:

import { importValueFromParameter } from '@ttoss/cloudformation';
import { createApiTemplate } from '@ttoss/appsync-api';
import { schemaComposer } from './schemaComposer';

const template = createApiTemplate({
schemaComposer,
dataSource: {
roleArn: importValueFromParameter('AppSyncLambdaDataSourceIAMRoleArn'),
},
lambdaFunction: {
roleArn: importValueFromParameter('AppSyncLambdaFunctionIAMRoleArn'),
environment: {
variables: {
TABLE_NAME: { Ref: 'DynamoTableName' },
SHARED_SECRET: importValueFromParameter('SharedSecretExportedName'),
},
},
},
});

export default template;
  1. Create a lambda.ts file that exports a Lambda handler function using createAppSyncResolverHandler:
import { createAppSyncResolverHandler } from '@ttoss/appsync-api';
import { schemaComposer } from './schemaComposer';

export const handler = createAppSyncResolverHandler({ schemaComposer });
  1. Add graphql to the lambdaExternals array on carlin.yml:
lambdaExternals:
- graphql

Now you can deploy your API using carlin deploy:

carlin deploy

API

Resolvers Context

The createAppSyncResolverHandler function adds the context object to the resolvers. This object contains the following properties:

createContext

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:

import { createAppSyncResolverHandler } from '@ttoss/appsync-api';
import { schemaComposer } from './schemaComposer';
import { getUserIdFromCognitoSub } from './auth';

export const handler = createAppSyncResolverHandler({
schemaComposer,
createContext: async ({ identity }) => ({
userId: await getUserIdFromCognitoSub(identity?.sub),
}),
});

Every resolver then receives context.userId without having to derive it individually.

Middlewares

You can use graphql-middleware-compatible middlewares via the middlewares option. Each middleware wraps the resolver — code before resolve() runs before the resolver, code after runs after.

In AppSync, each Lambda invocation handles a single field, so a middleware runs exactly once per request. Use middlewares for authorization rules or cross-cutting logic (logging, tracing). Combine with createContext for per-request context enrichment:

createContextmiddlewares
RunsOnce per requestOnce per resolver call
PurposeEnrich context (e.g. userId)Auth rules, logging, before/after logic
Can block executionOn error (request fails if createContext rejects/throws)Yes (can conditionally block by not calling resolve or throwing)

Authorization with GraphQL Shield

Use GraphQL Shield to add authorization rules:

import { allow, deny, shield } from '@ttoss/graphql-api/shield';

const permissions = shield(
{
Query: { '*': deny, me: allow },
},
{ fallbackRule: deny }
);

export const handler = createAppSyncResolverHandler({
schemaComposer,
middlewares: [permissions],
});

Custom domain name

You can add a custom domain name to your API using the customDomain option.

import { createApiTemplate } from '@ttoss/appsync-api';

export const handler = createApiTemplate({
schemaComposer,
customDomain: {
domainName: 'api.example.com', // required
certificateArn: {
'Fn::ImportValue': 'AppSyncDomainCertificateArn',
}, // required
},
});

If your domain is on Route53, you can use the option customDomain.hostedZoneName to create the required DNS records.

import { createApiTemplate } from '@ttoss/appsync-api';

export const template = createApiTemplate({
schemaComposer,
customDomain: {
domainName: 'api.example.com', // required
certificateArn: {
'Fn::ImportValue': 'AppSyncDomainCertificateArn',
}, // required
hostedZoneName: 'example.com.', // optional
},
});