> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/trailbaseio/trailbase/llms.txt
> Use this file to discover all available pages before exploring further.

# Creating WASM Components

> Build WebAssembly components using the Component Model

## Component Model

TrailBase uses the [WebAssembly Component Model](https://component-model.bytecodealliance.org/), which provides:

* **Language interoperability**: Components written in different languages can work together
* **Interface types**: Rich type system with records, variants, and resources
* **Composition**: Components can import and export interfaces
* **Versioning**: Built-in support for API versioning

## WIT (WebAssembly Interface Types)

Components are defined using WIT files that specify their interfaces. TrailBase provides the `trailbase:component` package:

```wit theme={null}
package trailbase:component@0.1.0;

interface init-endpoint {
  record arguments {
    version: option<string>,
  }

  enum http-method-type {
    get, post, head, options, patch, delete, put, trace, connect,
  }

  record http-handlers {
    handlers: list<tuple<http-method-type, string>>,
  }

  init-http-handlers: func(args: arguments) -> http-handlers;

  record job-handlers {
    handlers: list<tuple<string, string>>,
  }

  init-job-handlers: func(args: arguments) -> job-handlers;
}
```

## Creating a Rust Component

<Steps>
  <Step title="Set up your project">
    Create a new library crate:

    ```bash theme={null}
    cargo new --lib my-component
    cd my-component
    ```

    Add dependencies to `Cargo.toml`:

    ```toml theme={null}
    [lib]
    crate-type = ["cdylib"]

    [dependencies]
    trailbase-wasm = "0.1"
    ```
  </Step>

  <Step title="Implement the Guest trait">
    Create your component in `src/lib.rs`:

    ```rust theme={null}
    use trailbase_wasm::http::{HttpRoute, Request, routing};
    use trailbase_wasm::{Guest, export};

    struct MyComponent;

    impl Guest for MyComponent {
        fn http_handlers() -> Vec<HttpRoute> {
            vec![
                routing::get("/hello", hello_handler),
                routing::post("/data", data_handler),
            ]
        }
    }

    async fn hello_handler(_req: Request) -> String {
        "Hello from WASM!".to_string()
    }

    async fn data_handler(mut req: Request) -> Result<String, HttpError> {
        let body = req.body().bytes().await?;
        Ok(format!("Received {} bytes", body.len()))
    }

    export!(MyComponent);
    ```
  </Step>

  <Step title="Build the component">
    Compile to WASM:

    ```bash theme={null}
    cargo build --target wasm32-wasip2 --release
    ```

    Convert to a component:

    ```bash theme={null}
    wasm-tools component new \
      target/wasm32-wasip2/release/my_component.wasm \
      -o my_component.wasm
    ```
  </Step>

  <Step title="Deploy to TrailBase">
    Copy the component to your TrailBase data directory:

    ```bash theme={null}
    cp my_component.wasm /path/to/traildepot/components/
    ```

    TrailBase will automatically load it on startup or restart.
  </Step>
</Steps>

## Creating a TypeScript Component

<Steps>
  <Step title="Set up your project">
    Create a new project:

    ```bash theme={null}
    npm init -y
    npm install trailbase-wasm
    ```

    Configure `package.json`:

    ```json theme={null}
    {
      "type": "module",
      "scripts": {
        "build": "node build.js"
      }
    }
    ```
  </Step>

  <Step title="Write your component">
    Create `src/index.ts`:

    ```typescript theme={null}
    import { defineConfig } from "trailbase-wasm";
    import { HttpHandler } from "trailbase-wasm/http";
    import { JobHandler } from "trailbase-wasm/job";
    import { query } from "trailbase-wasm/db";

    export default defineConfig({
      httpHandlers: [
        HttpHandler.get("/fibonacci", (req) => {
          const n = parseInt(req.getQueryParam("n") || "10");
          return fibonacci(n).toString();
        }),
        
        HttpHandler.get("/users", async (req) => {
          const rows = await query(
            'SELECT * FROM users LIMIT 10',
            []
          );
          return HttpResponse.json(rows);
        }),
      ],
      
      jobHandlers: [
        JobHandler.hourly("cleanup", async () => {
          console.log("Running cleanup job");
          await query('DELETE FROM logs WHERE created < $1', [
            Date.now() - 86400000
          ]);
        }),
      ],
    });

    function fibonacci(num: number): number {
      return num <= 1 ? num : fibonacci(num - 1) + fibonacci(num - 2);
    }
    ```
  </Step>

  <Step title="Build configuration">
    Create `build.js` to compile TypeScript to WASM:

    ```javascript theme={null}
    import { build } from 'esbuild';
    import { exec } from 'child_process';
    import { promisify } from 'util';

    const execAsync = promisify(exec);

    // Build TypeScript
    await build({
      entryPoints: ['src/index.ts'],
      bundle: true,
      format: 'esm',
      target: 'esnext',
      outfile: 'dist/index.js',
    });

    // Compile to WASM using wasm-tools
    await execAsync('wasm-tools component new dist/index.wasm -o component.wasm');
    ```
  </Step>

  <Step title="Build and deploy">
    ```bash theme={null}
    npm run build
    cp component.wasm /path/to/traildepot/components/
    ```
  </Step>
</Steps>

## Creating a JavaScript Component

<Info>
  JavaScript components use the same approach as TypeScript but without type annotations.
</Info>

```javascript theme={null}
import { defineConfig } from "trailbase-wasm";
import { HttpHandler } from "trailbase-wasm/http";
import { query } from "trailbase-wasm/db";

export default defineConfig({
  httpHandlers: [
    HttpHandler.get("/hello", (req) => {
      const name = req.getQueryParam("name") || "World";
      return `Hello, ${name}!`;
    }),
    
    HttpHandler.post("/data", async (req) => {
      const data = req.json();
      await query(
        'INSERT INTO events (data) VALUES ($1)',
        [JSON.stringify(data)]
      );
      return HttpResponse.json({ success: true });
    }),
  ],
});
```

## Component Initialization

Components implement the `Guest` trait with three optional methods:

```rust theme={null}
pub trait Guest {
    /// Called once when the component is loaded
    fn init(_args: Args) {}

    /// Register HTTP request handlers
    fn http_handlers() -> Vec<HttpRoute> {
        vec![]
    }

    /// Register scheduled job handlers
    fn job_handlers() -> Vec<Job> {
        vec![]
    }

    /// Register custom SQLite scalar functions
    fn sqlite_scalar_functions() -> Vec<SqliteFunction> {
        vec![]
    }
}
```

## Request Handling

### HTTP Routes

Define routes with path parameters:

```rust theme={null}
routing::get("/users/{id}", async |req| {
    let id = req.path_param("id")
        .ok_or_else(|| HttpError::status(StatusCode::BAD_REQUEST))?;
    
    let rows = query(
        "SELECT * FROM users WHERE id = $1",
        [Value::Text(id.to_string())]
    ).await?;
    
    Ok(Json(rows))
})
```

### Query Parameters

```rust theme={null}
async fn search_handler(req: Request) -> Result<Json<Vec<User>>, HttpError> {
    #[derive(Deserialize)]
    struct Query {
        q: String,
        limit: Option<i64>,
    }
    
    let query: Query = req.query_parse()?;
    
    let rows = query(
        "SELECT * FROM users WHERE name LIKE $1 LIMIT $2",
        [Value::Text(format!("%{}%", query.q)), Value::Integer(query.limit.unwrap_or(10))]
    ).await?;
    
    Ok(Json(rows))
}
```

### Response Types

Multiple response types are supported:

```rust theme={null}
// Plain text
async fn text_handler(_req: Request) -> String {
    "Hello!".to_string()
}

// JSON
use trailbase_wasm::http::Json;

async fn json_handler(_req: Request) -> Json<MyData> {
    Json(MyData { value: 42 })
}

// HTML
use trailbase_wasm::http::Html;

async fn html_handler(_req: Request) -> Html<String> {
    Html("<h1>Hello</h1>".to_string())
}

// Redirect
use trailbase_wasm::http::Redirect;

async fn redirect_handler(_req: Request) -> Redirect {
    Redirect::to("/new-location")
}

// Custom Response
use trailbase_wasm::http::Response;

async fn custom_handler(_req: Request) -> Response {
    Response::builder()
        .status(StatusCode::CREATED)
        .header("X-Custom", "value")
        .body("Created".into_body())
        .unwrap()
}
```

## Error Handling

```rust theme={null}
use trailbase_wasm::http::{HttpError, StatusCode};

async fn handler(req: Request) -> Result<String, HttpError> {
    let id = req.path_param("id")
        .ok_or_else(|| HttpError::status(StatusCode::BAD_REQUEST))?;
    
    let rows = query("SELECT * FROM users WHERE id = $1", [Value::Text(id.to_string())])
        .await
        .map_err(|err| HttpError::message(
            StatusCode::INTERNAL_SERVER_ERROR,
            err.to_string()
        ))?;
    
    if rows.is_empty() {
        return Err(HttpError::status(StatusCode::NOT_FOUND));
    }
    
    Ok(format!("Found user: {}", id))
}
```

## Hot Reloading

<Tip>
  In development mode, TrailBase can watch for component changes and reload them automatically.
</Tip>

Create a watcher script:

```typescript theme={null}
// hot-reload.ts
import { watch } from 'fs';
import { exec } from 'child_process';

watch('src', { recursive: true }, (eventType, filename) => {
  console.log(`File changed: ${filename}`);
  exec('npm run build', (err, stdout, stderr) => {
    if (err) {
      console.error(stderr);
    } else {
      console.log('Rebuilt successfully');
    }
  });
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Custom Endpoints" href="/advanced/custom-endpoints" icon="route">
    Build HTTP endpoints with WASM
  </Card>

  <Card title="Server-Side Rendering" href="/advanced/server-side-rendering" icon="server">
    Render HTML dynamically
  </Card>

  <Card title="Jobs Scheduler" href="/advanced/jobs-scheduler" icon="clock">
    Create scheduled tasks
  </Card>

  <Card title="WASM Overview" href="/advanced/wasm-overview" icon="cube">
    Learn about the runtime
  </Card>
</CardGroup>
