grafbase_sdk/extension/
hooks.rs

1use crate::{
2    component::AnyExtension,
3    host_io::{
4        event_queue::EventQueue,
5        http::{Method, StatusCode},
6    },
7    types::{Configuration, Error, ErrorResponse, GatewayHeaders},
8};
9
10/// The Hooks extension allows you to hook into an incoming request or an outgoing response.
11///
12/// You have mutable access to the headers, and information about the request or response
13/// to decide whether to continue processing or not.
14///
15/// Keep in mind this is not meant for authentication purposes.
16///
17/// # Example
18///
19/// ```rust
20/// use grafbase_sdk::{
21///     HooksExtension,
22///     types::{GatewayHeaders, Configuration, Error, ErrorResponse},
23///     host_io::event_queue::EventQueue,
24/// };
25///
26/// #[derive(HooksExtension)]
27/// struct MyHooks {
28///     config: Config,
29/// }
30///
31/// #[derive(serde::Deserialize)]
32/// struct Config {
33///     // Define your configuration fields here. They are parsed from
34///     // the grafbase.toml configuration.
35///     something: String,
36/// }
37///
38/// impl HooksExtension for MyHooks {
39///     fn new(config: Configuration) -> Result<Self, Error> {
40///         let config = config.deserialize()?;
41///         Ok(Self { config })
42///     }
43///
44///     fn on_request(&mut self, url: &str, method: http::Method, headers: &mut GatewayHeaders) -> Result<(), ErrorResponse> {
45///         // Implement your request hook logic here.
46///         Ok(())
47///     }
48///
49///     fn on_response(
50///         &mut self,
51///         status: http::StatusCode,
52///         headers: &mut GatewayHeaders,
53///         event_queue: EventQueue,
54///     ) -> Result<(), String> {
55///         // Implement your response hook logic here.
56///         Ok(())
57///     }
58/// }
59/// ```
60#[allow(unused_variables)]
61pub trait HooksExtension: Sized + 'static {
62    /// Creates a new instance of the extension. The [`Configuration`] will contain all the
63    /// configuration defined in the `grafbase.toml` by the extension user in a serialized format.
64    ///
65    /// # Example
66    ///
67    /// The following TOML configuration:
68    /// ```toml
69    /// [extensions.my-hooks.config]
70    /// my_custom_key = "value"
71    /// ```
72    ///
73    /// can be easily deserialized with:
74    ///
75    /// ```rust
76    /// # use grafbase_sdk::types::{Configuration, Error};
77    /// # fn dummy(config: Configuration) -> Result<(), Error> {
78    /// #[derive(serde::Deserialize)]
79    /// struct Config {
80    ///     my_custom_key: String
81    /// }
82    ///
83    /// let config: Config = config.deserialize()?;
84    /// # Ok(())
85    /// # }
86    /// ```
87    fn new(config: Configuration) -> Result<Self, Error>;
88
89    /// Called immediately when a request is received, before entering the GraphQL engine.
90    ///
91    /// This hook can be used to modify the request headers before they are processed by the GraphQL engine, and provides a way to audit the headers, URL, and method before processing the operation.
92    fn on_request(
93        &mut self,
94        url: &str,
95        method: http::Method,
96        headers: &mut GatewayHeaders,
97    ) -> Result<(), ErrorResponse>;
98
99    /// Called right before the response is sent back to the client.
100    ///
101    /// This hook can be used to modify the response headers before the response is sent back to the client.
102    fn on_response(
103        &mut self,
104        status: http::StatusCode,
105        headers: &mut GatewayHeaders,
106        event_queue: EventQueue,
107    ) -> Result<(), String>;
108}
109
110#[doc(hidden)]
111pub fn register<T: HooksExtension>() {
112    pub(super) struct Proxy<T: HooksExtension>(T);
113
114    impl<T: HooksExtension> AnyExtension for Proxy<T> {
115        fn on_request(&mut self, url: &str, method: Method, headers: &mut GatewayHeaders) -> Result<(), ErrorResponse> {
116            HooksExtension::on_request(&mut self.0, url, method, headers)
117        }
118
119        fn on_response(
120            &mut self,
121            status: StatusCode,
122            headers: &mut GatewayHeaders,
123            event_queue: EventQueue,
124        ) -> Result<(), String> {
125            HooksExtension::on_response(&mut self.0, status, headers, event_queue)
126        }
127    }
128
129    crate::component::register_extension(Box::new(|_, config| {
130        <T as HooksExtension>::new(config).map(|extension| Box::new(Proxy(extension)) as Box<dyn AnyExtension>)
131    }))
132}