serverless-heaven / serverless-webpack

Serverless plugin to bundle your lambdas with Webpack
MIT License
1.72k stars 417 forks source link

No matching handler found for 'handler'. Check your service definition #171

Closed vcarel closed 7 years ago

vcarel commented 7 years ago

I just upgraded from serverless-webpack 2.0.0 to 2.2.0 and got the following error. It was working perfectly with the previous version.

No matching handler found for 'handler'. Check your service definition

Is there anything wrong in my configuration?

service: my_service

provider:
  ...

custom:
  webpack: webpack.config.serverless.js

functions:
  my_function:
    handler: handler.my_function
    events:
       ...

plugins:
  - serverless-webpack

FYI I'm on serverless latest (as of today: 1.18.1)

HyperBrain commented 7 years ago

Where is your handler.js located? In the service root directory? The error only happens, if the plugin cannot find any handler file that matches handler.*. Maybe you can post the result of ls -l in your service directory.

If your handler is located in a subdirectory, your handler definition (functions.my_function.handler) should be mydir/someotherdir/handler.my_function.

vcarel commented 7 years ago

It's defined in my webpack configuration:

module.exports = {
  entry: ['./src/server/handler.js'],
vcarel commented 7 years ago

By the way, I'm using yarn to freeze dependencies. I noticed while trying several configurations that I can get the issue with 2.0.0 too. It looks like the problem appears with the upgrade of an inner dependency, somewhere.

HyperBrain commented 7 years ago

Ok. I think the missing path in the function definition is the problem. The function definition should define the path - otherwise you'd also get issues with other plugins that need to find the handler. The problem is, that Serverless (the framework) is completely unaware of the handler location in your case.

So, please change the handler definition in your serverless.yml.

... and the good thing: With 2.2.0 you now do not have to set the entries manually anymore in your webpack config. Just use:

// webpack.conf.js
const slsw = require('serverless-webpack');
module.exports = {
  entry: slsw.lib.entries,
  ...
  output: {
    ...
    filename: '[name].js',
    ...
  }
};

Then you can add functions to your service without changing the config again.

HyperBrain commented 7 years ago

This is the function definition you should set:

functions:
  my_function:
    handler: src/server/handler.my_function
vcarel commented 7 years ago

I was searching how to define the path. I didn't put the serverless.yml is the same directory as the handler file, and that does not help.

Well, following what you said, I get another error:

  Type Error ---------------------------------------------

  Path must be a string. Received { 'src/server/handler': './src/server/handler.js' }

with

functions:
  my_function:
    handler: 'src/server/handler.my_function'
vcarel commented 7 years ago

Ok, found. The problem is that slsw.lib.entries does not contain an array, but the object { 'src/server/handler': './src/server/handler.js' }

If I just use the previous config, it works fine

module.exports = {
  entry: ['./src/server/handler.js']

So, at the end, the fix was to set the path in the handler definition:

functions:
  my_function:
    handler: src/server/handler.my_function
HyperBrain commented 7 years ago

Glad to hear that it now works 👍

lewisblackwood commented 7 years ago

Hey 👋 Apologies to reopen this, but I'm also seeing the same issue.

I'm trying to upgrade the serverless-babel-starter repo to use 2.2.0.

When I try to deploy, I get the error message:

Path must be a string. Received { 'src/handler': './src/handler.js' }

I've updated the webpack.config.js to:

const nodeExternals = require('webpack-node-externals');
const slsw = require('serverless-webpack');

module.exports = {
  entry: slsw.lib.entries,
  target: 'node',
  externals: [nodeExternals()],
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel'],
      include: __dirname,
      exclude: /node_modules/,
    }],
  },
};

and the serverless.yml to:

functions:
  hello:
    handler: src/handler.hello
    events:
      - http:
          path: hello
          method: get
      # Ping every 5 minutes to avoid cold starts
      - schedule:
          rate: rate(5 minutes)
          enabled: true

Do you have any suggestions? Would be great to get 2.2.0 working to be able to deploy a single function at a time.

HyperBrain commented 7 years ago

Can you try to define an output section in your config? With lib.entries it is important that you set the output definition properly.

    output: {
        libraryTarget: "commonjs2",
        path: path.join(__dirname, ".webpack"),
        filename: "[name].js"
    },

commonjs should also work instead of commonjs2 if you prefer that.

HyperBrain commented 7 years ago

It could be that there's a bug in the automatically generated output config if you omit it.

HyperBrain commented 7 years ago

If that does not help, could you run the deploy with SLS_DEBUG=* set and post the callstack of the error? This might help to find the exact reason faster.

lewisblackwood commented 7 years ago

Thanks! My webpack config now looks like this:

const nodeExternals = require("webpack-node-externals");
const slsw = require("serverless-webpack");
const path = require("path");

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  externals: [nodeExternals()],
  module: {
    loaders: [
      {
        test: /\.js$/,
        loaders: ["babel"],
        include: __dirname,
        exclude: /node_modules/
      }
    ]
  },
  output: {
    libraryTarget: "commonjs2",
    path: path.join(__dirname, ".webpack"),
    filename: "src/handler.js"
  }
};

