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}