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};
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/// # Options
93///
94/// - `batch = bool`: Enable/disable batch request handling (default: true)
95/// - `hook = fn_name`: Add a custom hook function for request validation/auth
96/// - `exclude_lambda_handler = bool`: Skip generation of Lambda handler code
97/// - `only_lambda_handler = bool`: Only generate Lambda handler code
98/// - `exclude_appsync_types = bool`: Skip generation of GraphQL type definitions
99/// - `only_appsync_types = bool`: Only generate GraphQL type definitions
100/// - `exclude_appsync_operations = bool`: Skip generation of operation enums
101/// - `only_appsync_operations = bool`: Only generate operation enums
102/// - `field_type_override = Type.field: CustomType`: Override type of a specific field
103///
104/// # Examples
105///
106/// Basic usage with authentication hook:
107/// ```no_run
108/// use lambda_appsync::{appsync_lambda_main, AppsyncEvent, AppsyncResponse, AppsyncIdentity};
109///
110///  fn is_authorized(identity: Option<&AppsyncIdentity>) -> bool {
111///     todo!()
112/// }
113///
114/// // If the function returns Some(AppsyncResponse), the Lambda function will immediatly return it
115/// // Else, the normal flow of the AppSync operation processing will continue
116/// // This is primarily intended for advanced authentication checks that AppSync cannot perform,
117/// // such as verifying that a user is requesting their own ID for example.
118/// async fn auth_hook(
119///     event: &lambda_appsync::AppsyncEvent<Operation>
120/// ) -> Option<lambda_appsync::AppsyncResponse> {
121///     // Verify JWT token, check permissions etc
122///     if !is_authorized(event.identity.as_ref()) {
123///         return Some(AppsyncResponse::unauthorized());
124///     }
125///     None
126/// }
127///
128/// appsync_lambda_main!(
129///     "schema.graphql",
130///     hook = auth_hook,
131///     dynamodb() -> aws_sdk_dynamodb::Client
132/// );
133/// ```
134///
135/// Generate only types for lib code generation:
136/// ```no_run
137/// use lambda_appsync::appsync_lambda_main;
138/// appsync_lambda_main!(
139///     "schema.graphql",
140///     only_appsync_types = true
141/// );
142/// ```
143///
144/// Override field types and use multiple AWS clients:
145/// ```no_run
146/// use lambda_appsync::appsync_lambda_main;
147/// appsync_lambda_main!(
148///     "schema.graphql",
149///     dynamodb() -> aws_sdk_dynamodb::Client,
150///     s3() -> aws_sdk_s3::Client,
151///     // Use String instead of the default lambda_appsync::ID
152///     field_type_override = Player.id: String,
153/// );
154/// ```
155///
156/// Disable batch processing:
157/// ```no_run
158/// lambda_appsync::appsync_lambda_main!(
159///     "schema.graphql",
160///     batch = false
161/// );
162/// ```
163#[proc_macro]
164pub fn appsync_lambda_main(input: TokenStream) -> TokenStream {
165    appsync_lambda_main::appsync_lambda_main_impl(input)
166}
167
168/// Marks an async function as an AWS AppSync resolver operation, binding it to a specific Query,
169/// Mutation or Subscription operation defined in the GraphQL schema.
170///
171/// The marked function must match the signature of the GraphQL operation, with parameters and return
172/// type matching what is defined in the schema. The function will be wired up to handle requests
173/// for that operation through the AWS AppSync Direct Lambda resolver.
174///
175/// # Important
176/// This macro can only be used in a crate where the [appsync_lambda_main!] macro has been used at the
177/// root level (typically in `main.rs`). The code generated by this macro depends on types and
178/// implementations that are created by [appsync_lambda_main!].
179///
180/// # Example Usage
181///
182/// ```ignore
183/// use lambda_appsync::{appsync_operation, AppsyncError, ID};
184///
185/// // Execute when a 'getUser' query is received
186/// #[appsync_operation(query(getUser))]
187/// async fn get_user(id: ID) -> Result<User, AppsyncError> {
188///     // Implement resolver logic
189///     Ok(dynamodb_get_user(id).await?)
190/// }
191///
192/// // Handle a 'createUser' mutation
193/// #[appsync_operation(mutation(createUser))]
194/// async fn create_user(name: String, email: String) -> Result<User, AppsyncError> {
195///     Ok(dynamodb_create_user(name, email).await?)
196/// }
197///
198/// // Keep the original function name available separately
199/// #[appsync_operation(query(getUser), keep_original_function_name)]
200/// async fn fetch_user(id: ID) -> Result<User, AppsyncError> {
201///     // Can still call fetch_user() directly
202///     Ok(dynamodb_get_user(id).await?)
203/// }
204/// ```
205///
206/// The macro will ensure the function signature matches what is defined in the GraphQL schema,
207/// and wire it up to be called when AWS AppSync invokes the Lambda resolver for that operation.
208#[proc_macro_attribute]
209pub fn appsync_operation(args: TokenStream, input: TokenStream) -> TokenStream {
210    appsync_operation::appsync_operation_impl(args, input)
211}