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}