A Middleware for Swagger Resolution

A few months ago I started building a Swagger resolver library, with the intention of having a parser that understood different inheritance properties of the spec. About halfway through I realized that this library was going to be huge, with dozens of objects, and that it wasn't going to work the way I needed to. See, I was looking for a hunk of business logic that would be able to perform routing and input/output validation based on a request within a lightweight framework. A huge library that walked through an entire spec document is not what I was looking for.

Enter the middleware concept. All I really needed to do was match a swagger operation, pull out the parameters based on the spec, and then (optionally) perform validation. All based on the incoming request. A middleware that executed on the request, pulled the appropriate path and operation, and then resolved the related parameters and definitions was good enough. It'd be nice to have a complete resolver to simplify some of this routing logic, though the memory overhead just wasn't worth the simplification.

In order to define the middleware interface I took a look at something that's already built and used in node - apigee/swagger-tools. This middleware can be plugged into the stack and attaches a swagger property onto the request for downstream processing. This property has all sorts of goodies on it.

  1. apiPath: key in the swagger spec

  2. path: path from spec that maps to request

  3. operation: operation from spec that maps to request

  4. operationParameters: compiled parameters related to operation

  5. operationPath: path to the operation

  6. params: all parameters related to operation with computed 'value' from request

  7. security: security settings for the request

  8. swaggerObject: complete swagger spec

This is exactly what I was looking for. Well, almost. In a perfect world I wouldn't have to plug this information into the request object, as that feels pretty hacky, but there's not a lot of other options. Ideally the downstream middleware could be instantiated with all this stuff, and then only a clean request/response interface could be held to throughout the stack. However, I also feel like middleware instantiation should also be stateless. So... the request object it is.

Armed with this interface I built out avalanche-development/swagger-router-middleware. Not the most creative name, I know. Anyways, this middleware accepts a raw swagger spec in the form of an array for instantiation (which doesn't break the statelessness of the middleware) and then, when the stack is executed, it will plug in the appropriate chunks of data under the 'swagger' property. It does a bit more than that - if the operation or path cannot be found, or if the processing of the parameters fails due to a malformed request, appropriate http exceptions are thrown.

  1. $request->getAttribute('swagger')

  2. 'swagger' => [

  3. 'apiPath' => '/comments/{comment_id}', // matched string path

  4. 'path' => [ ... ], // full path definition

  5. 'operation' => [ ... ], // specific operation definition

  6. 'params' => [ ... ], // resolved list of parameters for this operation

  7. 'security' => [ ... ], // resolved list of securities for this operation

  8. ]

I'm still tweaking a few things with this - I'd love to have some validation in place based on definitions - though I'm not sure if it should live here or not. And if it lives somewhere else, I'll need to make sure that the validator has all the information it needs without worrying about resolving anything in-place. For now this middleware gives me the swagger resolution I need and in a manner that the rest of the app can benefit without the computational overhead of resolving an entire spec.