Implementing Gateway Hooks

Grafbase Gateway provides hooks to extend the functionality of the gateway. Hooks are Rust functions that are called by the Grafbase Gateway engine at specific points in the request processing pipeline. Hooks can be used to implement custom logic, such as authorization, authentication, or access logs.

By compiling the hooks into WebAssembly components, you can extend the functionality of the gateway without modifying the gateway's source code. This allows you to implement custom logic in a safe and isolated environment without needing to compile the entire gateway and with your own custom licensing.

Hooks are implemented as Rust functions that are compiled into WebAssembly components. The WebAssembly components are loaded by the Grafbase Gateway engine and called at specific points in the request processing pipeline.

Get a template project from the Grafbase Gateway repository. Use this template as your starting point to implement hooks.

The project is structured as follows:

. ├── Cargo.lock ├── Cargo.toml ├── README.md ├── src └── lib.rs
  • Cargo.toml: The project manifest file that defines the project dependencies and configuration.
  • src/lib.rs: The main Rust file that implements the hooks.

To compile the hooks, install these tools:

  • Rust compiler
  • For Rust version 1.82 or earlier, cargo-component subcommand
  • For Rust version 1.83 or later, the wasm32-wasip2 target (rustup target add wasm32-wasip2)
  • An editor or IDE for writing Rust code (e.g., Visual Studio Code, Zed, Emacs, Vim or Helix)
  • rust-analyzer for code autocomplete and checks

Download the template project to your computer and specify a project name in the Cargo.toml file.

The Grafbase Hooks SDK crate grafbase-hooks defines the required types and traits. View the documentation here. Keep in mind that updating the Grafbase Gateway might require updating the SDK crate. The gateway changelog should always mention the minimum required SDK version.

Add the grafbase-hooks crate to your Cargo.toml file if you aren't using the template project:

cargo add grafbase-hooks

After installing the required tools, the project should build successfully.

On Rust 1.83 or later:

cargo build --target wasm32-wasip2

On Rust 1.82 or earlier:

cargo component build

This will generate the WebAssembly component file.

  • target/wasm32-wasip1/debug/<project name>.wasm with Rust 1.82 or earlier.
  • target/wasm32-wasip2/debug/<project name>.wasm with Rust 1.83 or later.

For this example, we will implement the gateway-request hook, which is called before the gateway processes the request. The gateway-request hook can be used to implement custom logic, such as authentication, authorization, or request modification.

The simplest possible hook implementation looks like this:

use grafbase_hooks::{grafbase_hooks, register_hooks, Context, ErrorResponse, Headers, Hooks}; struct MyHooks; #[grafbase_hooks] impl Hooks for MyHooks { fn new() -> Self where Self: Sized, { MyHooks } fn on_gateway_request(&mut self, context: Context, headers: Headers) -> Result<(), ErrorResponse> { Ok(()) } } register_hooks!(MyHooks);

We create a new struct for our hooks MyHooks and implement the Hooks trait to it. The only required method is new, which is called when a hook instance is initialized to the local pool. A running Grafbase Gateway instance can have multiple hook instances running in parallel. Any state stored in the hook instance is isolated from other instances.

You must add the grafbase_hooks attribute to the Hooks implementation, and register the hooks to the Gateway runtime with the register_hooks! macro.

The Context struct is passed to all hook methods and can be used to store state that is shared between hook methods.

This implementation does nothing and allows the engine to continue execution. You can add custom logic to the on_gateway_request function to implement your desired behavior. Let's add a simple check if a certain header value is present:

#[grafbase_hooks] impl Hooks for MyHooks { fn on_gateway_request(&mut self, context: Context, headers: Headers) -> Result<(), ErrorResponse> { match headers.get("x-secret-header") { Some(ref value) if value == "secret" => Ok(()), _ => { let error = Error { message: "Unauthorized".to_string(), extensions: Vec::new(), }; Err(ErrorResponse { status_code: 403, errors: vec![error], }) }, } } }

This implementation checks if the x-secret-header is set to secret. If the header is not present or the value is different, the hook returns a HTTP 403 error code.

To use the hooks in the Grafbase Gateway, they must first be compiled in release mode.

On Rust 1.83 or later:

cargo build --target wasm32-wasip2 --release

On Rust 1.82 or earlier:

cargo component build --release

The compiled hooks will be in target/wasm32-wasip1/release/<project name>.wasm with Rust 1.82 or earlier, and in target/wasm32-wasip2/release/<project name>.wasm with Rust 1.83 or later. Copy the file to a place where the Grafbase Gateway can access it, and configure the gateway to use the hooks.