appsync
AppSync Lambda Functions
For complex form validation—such as checking unique database fields or running heavy business logic—you should delegate validation to an AWS Lambda function attached to your AppSync resolver.
How AppSync Invokes Lambda
When AppSync invokes a Lambda resolver, it passes the context object as the Lambda event payload. Within this payload, you can access:
event.arguments.input— the user input arguments.event.request.headers— the HTTP headers sent by the client.
Implementing Validation in Lambda
Inside your Lambda function, you can use any Node.js validation library (like Zod, Yup, or Valibot) to parse and validate the input arguments.
Lambda Code Example (Node.js + Zod)
Here is a typical AWS Lambda handler that validates a request using Zod:
import { Handler } from 'aws-lambda'
import { z } from 'zod'
// 1. Define the validation schema
const RegisterSchema = z.object({
email: z.string().email('Invalid email address'),
username: z.string().min(3, 'Username must be at least 3 characters'),
password: z.string().min(8, 'Password must be at least 8 characters'),
})
export const handler: Handler = async (event) => {
const input = event.arguments.input
const headers = event.request.headers || {}
const isPrecognitive = headers.precognitive === 'true'
const validateOnly = headers['precognition-validate-only']
? headers['precognition-validate-only'].split(',')
: []
// 2. Perform validation
const result = RegisterSchema.safeParse(input)
if (!result.success) {
// Format Zod errors to { fieldName: [message1, message2] }
const validationErrors: Record<string, string[]> = {}
result.error.errors.forEach((err) => {
const field = err.path.join('.')
if (!validationErrors[field]) {
validationErrors[field] = []
}
validationErrors[field].push(err.message)
})
// Filter validation errors based on ValidateOnly header
const filteredErrors: Record<string, string[]> = {}
if (validateOnly.length > 0) {
for (const field of validateOnly) {
if (validationErrors[field]) {
filteredErrors[field] = validationErrors[field]
}
}
}
else {
Object.assign(filteredErrors, validationErrors)
}
// 3. Throw validation error if there are issues
if (Object.keys(filteredErrors).length > 0) {
throw new Error(JSON.stringify({
errorType: 'ValidationError',
validationErrors: filteredErrors
}))
}
}
// 4. Early exit for precognitive requests
if (isPrecognitive) {
throw new Error(JSON.stringify({
errorType: 'PrecognitionSuccess'
}))
}
// 5. Normal processing (e.g. save to DB, create user)
// ...
return {
id: 'user_123',
email: input.email,
username: input.username
}
}
AppSync Resolver Configuration
If the Lambda throws an error using throw new Error(), AWS AppSync will automatically capture the error message. You can use standard VTL or JS resolver response mapping to parse the JSON string and return a structured GraphQL error payload to the client.
Resolver Response Handler (APPSYNC_JS)
export function response(ctx) {
if (ctx.error) {
// Attempt to parse validation errors thrown by Lambda
try {
const parsedError = JSON.parse(ctx.error.message)
if (parsedError.errorType === 'ValidationError') {
util.error(
'Validation Failed',
'ValidationError',
null,
{ validationErrors: parsedError.validationErrors }
)
}
if (parsedError.errorType === 'PrecognitionSuccess') {
util.error(
'Precognition Success',
'PrecognitionSuccess'
)
}
}
catch (e) {
// Fallback for non-JSON errors
util.error(ctx.error.message, ctx.error.type)
}
}
return ctx.result
}
Benefits of Lambda Validation
Using Lambda for Precognition validation offers several advantages:
- Reusability: Share validation schemas between your frontend, API gateway, and backend handlers.
- Database Checks: Run dynamic validation rules (e.g., querying DynamoDB to check if a
usernameoremailis already registered) before completing the request. - Advanced libraries: Access NPM validation ecosystems (like Zod) which are not fully supported or are heavier to run inside local AppSync resolver environments.