Macro appsync_lambda_main

Source
appsync_lambda_main!() { /* proc-macro */ }
Expand description

Generates the code required to handle AWS AppSync Direct Lambda resolver events based on a GraphQL schema.

This macro takes a path to a GraphQL schema file and generates the complete foundation for implementing an AWS AppSync Direct Lambda resolver:

  • Rust types for all GraphQL types (enums, inputs, objects)
  • Query/Mutation/Subscription operation enums
  • AWS Lambda runtime setup with logging to handle the AWS AppSync event
  • Optional AWS SDK client initialization

§Schema Path Argument

The first argument to this macro must be a string literal containing the path to your GraphQL schema file. The schema path can be:

  • An absolute filesystem path (e.g. “/home/user/project/schema.graphql”)
  • A relative path, that will be relative to your crate’s root directory (e.g. “schema.graphql”, “graphql/schema.gql”)
  • When in a workspace context, the relative path will be relative to the workspace root directory

§Options

  • batch = bool: Enable/disable batch request handling (default: true)
  • hook = fn_name: Add a custom hook function for request validation/auth
  • exclude_lambda_handler = bool: Skip generation of Lambda handler code
  • only_lambda_handler = bool: Only generate Lambda handler code
  • exclude_appsync_types = bool: Skip generation of GraphQL type definitions
  • only_appsync_types = bool: Only generate GraphQL type definitions
  • exclude_appsync_operations = bool: Skip generation of operation enums
  • only_appsync_operations = bool: Only generate operation enums
  • type_override - see section below for details
  • name_override - see section below for details
  • field_type_override (Deprecated): Same as type_override

§Type Overrides

The type_override option allows overriding Rust types affected to various schema elements:

  • GraphQL type and input Field types: type_override = Type.field: CustomType
  • Operation return types (Query/Mutation): type_override = OpType.operation: CustomType
  • Operation arguments (Query/Mutation/Subscription): type_override = OpType.operation.arg: CustomType

These overrides are only for the Rust code and must be compatible for serialization/deserialization purposes, i.e. you can use String for a GraphQL ID but you cannot use a u32 for a GraphQL Float.

§Name Overrides

The name_override option supports renaming various schema elements:

  • Type/input/enum names: name_override = TypeName: NewTypeName
  • Field names: name_override = Type.field: new_field_name
  • Enum variants: name_override = Enum.VARIANT: NewVariant

These overrides are only for the Rust code and will not change serialization/deserialization, i.e. serde will rename to the original GraphQL schema name.

§AWS SDK Clients

AWS SDK clients can be initialized by providing function definitions that return a cached SDK client type. Each client is initialized only once and stored in a static OnceLock, making subsequent function calls essentially free:

  • Function name: Any valid Rust identifier that will be used to access the client
  • Return type: Must be a valid AWS SDK client like aws_sdk_dynamodb::Client
use lambda_appsync::appsync_lambda_main;

// Single client
appsync_lambda_main!(
    "schema.graphql",
    dynamodb() -> aws_sdk_dynamodb::Client,
);
// Multiple clients
appsync_lambda_main!(
    "schema.graphql",
    dynamodb() -> aws_sdk_dynamodb::Client,
    s3() -> aws_sdk_s3::Client,
);

These client functions can then be called from anywhere in the Lambda crate:

use crate::{dynamodb, s3};
async fn do_something() {
    let dynamodb_client = dynamodb();
    let s3_client = s3();
    // Use clients...
}

§Examples

§Basic usage with authentication hook:

use lambda_appsync::{appsync_lambda_main, AppsyncEvent, AppsyncResponse, AppsyncIdentity};

fn is_authorized(identity: &AppsyncIdentity) -> bool {
    todo!()
}

// If the function returns Some(AppsyncResponse), the Lambda function will immediately return it.
// Otherwise, the normal flow of the AppSync operation processing will continue.
// This is primarily intended for advanced authentication checks that AppSync cannot perform, such as verifying that a user is requesting their own ID.
async fn auth_hook(
    event: &lambda_appsync::AppsyncEvent<Operation>
) -> Option<lambda_appsync::AppsyncResponse> {
    // Verify JWT token, check permissions etc
    if !is_authorized(&event.identity) {
        return Some(AppsyncResponse::unauthorized());
    }
    None
}

appsync_lambda_main!(
    "schema.graphql",
    hook = auth_hook,
    dynamodb() -> aws_sdk_dynamodb::Client
);

§Generate only types for lib code generation:

use lambda_appsync::appsync_lambda_main;
appsync_lambda_main!(
    "schema.graphql",
    only_appsync_types = true
);

§Override field types, operation return type or argument types:

use lambda_appsync::appsync_lambda_main;
appsync_lambda_main!(
    "schema.graphql",
    // Use String instead of the default lambda_appsync::ID
    // Override Player.id to use String instead of ID
    type_override = Player.id: String,
    // Multiple overrides, here changing another `Player` field type
    type_override = Player.team: String,
    // Return value override
    type_override = Query.gameStatus: String,
    type_override = Mutation.setGameStatus: String,
    // Argument override
    type_override = Query.player.id: String,
    type_override = Mutation.deletePlayer.id: String,
    type_override = Subscription.onDeletePlayer.id: String,
);

§Override type, input, enum, fields or variants names:

use lambda_appsync::appsync_lambda_main;
appsync_lambda_main!(
    "schema.graphql",
    // Override Player struct name
    name_override = Player: NewPlayer,
    // Override Player struct field name
    name_override = Player.name: email,
    // Override team `PYTHON` to be `Snake` (instead of `Python`)
    name_override = Team.PYTHON: Snake,
    name_override = WeirdFieldNames.await: no_await,
    name_override = WeirdFieldNames.crate: no_crate,
    name_override = WeirdFieldNames.u8: no_u8,
    // MUST also override ALL the operations return type !!!
    type_override = Query.players: NewPlayer,
    type_override = Query.player: NewPlayer,
    type_override = Mutation.createPlayer: NewPlayer,
    type_override = Mutation.deletePlayer: NewPlayer,
);

Note that when using name_override, the macro does not automatically change the case: you are responsible to provide the appropriate casing or Clippy will complain.

§Disable batch processing:

lambda_appsync::appsync_lambda_main!(
    "schema.graphql",
    batch = false
);