claudiajs / claudia

Deploy Node.js projects to AWS Lambda and API Gateway easily
https://claudiajs.com
MIT License
3.8k stars 274 forks source link

Type error shows up in generated aws-serverless-express index.js #163

Closed LouisPennachio closed 6 years ago

LouisPennachio commented 6 years ago

I am following the tutorial to deploy a helloworld express app (using typescript).

I first generated my express wrapper using : claudia --source dist generate-serverless-express-proxy --express-module app

It did not produce any error. Then I tried to deploy this to AWS using : claudia create --source dist --handler lambda.handler --runtime nodejs8.10 --region eu-west-1

which produced this error :

TypeError: "listener" argument must be a function at _addListener (events.js:239:11) at Server.addListener (events.js:297:10) at new Server (_http_server.js:269:10) at Object.createServer (http.js:34:10) at Object.createServer (/tmp/5DNbRq/my-api-1.0.0-EsUxp4/package/node_modules/aws-serverless-express/index.js:155:25) at Object. (/tmp/5DNbRq/my-api-1.0.0-EsUxp4/package/lambda.js:13:37) at Module._compile (module.js:652:30) at Object.Module._extensions..js (module.js:663:10) at Module.load (module.js:565:32) at tryModuleLoad (module.js:505:12) at Function.Module._load (module.js:497:3) at Module.require (module.js:596:17) at require (internal/module.js:11:18) at validatePackage (/usr/local/lib/node_modules/claudia/src/tasks/validate-package.js:16:15) at initEnvVarsFromOptions.then.then.then.then.then.then.then.dir (/usr/local/lib/node_modules/claudia/src/commands/create.js:342:10) at cannot require ./lambda after clean installation. Check your dependencies.

So the problem is apparently coming from aws-serverless-express/index.js:155

Here it is :

function createServer (requestListener, serverListenCallback, binaryTypes) {
    onst server = http.createServer(requestListener) // <--- THIS LINE

    server._socketPathSuffix = getRandomString()
    server._binaryTypes = binaryTypes ? binaryTypes.slice() : []
    server.on('listening', () => {
        server._isListening = true

        if (serverListenCallback) serverListenCallback()
    })
    server.on('close', () => {
        server._isListening = false
    })
    .on('error', (error) => {
        if (error.code === 'EADDRINUSE') {
            console.warn(`WARNING: Attempting to listen on socket ${getSocketPath(server._socketPathSuffix)}, but it is already in use. This is likely as a result of a previous invocation error or timeout. Check the logs for the invocation(s) immediately prior to this for root cause, and consider increasing the timeout and/or cpu/memory allocation if this is purely as a result of a timeout. aws-serverless-express will restart the Node.js server listening on a new port and continue with this request.`)
            server._socketPathSuffix = getRandomString()
            return server.close(() => startServer(server))
        }

        console.log('ERROR: server error')
        console.error(error)
    })

    return server
}

I am not sure how to investigate any further, is this a bug or am I doing something wrong ?

gojko commented 6 years ago

what does your app.js module export?

LouisPennachio commented 6 years ago
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const express = require("express");
const bodyParser = require("body-parser");
const routes_1 = require("./routes/routes");
class App {
    constructor() {
        this.routes = new routes_1.Routes();
        this.app = express();
        this.config();
        this.routes.routes(this.app);
    }
    config() {
        // Parser setup
        this.app.use(bodyParser.json());
        this.app.use(bodyParser.urlencoded({ extended: true }));
    }
}
exports.default = new App().app;
//# sourceMappingURL=app.js.map
gojko commented 6 years ago

I assume es6 modules might be the problem there. Lambda/claudia baseline is still Node 6, so es6 modules aren’t supported. try exporting the app directly instead of exports.default. Alternatively, change the generated lambda file to load the app from the app module correctly. it just does a require rather than import, so you might fix it by importing.

LouisPennachio commented 6 years ago

Indeed !

Changing the export line from : exports.default = new App().app;

To : module.exports = new App().app;

did the trick. Thank you :)

dolosplus commented 5 years ago

Hello.

I'm running into the same exact error here and I've downgraded from es6 to es5. And my code has the export correctly.

import * as express from 'express';

export class App { public app: express.Application; constructor() { this.app = express(); } } module.exports = new App().app; ` I have: Node v8.11.3 npm 6.9.0 webpack 2.4.1

gojko commented 5 years ago

@dolosplus you can't use ES modules with node 8, that's not supported in that node version. this has nothing to do with claudia.

dolosplus commented 5 years ago

Thank you. That means I can't use Node 10/11 with Claudia either since AWS Lambda only support up to Node 8 ?

gojko commented 5 years ago

@dolosplus in theory, you could deploy Node 10/11 as a new runtime to Lambda in a layer, then link to that layer when deploying with claudia (see https://claudiajs.com/news/2019/01/07/claudia-5.3.html). If this is too much magic for you, you'll need to wait for official support for Node 10 in AWS Lambda.

dolosplus commented 5 years ago

Thank you. I forgot to mention that I also use Webpack to build my TS scripts into a single JS file with ES5 and use Claudia to proxy and deploy to Lambda. Would that still be possible without using Layers?