type-bridge-server 1.4.2

Query-intercepting proxy server for TypeDB with validation and audit logging
Documentation

type-bridge-server

Transport-agnostic query pipeline for TypeDB with composable interceptors.

Overview

type-bridge-server is both a library crate and a standalone binary that provides a structured query pipeline for TypeDB:

validate → intercept → compile → execute → intercept

The pipeline receives structured queries (parsed AST clauses), validates them against a loaded TypeQL schema, runs request interceptors, compiles to TypeQL, executes against TypeDB, then runs response interceptors.

Quick start

As a standalone server

cargo run -p type-bridge-server -- --config config.toml

As a library

use type_bridge_server::pipeline::PipelineBuilder;
use type_bridge_server::schema_source::InMemorySchemaSource;

let pipeline = PipelineBuilder::new(my_executor)
    .with_schema_source(InMemorySchemaSource::new(schema_tql))
    .with_default_database("my_db")
    .with_interceptor(my_audit_log)
    .build()?;

let output = pipeline.execute_query(input).await?;

Architecture

                    +-----------+
                    | Transport |   (Axum HTTP, or custom)
                    +-----+-----+
                          |
                    +-----v-----+
                    |  Pipeline  |
                    +-----+-----+
                          |
          +-------+-------+-------+-------+
          |       |       |       |       |
      Validate  Intercept Compile Execute Intercept
      (schema)  (request)  (AST→  (TypeDB) (response)
                           TypeQL)

Components:

Component Trait Built-in
Executor QueryExecutor TypeDBClient (feature: typedb)
Interceptor Interceptor AuditLogInterceptor
Schema source SchemaSource FileSchemaSource, InMemorySchemaSource
Transport N/A Axum HTTP (feature: axum-transport)

Configuration

The server reads a TOML config file:

[typedb]
address = "localhost:1729"
database = "my_database"
username = "admin"
password = "password"

[server]
host = "0.0.0.0"     # default
port = 3000           # default

[schema]
file = "schema.tql"   # optional: path to TypeQL schema

[interceptors]
enabled = ["audit-log"]

[audit_log]
output = "file"                 # "stdout" or "file"
file_path = "/var/log/audit.jsonl"

[logging]
level = "info"        # default
format = "text"       # "text" or "json"

HTTP API

All endpoints require Content-Type: application/json.

POST /query — execute structured query

{
  "database": "my_db",
  "transaction_type": "read",
  "clauses": [{ "match": [{ "entity": { "variable": "p", "type_name": "person" }}] }]
}

POST /query/raw — execute raw TypeQL

{
  "database": "my_db",
  "transaction_type": "read",
  "query": "match $p isa person;"
}

POST /query/validate — validate without executing

{
  "clauses": [{ "match": [{ "entity": { "variable": "p", "type_name": "person" }}] }]
}

GET /health — health check

Returns {"status": "ok", "connected": true}.

GET /schema — loaded schema

Returns the loaded TypeQL schema as JSON, or 500 if no schema is loaded.

Custom interceptors

Implement the Interceptor trait to add cross-cutting concerns:

use type_bridge_server::interceptor::{Interceptor, InterceptError, RequestContext};
use type_bridge_core_lib::ast::Clause;
use std::pin::Pin;
use std::future::Future;

struct RateLimiter { /* ... */ }

impl Interceptor for RateLimiter {
    fn name(&self) -> &str { "rate-limiter" }

    fn on_request<'a>(
        &'a self,
        clauses: Vec<Clause>,
        ctx: &'a mut RequestContext,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<Clause>, InterceptError>> + Send + 'a>> {
        Box::pin(async move {
            // Check rate limit, reject or pass through
            Ok(clauses)
        })
    }
}

Register via PipelineBuilder::with_interceptor().

Custom executors

Implement QueryExecutor for non-TypeDB backends or mocking:

use type_bridge_server::executor::QueryExecutor;
use type_bridge_server::error::PipelineError;
use std::pin::Pin;
use std::future::Future;

struct MyBackend;

impl QueryExecutor for MyBackend {
    fn execute<'a>(
        &'a self,
        database: &'a str,
        typeql: &'a str,
        transaction_type: &'a str,
    ) -> Pin<Box<dyn Future<Output = Result<serde_json::Value, PipelineError>> + Send + 'a>> {
        Box::pin(async move {
            Ok(serde_json::json!({"ok": true}))
        })
    }
    fn is_connected(&self) -> bool { true }
}

Feature flags

Feature Default Effect
typedb yes Enables TypeDBClient and typedb-driver dependency
axum-transport yes Enables HTTP server with Axum

Build as bare library (no transport, no TypeDB):

cargo check -p type-bridge-server --no-default-features

Testing

# Unit tests (no external dependencies)
cargo test -p type-bridge-server

# MC/DC coverage (requires nightly + cargo-llvm-cov)
./scripts/coverage.sh mcdc --open