Skip to content

Support for path prefixes to mount multiple OpenAPI specs on a single port #96

@mateof

Description

@mateof

Support for path prefixes to mount multiple OpenAPI specs on a single port

Problem

Currently, each erf:start_link/1 call creates an independent HTTP server bound to a specific port. This means that if you have multiple OpenAPI specifications, you must run each one on a separate port:

%% API 1 on port 8081
erf:start_link(#{
    name => users_api,
    port => 8081,
    spec_path => <<"users.openapi.json">>,
    ...
}).

%% API 2 on port 8082
erf:start_link(#{
    name => products_api,
    port => 8082,
    spec_path => <<"products.openapi.json">>,
    ...
}).

This creates operational overhead and complicates deployments, especially in containerized environments where exposing multiple ports requires additional configuration.

Use Case

Organizations often have multiple bounded contexts or microservices that they want to expose through a single HTTP endpoint with path-based routing:

Path API
https://api.example.com/users/... Users API
https://api.example.com/products/... Products API
https://api.example.com/orders/... Orders API

Proposed Solution

Add a path_prefix configuration option that allows mounting an OpenAPI spec under a specific path prefix:

erf:start_link(#{
    name => users_api,
    port => 8080,
    path_prefix => <<"/users">>,  %% New option
    spec_path => <<"users.openapi.json">>,
    callback => users_callback
}).

erf:start_link(#{
    name => products_api,
    port => 8080,  %% Same port
    path_prefix => <<"/products">>,
    spec_path => <<"products.openapi.json">>,
    callback => products_callback
}).

A request to GET /users/123 would strip the prefix and match against /123 in the users spec router.

Implementation Considerations

The change would likely involve:

  1. erf.erl: Add path_prefix to the conf() type and pass it through to router generation

  2. erf_router.erl: Modify generate/2 to either:

    • Prepend the prefix to all path patterns in the generated clauses, or
    • Strip the prefix from incoming requests before pattern matching
  3. HTTP server coordination: When multiple erf instances share the same port, coordinate through a shared listener that dispatches to the correct router based on prefix matching

  4. Configuration validation: Ensure no prefix conflicts exist on the same port

Benefits

  • Simplified deployments: Single port exposure for multiple APIs
  • Better resource utilization: Shared acceptor pool and connection handling
  • Cleaner architecture: Logical grouping of related APIs under a common host
  • Container-friendly: Reduces port mapping complexity in Docker/Kubernetes environments
  • API gateway pattern: Enables building lightweight API gateways with erf

Alternatives Considered

Alternative Drawback
Manually modifying OpenAPI specs Prepending prefixes to all paths in each spec works but is error-prone and breaks the design-first philosophy where the spec should remain the source of truth
External reverse proxy Adding Nginx/HAProxy in front works but introduces additional infrastructure complexity for what could be handled natively
Merging specs into one Combining multiple specs loses the benefits of separation, independent versioning, and modular callback organization

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Status

    📑 TODO

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions