Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom basePath #113

Open
henhal opened this issue Nov 8, 2019 · 12 comments
Open

Support custom basePath #113

henhal opened this issue Nov 8, 2019 · 12 comments

Comments

@henhal
Copy link

henhal commented Nov 8, 2019

I have an OpenAPI 3.0.2 definition which I use to create the public documentation for my API. In the spec, I list the URL:s of my public servers under the servers section.
Now I am trying to use the same definition document with express-openapi-validator to validate my requests, but my backend server is running in a private VPC behind a proxy and is not using the same base path as the public facing API served through AWS API gateway.

To solve this it would be great to be able to supply a custom basePath to the options passed to the OpenApiValidator, instead of having it parse the servers section.

Example:

document:

openapi: 3.0.2
info:
  title: foo
servers:
  - url: 'https://my-public-api.com/some/path/prefix'

API gateway will now route /some/path/prefix/* to my Express application, without the prefix, so a request for /some/path/prefix/foo will be routed to my backend server simply as /foo.
However, when I attach the OpenApi validator, it will look for route /foo in the open api route map as /some/path/prefix/foo, since it will have applied the servers path prefix to all routes, and this route does not exist so it ends up producing a NOT FOUND response.

I know that if I supply both the public and the internal mapping under the servers section, the routing will work - but OpenApiValidator will create a route map of double the size, half of it being incorrect - and my documentation rendered from the definition will now also be wrong.

It would be a shame to have to separate the definition I use as input to my documentation renderer from the one I use for validation in my backend server. It seems this could be solved by allowing a custom basePath: '/' to be supplied through the constructor options, which would then mean ignoring the servers section?

Note that this scenario also applies if you use multiple express sub-apps with separate API definitions for each one, e.g. you could have one definition for admin routes and one for user routes, and express set up with app.use('/admin', adminApp); app.use('/user', userApp);. Then the definitions would state the URL in the servers section including the path prefix, but each sub-app handles requests without the prefix.

@henhal
Copy link
Author

henhal commented Nov 8, 2019

Assuming PR's are welcome, I provided one for you to look at.

Thanks.

@cdimascio
Copy link
Owner

cdimascio commented Nov 9, 2019

@henhal PRs are always welcome. We love contributors! Thanks for your interest in this project :)

With respect to the servers proposal...
I'm reluctant to add this feature for two reasons:

  1. The performance (despite doubling the route count) is negligible. For example, on my Macbook, I can perform well over 200,000 lookups per millisecond on a js object containing 100,000 entries - see https://jsperf.com/javascript-objects-vs-map-performance. All in all, the performance aspect is insignificant. Also, the number of routes in an api spec, even when doubled, triple, quadrupled, ... will certainly not approach 100k.

  2. Given that performance is effectively a non-issue, I'd prefer not to add such additional options as it clutters the usage options and expands scope.

That all being said and assuming you still do not want to include both base paths in the openapi spec, particularly when running in production, I would recommend the following... Add a modification to your deployment process that modifies the spec. For example, you might use a yaml parser or tool e.g. js-yaml or yq to automate the removal/addition of the server entry when deploying to prod, and vice versa for dev. There are many tools and options available to do this.

Hope this helps!

@henhal
Copy link
Author

henhal commented Nov 9, 2019

Thanks for the input. I honestly don't think a base path option is very cluttering, and other open api adapters I've worked with provide this, but I see your point. I actually already use a preprocessing step on my spec (with yimp) so I guess I could solve it there though.

However, I want to add that I think flexibility is key. For example I'd also have liked being able to skip certain types of validation, such as headers. Same reason really, a public facing api gateway might take care of the security schemes such as api keys and access tokens before relaying requests to a backend server. In that case I'd like to be able to customize so that headers are ignored in my express app.

Just wanted to bring some context around the fact that open api specs can be used in several layers of a system.

@cdimascio
Copy link
Owner

Thanks @henhal. I'm going to leave this open for a bit.

@cdimascio
Copy link
Owner

cdimascio commented Dec 4, 2019

@henhal you're point about security is a very good one. There certainly should be a way to opt-out of security, particularly since this is an area gateways often handle. I will be sure to add that feature asap.
that being said, i'm happy to revisit base path. it would be great to hear another compelling argument to sway me :) really appreciate your feedback

@dannydeut
Copy link

dannydeut commented Jan 13, 2020

I don't known if it applies to this issue. We have a variable in our base path (see specs serverObject). The validator is only applied when I use the default value "000000". The validator does not work when I do a request on /v1/org/123456/.

Edit: I think #115 would solve our issue

openapi: 3.0.0
...
servers:
  - url: 'https://{enviroment}.domain.com/v1/org/{organisationId}/'
    variables:
      organisationId:
        description: 'Organisation ID assigned by the provider'
        default: "000000"
      environment:
        description: 'Production or testing server'
        default: ta
        enum:
          - api
          - api-sandbox

@cdimascio
Copy link
Owner

Thanks @dannydeut. This is a gap.we do plsn to add support server variables. Feel free to create a separate issue for this.

@tony-aq
Copy link

tony-aq commented Sep 14, 2021

I have the same issue, using AWS with API Gateway and trying to present a spec for client use but also using a spec for validation on the backend.

I would appreciate some way to "not validate servers or base path" so that I could have one single spec instead of multiple spec views.

My workaround is to have a spec for validation and a modified spec for client inspection.

@rjbma
Copy link

rjbma commented Oct 25, 2021

I also have a similar issue where this would come in handy.

In my case I use a spec produced by a third party, which includes some (dummy) absolute base path (e.g, http://my.server.com/base/rsc/v1). What I currently do is modify the spec to say /base/rsc/v1 instead.

Of course this creates a problem when a new version of the spec comes along; I need to remember to change the path again, or else express-openapi-validator will stop validating requests without me being aware there's a problem.

Of course this is manageable (like pre-processing the spec as suggested previously), but would be great to have a "not so hacky" solution.

Anyways, thanks for this great library!

@knorrli
Copy link

knorrli commented Dec 12, 2022

Just to leave another vote for this feature: I have exactly the same problem as stated in the original post:

  • my express server lies behind a traefik reverse proxy that strips the https://domain.tld/api/v1 prefix
  • the OpenAPI definition is rendered with SwaggerUI to document the API
  • the same OpenAPI definition is used by express-openapi-validator to validate requests

My current workaround while still in development is to use a relative path as the server value, however this will not be acceptable when the project moves to production.

@cdimascio
Copy link
Owner

the new useRequestUrl option is available in v5.1.0. Set useRequestUrl: true in the validator options

See PR #857

@g-radam
Copy link
Contributor

g-radam commented Jun 3, 2024

@cdimascio If i'm following this issue correctly, PR #857 only partially fixes the issues described here.

Just to recap:
The desired behaviour here is that if we use the same spec for the gateway and internal server, the validation should be able to work irrespective of the public server paths. The issue is specifically with rewrites that gets performed on the gateway which breaks validation on the internal server. The simplest way to get this working is by defining both the gateway and internal servers:

servers:
  - url: /api/my/pubic/gateway/
    description: Public Gateway Server - Used by the public, performs rewrites to below internal URL
  - url: /api/v1/private/
    description: Internal URL - used by express-openapi-validator on internal server for validation only.

However I would say the issues with this is that gateway spec exposes the internal server rewrite path, when it's only there for use by the internal sever validation code. Ontop of this, express-openapi-validator can't validate anything else but the express route its used on, so the gateway path /api/my/pubic/gateway/ is useless to it.


Issue #857 useRequestUrl Allows the validator to handle paths relative to the express route (/) that it's used on, where previously the code was always using the req.originalUrl which was not relative. Great. However in reference to this issue, now to we have to set our spec servers array to the below PLUS setting useRequestUrl: true in our options:

servers:
  - url: /api/my/pubic/gateway/
    description: Public Gateway Server
  - url: /
    description: Allow lookupRoute()'s req.url to match this `/` prefix of the express route where the validator is used().

So when our validator is used() on the express route: /api/v1/private/, the above / relative server URL will work. Its better than the first example I gave above, but still, we need to define this server only for relative internal server validation.


One stop-gap solution I can think of is to NOT define the url: / server item in the spec, but instead automatically add it prior to use()'ing the validator:

// Load schema as a json object and attach a dummy "/" server for relative route matching in the lookupRoute() fn
const schema = loadSchema("./path/to/schema.json"); // Or use yaml
schema.servers.push({ url: "/" });

// Attach the validator to some sub route, EG: /api/v1/private/ 
router.use(
    OpenApiValidator.middleware({
        apiSpec: schema,
        useRequestUrl: true, // Use our CURRENT relative express route which will match the relative "/" server path
    }),
);

The spec:

servers:
  - url: /api/v1/
    description: Gateway

And what it looks like in a swagger-ui using this exact spec:
image

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

No branches or pull requests

7 participants