dougmoscrop / serverless-http

Use your existing middleware framework (e.g. Express, Koa) in AWS Lambda 🎉
Other
1.72k stars 165 forks source link

"Cannot Get /" when Lamba function endpoint is accessed for Node.js/Express app? #162

Closed roschler closed 4 years ago

roschler commented 4 years ago

I have managed to get my Node.js Express app successfully deployed using serverless and serverless-http. However, whenever I access my endpoint URL, I get the response:

Cannot get /

I have my code setup to run in regular Express app server mode when running locally, and in serverless-http assisted mode when running as a Lambda function endpoint. The selection is controlled via an environment variable. When I run locally on my PC, the Express index page shows fine. When I run in Lambda endpoint mode, I get the error above. I have checked the CloudWatch logs for my Lambda function and there are no errors at all.

What could be the problem? Why is routing failing when my app is running as a Lambda function but works fine when running locally on my PC?

Below is my app.js code for the Lambda function execution context:

  // We are running as a REST API endpoint in the cloud.
  const serverless = require('serverless-http');
  const express = require('express');
  const app = express();

  app.use(express.urlencoded({ extended: true }));
  app.use(express.json());
  app.get('/api/info', (req, res) => {
    res.send({ application: 'my-node-js-express-app', version: '1' });
  });
  app.post('/api/v1/getback', (req, res) => {
    res.send({ ...req.body });
  });

  // This is the entry point specified in the serverless.yml file for our
  //  REST API we expose to provide the web server content.
  module.exports.handler = serverless(app);

  console.log(errPrefix + `Running in Lambda function execution context.`);
roschler commented 4 years ago

This was due to my inexperience with serverless-http. You still need to do all the Express initialization operations that you do normally. The example I had found on the web that was the source of the code shown in my original post, did not include those auxiliary operations. I have it working now. Below is the code I use to support both running on a local PC, for local debugging, and for running as an AWS Lambda function. For local debugging, set an environment variable named EXECUTION_CONTEXT to the value of "server" to run as a typical Express app listening on a local port.

NOTE: I know I could modify the code with some IF blocks to avoid repeating much of the code between the two execution contexts. I didn't do that because my belief is that over time there will be things that I want do differently between the two execution contexts, so I'd rather just edit the duplicate code blocks ad hoc without worrying about how it will affect the opposing execution context.

      const path = require('path');
      const fs = require('fs');

      let errPrefix = 'app.js::outside_context'

      if (process.env.EXECUTION_CONTEXT && process.env.EXECUTION_CONTEXT == 'server') 
      {
        let errPrefix = `(app.js::server) `;

        // ========================= EXECUTION CONTEXT: SERVER (LOCAL PC) ================

        // See app.js to see how the execution context for the Lambda function
        //  execution context is set up.  This file is run only when we are
        //  running on our local PC.

        let createError = require('http-errors');
        let express = require('express');
        let path = require('path');
        let cookieParser = require('cookie-parser');
        let logger = require('morgan');

        let indexRouter = require('./routes/index');
        let usersRouter = require('./routes/users');

        let app = express();

        // view engine setup
        app.set('views', path.join(__dirname, 'views'));
        app.set('view engine', 'pug');

        app.use(logger('dev'));
        app.use(express.json());
        app.use(express.urlencoded({ extended: false }));
        app.use(cookieParser());
        app.use(express.static(path.join(__dirname, 'public')));

        app.use('/', indexRouter);
        app.use('/users', usersRouter);

        // catch 404 and forward to error handler
        app.use(function(req, res, next) {
          next(createError(404));
        });

        // error handler
        app.use(function(err, req, res, next) {
          // set locals, only providing error in development
          res.locals.message = err.message;
          res.locals.error = req.app.get('env') === 'development' ? err : {};

          // render the error page
          res.status(err.status || 500);
          res.render('error');
        });

        module.exports = app;

        console.log(errPrefix + `Running in LOCAL PC execution context.`);
        console.log(errPrefix + `__dirname is ${__dirname}.`);
      } else {
        let errPrefix = `(app.js::lambda) `;

        // ========================= EXECUTION CONTEXT: REST API (CLOUD) ================

        // We are running as a REST API endpoint in the cloud.
        const serverless = require('serverless-http');
        const express = require('express');

        const createError = require('http-errors');
        const path = require('path');
        const cookieParser = require('cookie-parser');
        const logger = require('morgan');

        const indexRouter = require('./routes/index');
        const usersRouter = require('./routes/users');  
        const app = express();

        app.get('/api/info', (req, res) => {
          res.send({ application: 'my-express-app-as-lambda', version: '1' });
        });

        app.post('/api/v1/getback', (req, res) => {
          res.send({ ...req.body });
        });

       // view engine setup
        app.set('views', path.join(__dirname, 'views'));
        app.set('view engine', 'pug');

        app.use(logger('dev'));
        app.use(express.json());
        app.use(express.urlencoded({ extended: false }));
        app.use(cookieParser());
        app.use(express.static(path.join(__dirname, 'public')));

        app.use('/', indexRouter);
        app.use('/users', usersRouter);

        // catch 404 and forward to error handler
        app.use(function(req, res, next) {
          next(createError(404));
        });

        // error handler
        app.use(function(err, req, res, next) {
          // set locals, only providing error in development
          res.locals.message = err.message;
          res.locals.error = req.app.get('env') === 'development' ? err : {};

          // render the error page
          res.status(err.status || 500);
          res.render('error');
        });

        // This is the entry point specified in the serverless.yml file for our
        //  REST API we expose to provide the web server content.
        module.exports.handler = serverless(app);

        console.log(errPrefix + `Running in Lambda function execution context.`);
        console.log(errPrefix + `__dirname is ${__dirname}.`);
      }
dougmoscrop commented 4 years ago

can you try 2.5.0 with a simple config? should work

SiriusDely commented 3 years ago

in my case it was due to

app.use(cookieParser); // incorrect

// instead of:
app.use(cookieParser()) // correct

😓