Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated Apr 19, 2024

Custom data access using Lambda functions

You can define your own custom authorization rule with a Lambda function.

amplify/data/resource.ts
1import {
2 type ClientSchema,
3 a,
4 defineData,
5 defineFunction,
6} from '@aws-amplify/backend';
7
8const schema = a.schema({
9 Todo: a
10 .model({
11 content: a.string(),
12 })
13 // STEP 1
14 // Indicate which models / fields should use a custom authorization rule
15 .authorization(allow => [allow.custom()]),
16});
17
18export type Schema = ClientSchema<typeof schema>;
19
20export const data = defineData({
21 schema,
22 authorizationModes: {
23 defaultAuthorizationMode: 'lambda',
24 // STEP 2
25 // Pass in the function to be used for a custom authorization rule
26 lambdaAuthorizationMode: {
27 function: defineFunction({
28 entry: './custom-authorizer.ts',
29 }),
30 // (Optional) STEP 3
31 // Configure the token's time to live
32 timeToLiveInSeconds: 300,
33 },
34 },
35});

In your application, you can perform CRUD operations against the model using client.models.<model-name> with the lambda auth mode.

1import { generateClient } from 'aws-amplify/data';
2import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition
3
4const client = generateClient<Schema>();
5
6const { errors, data: newTodo } = await client.models.Todo.create(
7 {
8 content: 'My new todo',
9 },
13);

The Lambda function of choice will receive an authorization token from the client and execute the desired authorization logic. The AppSync GraphQL API will receive a payload from Lambda after invocation to allow or deny the API call accordingly.

To configure a Lambda function as the authorization mode, create a new file amplify/data/custom-authorizer.ts. You can use this Lambda function code template as a starting point for your authorization handler code:

1// amplify/data/custom-authorizer.ts
2
3// This is sample code. Update this to suite your needs
4import type { AppSyncAuthorizerHandler } from 'aws-lambda'; // types imported from @types/aws-lambda
5
6type ResolverContext = {
7 userid: string;
8 info: string;
9 more_info: string;
10};
11
12export const handler: AppSyncAuthorizerHandler<ResolverContext> = async (
13 event
14) => {
15 console.log(`EVENT: ${JSON.stringify(event)}`);
16 const {
17 authorizationToken,
18 requestContext: { apiId, accountId }
19 } = event;
20 const response = {
21 isAuthorized: authorizationToken === 'custom-authorized',
22 resolverContext: {
23 // eslint-disable-next-line spellcheck/spell-checker
24 userid: 'user-id',
25 info: 'contextual information A',
26 more_info: 'contextual information B'
27 },
28 deniedFields: [
29 `arn:aws:appsync:${process.env.AWS_REGION}:${accountId}:apis/${apiId}/types/Event/fields/comments`,
30 `Mutation.createEvent`
31 ],
32 ttlOverride: 300
33 };
34 console.log(`RESPONSE: ${JSON.stringify(response, null, 2)}`);
35 return response;
36};

You can use the template above as a starting point for your custom authorization rule. The authorization Lambda function receives the following event:

1{
2 "authorizationToken": "ExampleAuthToken123123123", # Authorization token specified by client
3 "requestContext": {
4 "apiId": "aaaaaa123123123example123", # AppSync API ID
5 "accountId": "111122223333", # AWS Account ID
6 "requestId": "f4081827-1111-4444-5555-5cf4695f339f",
7 "queryString": "mutation CreateEvent {...}\n\nquery MyQuery {...}\n", # GraphQL query
8 "operationName": "MyQuery", # GraphQL operation name
9 "variables": {} # any additional variables supplied to the operation
10 }
11}

Your Lambda authorization function needs to return the following JSON:

1{
2 // required
3 "isAuthorized": true, // if "false" then an UnauthorizedException is raised, access is denied
4 "resolverContext": { "banana": "very yellow" }, // JSON object visible as $ctx.identity.resolverContext in VTL resolver templates
5
6 // optional
7 "deniedFields": ["TypeName.FieldName"], // Forces the fields to "null" when returned to the client
8 "ttlOverride": 10 // The number of seconds that the response should be cached for. Overrides default specified in "amplify update api"
9}

Review the Amplify documentation to set the custom authorization token for the Data client.