Skip to content

Add Backend Configuration API #57

@posborne

Description

@posborne

Overview

Extract and enhance backend functionality as a public API for both high-level and low-level use cases.

Current State:

  • ✅ Basic backend resolution in fastly_compute.requests.backend (internal)
  • ✅ Static backend lookup via wit_backend.Backend.open()
  • ✅ Dynamic backend registration with TLS/timeout config
  • ❌ Limited to requests facade - not exposed publicly
  • ❌ Missing advanced features (client certs, custom CA, cipher selection, gRPC, pooling, keepalive)
  • ❌ Returns raw WIT objects instead of wrapped resources

This Issue:
Create public fastly_compute.backend module exposing:

  1. Backend wrapper - Pythonic wrapper around WIT Backend resource
  2. Advanced configuration - All WIT features (certs, ciphers, TLS versions, keepalive, pooling)
  3. Introspection API - Query backend properties (health, timeouts, TLS settings)
  4. Requests integration - Existing facade continues working unchanged

WIT Interface

interface backend {
  use types.{error, open-error};
  use http-types.{tls-version};
  use secret-store.{secret};

  resource dynamic-backend-options {
    constructor();
    override-host: func(value: string);
    connect-timeout: func(value: u32);
    first-byte-timeout: func(value: u32);
    between-bytes-timeout: func(value: u32);
    use-tls: func(value: bool);
    tls-min-version: func(value: tls-version);
    tls-max-version: func(value: tls-version);
    cert-hostname: func(value: string);
    ca-certificate: func(value: string);
    tls-ciphers: func(value: string);
    sni-hostname: func(value: string);
    client-cert: func(client-cert: string, key: borrow<secret>);
    http-keepalive-time-ms: func(value: u32);
    tcp-keepalive-enable: func(value: u32);
    tcp-keepalive-interval-secs: func(value: u32);
    tcp-keepalive-probes: func(value: u32);
    tcp-keepalive-time-secs: func(value: u32);
    max-connections: func(value: u32);
    max-use: func(value: u32);
    max-lifetime-ms: func(value: u32);
    pooling: func(value: bool);
    grpc: func(value: bool);
    prefer-ipv6: func(value: bool);
  }

  type timeout-ms = u32;
  type timeout-secs = u32;
  type probe-count = u32;

  enum backend-health {
    unknown,
    healthy,
    unhealthy,
  }

  resource backend {
    open: static func(name: string) -> result<backend, open-error>;
    get-name: func() -> string;
    is-healthy: func() -> result<backend-health, error>;
    is-dynamic: func() -> result<bool, error>;
    get-host: func(max-len: u64) -> result<string, error>;
    get-override-host: func(max-len: u64) -> result<option<list<u8>>, error>;
    get-port: func() -> result<u16, error>;
    get-connect-timeout-ms: func() -> result<timeout-ms, error>;
    get-first-byte-timeout-ms: func() -> result<timeout-ms, error>;
    get-between-bytes-timeout-ms: func() -> result<timeout-ms, error>;
    is-tls: func() -> result<bool, error>;
    get-tls-min-version: func() -> result<option<tls-version>, error>;
    get-tls-max-version: func() -> result<option<tls-version>, error>;
    get-http-keepalive-time: func() -> result<timeout-ms, error>;
    get-tcp-keepalive-enable: func() -> result<bool, error>;
    get-tcp-keepalive-interval: func() -> result<timeout-secs, error>;
    get-tcp-keepalive-probes: func() -> result<probe-count, error>;
    get-tcp-keepalive-time: func() -> result<timeout-secs, error>;
  }

  register-dynamic-backend: func(prefix: string, target: string, options: dynamic-backend-options) -> result<backend, error>;
}

WIT bindings: stubs/wit_world/imports/backend.py

API Design

Extract backend logic from internal fastly_compute.requests.backend module into public fastly_compute.backend:

  • Implement Backend resource wrapper around WIT Backend
  • Static backends: Backend.open(name)
  • Dynamic backends: Backend.create_dynamic(target, **options) for simple cases
  • Advanced config: Backend.register_dynamic(name, DynamicBackendConfig(...)) with dataclass for all WIT options (TLS, certs, timeouts, pooling, keepalive, gRPC)
  • Introspection properties: name, health, is_dynamic, host, port, connect_timeout (as timedelta), TLS settings
  • Use TLSVersion enum (V1_0, V1_1, V1_2, V1_3) and BackendHealth enum (UNKNOWN, HEALTHY, UNHEALTHY)
  • Existing requests facade continues working unchanged (uses new module internally)

Cross-SDK Comparison: Rust uses builder pattern for dynamic backends, Go uses simple factory functions with struct fields, JS uses constructor config object. Python should use DynamicBackendConfig dataclass (more Pythonic than builder).

Viceroy Testing

Viceroy supports backend configuration via viceroy.toml and dynamic backend registration. Tests can verify SDK wrapper logic (property conversion, enum mapping, error handling), but actual backend requests require mock server or real endpoints.

Reference

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions