nestjs / swagger

OpenAPI (Swagger) module for Nest framework (node.js) :earth_americas:
https://nestjs.com
MIT License
1.7k stars 477 forks source link

Serverless issue #199

Closed csakbalint closed 5 years ago

csakbalint commented 5 years ago

I'm submitting a...


[ ] Regression 
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

Swagger endpoint is not found.

Expected behavior

Html response generated by swagger-ui.

Minimal reproduction of the problem with instructions

Here is an example I use in my project.

What is the motivation / use case for changing the behavior?

I'd like to generate swagger documents based on my modules and endpoints, but I cannot.

Environment


"@nestjs/common": "^5.4.0",
"@nestjs/core": "^5.4.0",
"@nestjs/swagger": "^2.5.1",
"aws-serverless-express": "^3.3.5",
"express": "^4.16.4",


For Tooling issues:
- Node version: 8.10  
- Platform: Mac 

Others:
I run my environment with serverless and serverless-offline plugin

jtmthf commented 5 years ago

@csakbalint I just ran into this the other day. The problem is that given a swagger path of swagger, swagger-ui-express serves its html file from /swagger/ and will redirect if the request path is /swagger. Unfortunately API Gateway strips the trailing slash from the request path so you have to add it back with the block below.

if (event.path === '/swagger') {
  event.path = '/swagger/';
}

This cannot be done as a middleware as req.path from express is a getter and will throw an error if you try to overwrite it.

plorencrstit commented 4 years ago

I have the similar problem. Swagger documentation doesn't work on AWS lambda using serverless framework. It works testing it locally (sls offline start).

What is very strange, that only one file .js and doesn't have 404. Others does. I have no idea why.

Swagger API documentation: https://js6bdk1qg3.execute-api.eu-west-2.amazonaws.com/master/api

404: https://js6bdk1qg3.execute-api.eu-west-2.amazonaws.com/master/swagger-ui-bundle.js

200: https://js6bdk1qg3.execute-api.eu-west-2.amazonaws.com/master/swagger-ui-init.js

My part of configuration:

  if (event.path === '/api') {
    event.path = '/api/';
  }

event.path = event.path.includes('swagger-ui') ? `/api${event.path}` : event.path;
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

export default (app, isServerless = false): void => {
  const serverUrl = isServerless ? '/dev' : '/';

  const options = new DocumentBuilder()
    .setTitle(process.env.APP_NAME)
    .setDescription(process.env.APP_DESC)
    .setVersion('1.0')
    .addServer(serverUrl)
    .addBearerAuth()
    .build();

  const document = SwaggerModule.createDocument(app, options);

  SwaggerModule.setup('api', app, document);
};

@jtmthf @csakbalint would you have any idea?

nathanagez commented 4 years ago

Hi @plorencrstit , I am trying to use swagger with serverless and lambda functions. Can you explain how did you configure it ?

plorencrstit commented 4 years ago

Hi @NastyZ98, what problem do you have?

Using TS and serverless-plugin-optimize plugin I needed to add to my serverless.yml configuration:

custom:
  optimize:
    external: ['swagger-ui-dist']

in lambda.ts: [my handler]

export const handler: Handler = async (event: any, context: Context) => {
  if (event.path === '/api') {
    event.path = '/api/';
  }
  event.path = event.path.includes('swagger-ui') ? `/api${event.path}` : event.path;

  cachedServer = await bootstrapServer();

  return proxy(cachedServer, event, context, 'PROMISE').promise;
};
nathanagez commented 4 years ago

@plorencrstit hi, thank you for your reply, I am trying to make swagger working on my aws lambda but it result of a 404 with sls offline and of an error 500 when I deploy on aws.

I also added serverless-plugin-optimize and edited my serverless.yml but it looks like that serverless-plugin-optimize broke something when I call: myawsurl/dev/api

Capture d’écran 2020-04-22 à 00 59 17

Here is my lambda.ts

const binaryMimeTypes: string[] = [];

let cachedServer: Server;

process.on('unhandledRejection', reason => {
  console.error(reason);
});

process.on('uncaughtException', reason => {
  console.error(reason);
});

function setupSwagger(app: INestApplication) {
  const options = new DocumentBuilder()
    .setTitle('Tyffis API')
    .setDescription('Tyffis REST API documentation')
    .setVersion('1.0.0')
    .addTag('tyffis')
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('api', app, document);
}

async function bootstrapServer(): Promise<Server> {
  if (!cachedServer) {
    try {
      const expressApp = express();
      const adapter = new ExpressAdapter(expressApp);
      const nestApp = await NestFactory.create(AppModule, adapter);
      nestApp.use(eventContext());
      await nestApp.init();
      setupSwagger(nestApp);
      cachedServer = createServer(expressApp, undefined, binaryMimeTypes);
    } catch (error) {
      return Promise.reject(error);
    }
  }
  return Promise.resolve(cachedServer);
}

export const handler: Handler = async (event: any, context: Context) => {
  if (event.path === '/api') {
    event.path = '/api/';
  }
  event.path = event.path.includes('swagger-ui')
    ? `/api${event.path}`
    : event.path;

  cachedServer = await bootstrapServer();
  return proxy(cachedServer, event, context, 'PROMISE').promise;
};
plorencrstit commented 4 years ago

I had the same problem before with typeorm. My solution was to manually listed entities in the configuration file. So I have two kind of settings.

  entities: [`${__dirname}/../**/*.entity{.ts,.js}`],
  ...typeOrmConfigGeneral,
};

export const typeOrmConfigServerless: TypeOrmModuleOptions = {
  entities: [
    Category,
    Comment,
    Discussion,
  ],
  ...typeOrmConfigGeneral,
};

I choose the configuration based on some env.

Do you use typeorm?

nathanagez commented 4 years ago

Yes I use typeorm, your solution have fixed my problem concerning the entity :) ! But my swagger documentation still don't work..

Capture d’écran 2020-04-22 à 10 46 53

Here is my serverless.yml

functions:
  index:
    handler: src/lambda.handler
    events:
      - http:
          cors: true
          path: '/{proxy+}'
          method: any

Did I miss something in my lambda.tsupper ?

plorencrstit commented 4 years ago

Show the entire serverless.yml, please.

nathanagez commented 4 years ago

Here it is:

service: tyffis-backend-test

provider:
  name: aws
  runtime: nodejs12.x
  region: us-east-1
  stage: dev
  role: LambdaRole
  memorySize: 512
  vpc:
    securityGroupIds:
      - sg
    subnetIds:
      - ids
  environment:
    TYPEORM_HOST: ${self:custom.AURORA.HOST}
    TYPEORM_PORT: ${self:custom.AURORA.PORT}
    TYPEORM_USER: ${self:custom.USERNAME}
    TYPEORM_PASSWORD: ${self:custom.PASSWORD}
    TYPEORM_DB: ${self:custom.DB_NAME}
    JWT_SECRET: secret

plugins:
  - serverless-plugin-typescript
  - serverless-plugin-optimize
  - serverless-offline

package:
  individually: true

custom:
  DB_NAME: db_name
  USERNAME: user
  PASSWORD: pwd
  AURORA:
    HOST:
      Fn::GetAtt: [AuroraRDSCluster, Endpoint.Address]
    PORT:
      Fn::GetAtt: [AuroraRDSCluster, Endpoint.Port]
  optimize:
    external: ['swagger-ui-dist']

resources:
  Resources:
    LambdaRole: ${file(./infra-serverless/LambdaRole.yml)}
    AuroraRDSCluster: ${file(./infra-serverless/AuroraRDSCluster.yml)}

functions:
  index:
    handler: src/lambda.handler
    events:
      - http:
          cors: true
          path: '/{proxy+}'
          method: any

All my other routes work, I have authentication flow with jwt, I have no problem with them

plorencrstit commented 4 years ago

Try with:

functions:
  main:
    handler: src/lambda.handler
    events:
      - http:
          method: any
          path: /{any+}
nathanagez commented 4 years ago

It finally work !! 😄 The problem was here: lambda.ts

await nestApp.init();
setupSwagger(nestApp);

I copied https://gist.github.com/csakbalint/7fe406bd1b15124180a988c87d57cf9b from the original author to make my test but nestApp init has to be call before the setupSwagger function..

setupSwagger(nestApp);
await nestApp.init();

Thank you very much for your time @plorencrstit ! I Appreciate it!

@csakbalint there is an error in your gist, look upper you switched your setupSwagger function with await nestApp.init(); one.

tuna318 commented 4 years ago

@plorencrstit Thank you very much, you saved my day!!

wagnerlinharesm commented 3 years ago

Hello guys! I have the same problem, but in serveless offline, the swagger works. When I access the aws endpoint, he didn't work. My lambda is similar of @nathanagez. Anyone can help me?