But I'm now seeing this error associated with webpack:

throw new Error("Module '" + loader.path + "' is not a loader (must have normal or pitch function)");
HyperBrain commented 7 years ago

Hmmm. we use webpack 3 here, there you have module.rules instead of loaders. I think since webpack 2 module.loaders changed to module.rules like this:

    module: {
        rules: [
            {
                // Process ES6 with Babel.
                test: /\.(js|jsx)$/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: [ "node6", "stage-0" ],
                            plugins: []
                        }
                    }
                ],
            }
        ]
    }

Make also sure that you have defined the loaders and presets that you use in your package.json. Ours are:

    "babel-loader": "^7.1.1",
    "babel-preset-node6": "^11.0.0",
    "babel-preset-stage-0": "^6.24.1",
    "webpack": "^3.0.0"

We use node6 for all projects that deploy to node 6.10 lambdas.

You can safely use webpack ^3.0.0 as dependency. We didn't have any problems with that. Additionally you could use "[name].js" as filename in output, as that will dynamically adjust the handler name with each different handler used.

HyperBrain commented 7 years ago

@lewisblackwood If it does not work, I can have a look tomorrow and if I have time I could prepare a PR for the babel-starter repo.

lewisblackwood commented 7 years ago

@HyperBrain thanks for all your help! I have a working set-up now:

const nodeExternals = require("webpack-node-externals");
const slsw = require("serverless-webpack");
const path = require("path");

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  externals: [nodeExternals()],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: [
          {
            loader: "babel-loader",
            options: {
              presets: ["node6", "stage-0"],
              plugins: []
            }
          }
        ]
      }
    ]
  },
  output: {
    libraryTarget: "commonjs2",
    path: path.join(__dirname, ".webpack"),
    filename: "[name].js"
  }
};

and followed your suggestion of using webpack ^3.0.0 as a dependency.

I'll reach out to the maintainer of serverless-babel-starter and see if they're open to a PR 👍

Thanks again!

lewisblackwood commented 7 years ago

Hmm, although it seems I've now broken the use of webpack-node-externals.

Serverless: Packing external modules... Serverless: Uploading service .zip file to S3 (9.46 MB)...

This takes 100 secs to deploy the whole project, and the same using single function deploy.

@HyperBrain are you using webpack-node-externals in your set-up?

HyperBrain commented 7 years ago

@lewisblackwood Yes, I do, and it works just by adding externals: [ nodeExternals() ], to the config.

So serverless deploy and serverless deploy function --f <xxxxx> takes the same time for you? That sounds strange because the single function deploy should only package and deploy exactly one function.

To test you can try to run just the package phase of Serverless with serverless package --function=xxx to see if it is Serverless' deployment that is broken or if it is something with the plugin's packaging.

lewisblackwood commented 7 years ago

Apologies, I think I made a mistake here. The deploy is still relatively slow, but it's actually just one of the functions that is both slow to deploy individually and slows deployment of the whole project.

@HyperBrain one last question if I can - are you using either of serverless-plugin-optimize or serverless-optimizer-plugin? Should they be compatible with serverless-webpack?

I've included serverless-plugin-optimize which significantly speeds up deployment, but once the functions are deployed they error with:

module initialization error: Error.

HyperBrain commented 7 years ago

🤔 No... I never used these two plugins and do not know if there are any issues. But it would be worth to have a look at it - and see why there are problems. Could you try to ask about that in the serverless forum? Maybe someone else has run into that and probably found a solution (or workaround). At least we might get more information to solve it.

HyperBrain commented 7 years ago

After some thinking, I'm not really sure if any optimization plugins can add any value to the webpacked build and deployment. From the code and dependency side, everything is already optimized and only things that are needed are packaged (at least with the upcoming v3 version). I have to have a closer look into the plugin-optimize to see what exactly that does, and how it technically tries to speed up the deployment.

If you don't mind, you can experimentally try the v3.0.0-individual-packaging branch - and use the optimize plugin there. I made some improvements there that target stability and compatibility with other plugins.

HyperBrain commented 7 years ago

Will close this issue again now since there's no actual fail and you managed to deploy.

lewisblackwood commented 7 years ago

@HyperBrain thanks again for your help.

I've experimented further, using your v3.0.0-individual-packaging branch and serverless-plugin-optimize. Here's the repo.

Adding the plugin does reduce the time to deploy, but once deployed the Lambda errors with:

module initialization error: Error
    at s (/var/task/_optimize/service-name-dev-hello/src/handler.js:1:683)
    at /var/task/_optimize/service-name-dev-hello/src/handler.js:1:734
    at Object.309../utils (/var/task/_optimize/service-name-dev-hello/src/handler.js:86857:174)
    at s (/var/task/_optimize/service-name-dev-hello/src/handler.js:1:683)
    at /var/task/_optimize/service-name-dev-hello/src/handler.js:1:734
    at Object.308../hello (/var/task/_optimize/service-name-dev-hello/src/handler.js:86854:80)

It's probably that my configuration with serverless-plugin-optimize is incorrect. Will try to get it working.

Hope that's useful 😬