Search code examples
expressaws-lambdaswaggeraws-api-gatewayswagger-ui

Setup of swagger-ui-express with API Gateway


I am not able to figure out the correct setup of either my API Gateway or swagger-ui-express. The lambda function runs successful and and return the html but the related resouces are not able to be loaded and get an 404 error.

const app = express();
const swaggerUI = require('swagger-ui-express');
const serverlessExpress = require('@vendia/serverless-express');

const Stage = process.env.Stage;
const apiId = process.env.apiId;
const options = {
    customCss: '.swagger-ui .topbar { display: none }',
    customCss: '.swagger-ui .topbar { background-color: red }'
}

let serverlessExpressInstance

async function asyncTask() {
    // load latest spec from API Gateway export removed to simplicity reasons.
    const swaggerDocument = spec;
    console.log("load swagger file complete.");
    return swaggerDocument
}

async function setup(event, context) {
    const swaggerDocument = await asyncTask()
    console.log(swaggerDocument)

    app.use('/api-doc', swaggerUI.serveWithOptions({ redirect: false }));
    app.use('/api-doc', swaggerUI.setup(swaggerDocument, options));
    console.log("setup of swagger complete");

    serverlessExpressInstance = serverlessExpress({ app })
    return serverlessExpressInstance(event, context)
}

function handler(event, context) {
    if (serverlessExpressInstance) return serverlessExpressInstance(event, context)
    return setup(event, context)
}

exports.handler = handler

Setup on API Gateway is the following:

API Gateway resources

Both resources are pointing to the lambda function. When I load the page via: https://.execute-api..amazonaws.com/dev/api-doc The following errors are raised:

Error logs

How can I ensure that the resources are loaded correctly via the correct path ...dev/api/doc/...


Solution

  • You're probbably getting these errors because swagger-ui is configured to look for the image/js resources and express doesn't serve these files. You could potentially use something like serverless-http like this, but I'm not really sure it's a good idea to try and force express into the response of an API gateway lambda function.

    As an alternative I managed to get swagger up and running on an API Gateway lambda function, fetching the swagger JSON directly.

    import { APIGateway } from "aws-sdk";
    import { env } from "../../helpers/env";
    
    const API_ID = env("API_ID", "");
    
    export const handler = async () => {
      const apiGateway = new APIGateway({
        apiVersion: "2018-11-29",
      });
      const apiGatewayExport = await apiGateway
        .getExport({
          exportType: "swagger",
          restApiId: API_ID,
          stageName: "prod",
          accepts: "application/json",
          parameters: {
            extensions: "apigateway",
          },
        })
        .promise();
    
      if (!apiGatewayExport.body) {
        throw new Error("No body found in API Gateway Export");
      }
    
      return {
        statusCode: 200,
        headers: {
          "Content-Type": "text/html",
        },
        body: generateSwaggerPageBody(apiGatewayExport.body?.toString()),
      };
    };
    
    const generateSwaggerPageBody = (swaggerSpec: string) => `<!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>Swagger</title>
                <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@3/swagger-ui.css">
            </head>
            <body>
                <div id="swagger"></div>
                <script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
                <script>
                  SwaggerUIBundle({
                    dom_id: '#swagger',
                    spec: ${swaggerSpec}
                });
                </script>
            </body>
            </html>`;
    
    

    I'm taking the API information directly from API Gateway via SDK and then I just return an html page with tags loading swagger directly - no express overhead needed.