Attribute Macro appsync_operation

Source
#[appsync_operation]
Expand description

Marks an async function as an AWS AppSync resolver operation, binding it to a specific Query, Mutation or Subscription operation defined in the GraphQL schema.

The marked function must match the signature of the GraphQL operation, with parameters and return type matching what is defined in the schema. The function will be wired up to handle requests for that operation through the AWS AppSync Direct Lambda resolver.

§Important

This macro can only be used in a crate where the appsync_lambda_main! macro has been used at the root level (typically in main.rs). The code generated by this macro depends on types and implementations that are created by appsync_lambda_main!.

§Example Usage

use lambda_appsync::{appsync_operation, AppsyncError};

// Your types are declared at the crate level by the appsync_lambda_main! macro
use crate::Player;

// Execute when a 'players' query is received
#[appsync_operation(query(players))]
async fn get_players() -> Result<Vec<Player>, AppsyncError> {
    // Implement resolver logic
    Ok(dynamodb_get_players().await?)
}

// Handle a 'createPlayer' mutation
#[appsync_operation(mutation(createPlayer))]
async fn create_player(name: String) -> Result<Player, AppsyncError> {
    Ok(dynamodb_create_player(name).await?)
}

§Using the AppSync event

You may need to explore the AppsyncEvent received by the lambda in your operation handler. You can make it available by adding the with_appsync_event flag and adding a reference to it in your operation handler signature (must be the last argument), like so:

use lambda_appsync::{appsync_operation, AppsyncError, AppsyncEvent, AppsyncIdentity};

// Your types are declared at the crate level by the appsync_lambda_main! macro
use crate::{Operation, Player};

// Use the AppsyncEvent
#[appsync_operation(mutation(createPlayer), with_appsync_event)]
async fn create_player(name: String, event: &AppsyncEvent<Operation>) -> Result<Player, AppsyncError> {
    // Example: extract the cognito user ID
    let user_id = if let AppsyncIdentity::Cognito(cognito_id) = &event.identity {
        cognito_id.sub.clone()
    } else {
        return Err(AppsyncError::new("Unauthorized", "Must be Cognito authenticated"))
    };
    Ok(dynamodb_create_player(name).await?)
}

Note that the args field of the AppsyncEvent will always contain Null at this stage because its initial content is taken to extract the argument values for the operation.

§Preserve original function name

By default the appsync_operation macro will discard your function’s name but you can also keep it available by adding the keep_original_function_name flag:

use lambda_appsync::{appsync_operation, AppsyncError};

// Your types are declared at the crate level by the appsync_lambda_main! macro
use crate::Player;

// Keep the original function name available separately
#[appsync_operation(query(players), keep_original_function_name)]
async fn fetch_players() -> Result<Vec<Player>, AppsyncError> {
    Ok(dynamodb_get_players().await?)
}
async fn other_stuff() {
    // Can still call fetch_players() directly
    fetch_players().await;
}

§Using enhanced subscription filters

// (Optional) Use an enhanced subscription filter for onCreatePlayer
use lambda_appsync::{appsync_operation, AppsyncError};
use lambda_appsync::subscription_filters::{FilterGroup, Filter, FieldPath};

#[appsync_operation(subscription(onCreatePlayer))]
async fn on_create_player(name: String) -> Result<Option<FilterGroup>, AppsyncError> {
    Ok(Some(FilterGroup::from([
        Filter::from([
            FieldPath::new("name")?.contains(name)
        ])
    ])))
}

When using a single FieldPath you can turn it directly into a FilterGroup. The following code is equivalent to the one above:

#[appsync_operation(subscription(onCreatePlayer))]
async fn on_create_player(name: String) -> Result<Option<FilterGroup>, AppsyncError> {
    Ok(Some(FieldPath::new("name")?.contains(name).into()))
}

§Important Note

When using enhanced subscription filters (i.e., returning a FilterGroup from Subscribe operation handlers), you need to modify your Response mapping in AWS AppSync. It must contain the following:

#if($context.result.data)
$extensions.setSubscriptionFilter($context.result.data)
#end
null