Skip to content

Conversation

@orionstudt
Copy link
Contributor

@orionstudt orionstudt commented Dec 16, 2025

Adds support for a static path_prefix value to be set on the creation of a Definition. The issue with the existing config.path function is that it needs to run at runtime for every single request, and that when there are many different possible prefixes it needs to account for all of them.

Imagine a Rails app built from many different engines. Engines like:

  • root
  • weather
  • stocks
  • analytics

These engines each provide APIs that have different scaling needs. So in a production environment, you will not deploy them together. They will each get their own domains like:

  • api.example.com
  • weather-api.example.com
  • stocks-api.example.com
  • analytics-api.example.com

However when you are running this Rails app locally you mount all of the engines to a single app, and configure them like:

  • localhost:3000
  • localhost:3000/weather
  • localhost:3000/stocks
  • localhost:3000/analytics

Furthermore, imagine that each of your engines have their own Open API document. In a scenario like this, in order to configure prefixes when running locally you would have to drop those prefixes from every request like:

OpenapiFirst.configure do |config|
  config.register('openapi/openapi.yaml')
  config.register('openapi/weather-openapi.yaml', as: :weather)
  config.register('openapi/stocks-openapi.yaml', as: :stocks)
  config.register('openapi/analytics-openapi.yaml', as: :analytics)
  config.path = lambda { |request|
    request.path.delete_prefix('/weather') if request.path.start_with?('/weather')
    request.path.delete_prefix('/stocks') if request.path.start_with?('/stocks')
    request.path.delete_prefix('/analytics') if request.path.start_with?('/analytics')
  }
end

But this has issues because:

  1. We know what the prefix will be when we register the definition
  2. The delete_prefix checks are going to run on every request, even for engines that don't care about the prefix being deleted
  3. If the root engine has any endpoints that begin with /weather, /stocks, or /analytics they will lose the prefix when they shouldn't

So adding support for path_prefix on Definition load enables this to become:

OpenapiFirst.configure do |config|
  config.register('openapi/openapi.yaml')
  config.register('openapi/weather-openapi.yaml', as: :weather, path_prefix: '/weather')
  config.register('openapi/stocks-openapi.yaml', as: :stocks, path_prefix: '/stocks')
  config.register('openapi/analytics-openapi.yaml', as: :analytics, path_prefix: '/analytics')
end

And now the prefix only drops for the specific definition being evaluated.

Comment on lines +86 to +87
# @param request [Rack::Request] The Rack request object.
# @param response [Rack::Response] The Rack response object.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive by re-naming to align with what validate_request already calls them since it is less verbose

let(:response) { Rack::Response.new(JSON.generate({ 'id' => 42 }), 200, { 'Content-Type' => 'application/json' }) }

it 'returns a valid response' do
request = build_request('/prefix/stuff')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one wasn't actually testing because it was passing a request defined above that didn't have /prefix in the path

@orionstudt orionstudt marked this pull request as ready for review December 17, 2025 00:37
@orionstudt orionstudt requested a review from ahx as a code owner December 17, 2025 00:37
@ahx
Copy link
Owner

ahx commented Dec 18, 2025

Thanks for the great description. Looks good to me. Can you add a changelog entry about this? (Just in the "Unreleased" section)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants