I am having trouble creating an endpoint to serve the swagger documentation. The only way I can do it locally is if the path has a double slash at the end localhost:3003/dev/swagger//
; If I omit one of the forward slashes, it returns a 404 for the path localhost:3003/swagger
without /dev
. Once deployed, API Gateway will return {"message": "Forbidden"}
for either swagger endpoint (with or without //
). How can I get the API Gateway /swagger
endpoint to return the swagger UI?, I'm not sure if I have missed some steps.
Below are the main.ts for my NestJS application as well as the serverless.yml and here is a sample repo with minimum setup to duplicate my issue. https://github.com/MRdgz/serverless-nestj-swagger
main.ts
// main.ts
import { INestApplication } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { configure as serverlessExpress } from '@vendia/serverless-express';
import { Callback, Context, Handler } from 'aws-lambda';
import { AppModule } from './app.module';
let server: Handler;
function setupSwagger(nestApp: INestApplication): void {
const config = new DocumentBuilder()
.setTitle('Sample API')
.setDescription('Sample API Documentation')
.setVersion('0.0.1')
.addServer('/dev')
.build();
const document = SwaggerModule.createDocument(nestApp, config);
SwaggerModule.setup('/swagger', nestApp, document, {
customSiteTitle: 'Sample',
swaggerOptions: {
docExpansion: 'none',
operationSorter: 'alpha',
tagSorter: 'alpha',
},
});
}
async function bootstrap(): Promise<Handler> {
const app = await NestFactory.create(AppModule);
setupSwagger(app);
await app.init();
const expressApp = app.getHttpAdapter().getInstance();
return serverlessExpress({ app: expressApp });
}
export const handler: Handler = async (
event: any,
context: Context,
callback: Callback,
) => {
event.path = `${event.path}/`;
event.path = event.path.includes('swagger-ui')
? `swagger${event.path}`
: event.path;
server = server ?? (await bootstrap());
return server(event, context, callback);
};
severless.yml
service: sample-api
variablesResolutionMode: 20210326
useDotenv: true
plugins:
- serverless-offline
- serverless-plugin-optimize
# functions will inherit settings from provider properties if available,
provider:
name: aws
runtime: nodejs14.x
lambdaHashingVersion: 20201221
# memorySize: 1024 # default 1024 MB
timeout: 30 # default 6 seconds
# sls deploy --stage {stage} otherwise defaults to dev
stage: ${opt:stage, 'dev'}
functions:
main:
handler: dist/main.handler
name: ${opt:stage, 'dev'}-${self:service}
events:
- http:
method: ANY
path: /{proxy+}
cors: true
custom:
serverless-offline:
httpPort: 3003
optimize:
external: ['swagger-ui-dist']