fastify / fastify-swagger-ui

Serve Swagger-UI for Fastify
MIT License
142 stars 42 forks source link

Support multiple distinct UIs #55

Closed fozziethebeat closed 1 year ago

fozziethebeat commented 1 year ago

Prerequisites

🚀 Feature Proposal

With ChatGPT plugins depending on OpenAPI specifications, it would be really helpful if there's a simplified way to create at least two distinct OpenAPI specifications and Swagger UI's. Each version would have to live under distinct URL routes. They would both consume the same underlying swaggerObject but would likely have different transformSpecification functions to selectively reveal or hide different routes.

Ideally this is done by registering the swagger ui plugin multiple times and configuring them appropriately.

Right now this doesn't work because the plugin does some decorations that clash on the second registration.

Motivation

This would make it trivial to run and manage a single server that has an API service that's suitable for OpenAI plugins with a subset of routes supported and a wider set of API routes that are available for other clients, such as a web app.

Example

An example setup could be:

  await fastify.register(require("@fastify/swagger-ui"), {
    routePrefix: "/internal-documentation",
    uiConfig: {
      docExpansion: "full",
      deepLinking: false,
    },
    uiHooks: {
      onRequest: function (request, reply, next) {
        next();
      },
      preHandler: function (request, reply, next) {
        next();
      },
    },
    staticCSP: true,
    transformStaticCSP: (header) => header,
    transformSpecification: internalAPITransformation,
    transformSpecificationClone: true,
  });
  await fastify.register(require("@fastify/swagger-ui"), {
    routePrefix: "/external-documentation",
    uiConfig: {
      docExpansion: "full",
      deepLinking: false,
    },
    uiHooks: {
      onRequest: function (request, reply, next) {
        next();
      },
      preHandler: function (request, reply, next) {
        next();
      },
    },
    staticCSP: true,
    transformStaticCSP: (header) => header,
    transformSpecification: externalAPITransformation,
    transformSpecificationClone: true,
  });
mcollina commented 1 year ago

What error are you getting when running the above? This should be feasible to achieve.

fozziethebeat commented 1 year ago

I'm getting the following error from Fastify:

[App] FastifyError [Error]: The decorator 'swaggerCSP' has already been added!
[App]     at decorate (/Users/fozziethebeat/devel/durable/AI-API/node_modules/fastify/li
b/decorate.js:23:11)
[App]     at Object.decorateFastify [as decorate] (/Users/fozziethebeat/devel/durable/AI-API/node_modules/fastify/lib/decorate.js:67:3)
[App]     at fastifySwaggerUi (/Users/fozziethebeat/devel/durable/AI-API/node_modules/@fastify/swagger-ui/index.js:7:11)
[App]     at Plugin.exec (/Users/fozziethebeat/devel/durable/AI-API/node_modules/avvio/p
lugin.js:130:19)
[App]     at Boot.loadPlugin (/Users/fozziethebeat/devel/durable/AI-API/node_modules/avvio/plugin.js:272:10)
[App]     at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
 {
[App]   code: 'FST_ERR_DEC_ALREADY_PRESENT',
[App]   statusCode: 500
[App] }

I'm guessing it's as simple as making that csp object more uniquely named each time the plugin is registered.

Eomm commented 1 year ago

You need to create two encapsulated contexts. Here is an example with another plugin - but the code would be the same: https://stackoverflow.com/questions/75938106/is-there-any-way-to-configure-fastify-caching-per-route/

fozziethebeat commented 1 year ago

Well, something new i just learned about Fastify. That absolutely solved the problem with just a few line changes. Thank you!

ThomasKoscheck commented 3 months ago

@fozziethebeat I have the same problem, can you share how you exactly did it?

fozziethebeat commented 3 months ago

I sadly have forgotten what I did here