lambda_appsync_proc/
lib.rs

1#![warn(missing_docs)]
2#![warn(rustdoc::missing_crate_level_docs)]
3//! This crate provides procedural macros for implementing AWS AppSync Direct Lambda resolvers.
4//!
5//! It helps convert GraphQL schemas into type-safe Rust code with full AWS Lambda runtime support.
6//! The main functionality is provided through the `appsync_lambda_main` and `appsync_operation` macros.
7//!
8//! # Complete Example
9//!
10//! ```ignore
11//! use lambda_appsync::{appsync_lambda_main, appsync_operation, AppsyncError, ID};
12//!
13//! // 1. First define your GraphQL schema (e.g. `schema.graphql`):
14//! //
15//! // type Query {
16//! //   players: [Player!]!
17//! //   gameStatus: GameStatus!
18//! // }
19//! //
20//! // type Player {
21//! //   id: ID!
22//! //   name: String!
23//! //   team: Team!
24//! // }
25//! //
26//! // enum Team {
27//! //   RUST
28//! //   PYTHON
29//! //   JS
30//! // }
31//! //
32//! // enum GameStatus {
33//! //   STARTED
34//! //   STOPPED
35//! // }
36//!
37//! // 2. Initialize the Lambda runtime with AWS SDK clients in main.rs:
38//!
39//! // Optional hook for custom request validation/auth
40//! async fn verify_request(
41//!     event: &lambda_appsync::AppsyncEvent<Operation>
42//! ) -> Option<lambda_appsync::AppsyncResponse> {
43//!     // Return Some(response) to short-circuit normal execution
44//!     None
45//! }
46//!
47//! // Generate types and runtime setup from schema
48//! appsync_lambda_main!(
49//!     "schema.graphql",
50//!     // Initialize DynamoDB client if needed
51//!     dynamodb() -> aws_sdk_dynamodb::Client,
52//!     // Enable validation hook
53//!     hook = verify_request,
54//!     // Enable batch processing
55//!     batch = true
56//! );
57//!
58//! // 3. Implement resolver functions for GraphQL operations:
59//!
60//! #[appsync_operation(query(players))]
61//! async fn get_players() -> Result<Vec<Player>, AppsyncError> {
62//!     let client = dynamodb();
63//!     todo!()
64//! }
65//!
66//! #[appsync_operation(query(gameStatus))]
67//! async fn get_game_status() -> Result<GameStatus, AppsyncError> {
68//!     let client = dynamodb();
69//!     todo!()
70//! }
71//!
72//! // The macro ensures the function signature matches the GraphQL schema
73//! // and wires everything up to handle AWS AppSync requests automatically
74//! ```
75
76mod appsync_lambda_main;
77mod appsync_operation;
78mod common;
79
80use proc_macro::TokenStream;
81
82/// Generates the code required to handle AWS AppSync Direct Lambda resolver events based on a GraphQL schema.
83///
84/// This macro takes a path to a GraphQL schema file and generates the complete foundation
85/// for implementing an AWS AppSync Direct Lambda resolver:
86///
87/// - Rust types for all GraphQL types (enums, inputs, objects)
88/// - Query/Mutation/Subscription operation enums
89/// - AWS Lambda runtime setup with logging to handle the AWS AppSync event
90/// - Optional AWS SDK client initialization
91///
92/// # Example Usage
93///
94/// ```ignore
95/// use lambda_appsync::appsync_lambda_main;
96///
97/// // If the function returns Some(AppsyncResponse), the Lambda function will immediatly return it
98/// // Else, the normal flow of the AppSync operation processing will continue
99/// // This is primarily intended for advanced authentication checks that AppSync cannot perform,
100/// // such as verifying that a user is requesting their own ID for example.
101/// async fn before_request(
102///     event: &lambda_appsync::AppsyncEvent<Operation>
103/// ) -> Option<lambda_appsync::AppsyncResponse> {
104///     todo!()
105/// }
106///
107/// appsync_lambda_main!(
108///     // Path to GraphQL schema file
109///     "path/to/schema.gql",
110///
111///     // Optional AWS SDK clients
112///     dynamodb() -> aws_sdk_dynamodb::Client,
113///     s3() -> aws_sdk_s3::Client,
114///
115///     // Options
116///     batch = true,  // Enable batch request handling
117///     hook = before_request, // Add custom request hook, typically used for authentication
118///     field_type_override = MyType.field: CustomType // Override generated field types
119/// );
120/// ```
121#[proc_macro]
122pub fn appsync_lambda_main(input: TokenStream) -> TokenStream {
123    appsync_lambda_main::appsync_lambda_main_impl(input)
124}
125
126/// Marks an async function as an AWS AppSync resolver operation, binding it to a specific Query,
127/// Mutation or Subscription operation defined in the GraphQL schema.
128///
129/// The marked function must match the signature of the GraphQL operation, with parameters and return
130/// type matching what is defined in the schema. The function will be wired up to handle requests
131/// for that operation through the AWS AppSync Direct Lambda resolver.
132///
133/// # Important
134/// This macro can only be used in a crate where the [appsync_lambda_main!] macro has been used at the
135/// root level (typically in `main.rs`). The code generated by this macro depends on types and
136/// implementations that are created by [appsync_lambda_main!].
137///
138/// # Example Usage
139///
140/// ```ignore
141/// use lambda_appsync::{appsync_operation, AppsyncError, ID};
142///
143/// // Execute when a 'getUser' query is received
144/// #[appsync_operation(query(getUser))]
145/// async fn get_user(id: ID) -> Result<User, AppsyncError> {
146///     // Implement resolver logic
147///     Ok(dynamodb_get_user(id).await?)
148/// }
149///
150/// // Handle a 'createUser' mutation
151/// #[appsync_operation(mutation(createUser))]
152/// async fn create_user(name: String, email: String) -> Result<User, AppsyncError> {
153///     Ok(dynamodb_create_user(name, email).await?)
154/// }
155///
156/// // Keep the original function name available separately
157/// #[appsync_operation(query(getUser), keep_original_function_name)]
158/// async fn fetch_user(id: ID) -> Result<User, AppsyncError> {
159///     // Can still call fetch_user() directly
160///     Ok(dynamodb_get_user(id).await?)
161/// }
162/// ```
163///
164/// The macro will ensure the function signature matches what is defined in the GraphQL schema,
165/// and wire it up to be called when AWS AppSync invokes the Lambda resolver for that operation.
166#[proc_macro_attribute]
167pub fn appsync_operation(args: TokenStream, input: TokenStream) -> TokenStream {
168    appsync_operation::appsync_operation_impl(args, input)
169}