14

This question is pretty much the same as Serverless offline not getting route, but since that one was not answered I'm asking again. I'm trying to follow this article, https://medium.com/@awesome1888/how-to-use-serverless-locally-with-webpack-and-docker-5e268f71715, on how to deploy a Lambda function with Serverless.

I have a directory with the following structure:

> tree -I node_modules
.
├── package-lock.json
├── package.json
├── serverless.yml
├── src
│   ├── handler.js
│   └── index.js
└── webpack.config.js

where serverless.yml reads

service: my-first-lambda

plugins:
  - serverless-webpack
  - serverless-offline

provider:
  name: aws
  runtime: nodejs10.x
  region: us-east-1
  stage: dev

functions:
  hello:
    handler: src/handler.main
    events:
      - http:
        path: /hello
        method: any

custom:
  webpack:
    includeModules: true

src/index.js reads

import moment from 'moment';

const handler = async (event, context) => {
  const body = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(`Hello, this is your lambda speaking. Today is ${moment().format('dddd')}`)
    }, 2000);
  });
  return {
    statusCode: 200,
    body,
  };
}

export default handler;

src/handler.js reads

export { default as main } from './index';

and webpack.config.js reads

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

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  mode: slsw.lib.webpack.isLocal ? "development" : "production",
  externals: [nodeExternals()],
  output: {
    libraryTarget: "commonjs",
    path: path.join(__dirname, ".webpack"),
    filename: "[name].js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: "babel-loader",
            options: {
              presets: ["@babel/preset-env"],
              plugins: ["@babel/plugin-proposal-object-rest-spread"]
            }
          }
        ]
      }
    ]
  }
};

The problem is when I start up the function in offline mode, it seems to have only one very specific route:

> 
npx serverless offline start --region us-east-1 --noTimeout --port 3000 --host 0.0.0.0
Serverless: Bundling with Webpack...
Time: 1203ms
Built at: 08/30/2019 2:35:10 PM
         Asset      Size       Chunks             Chunk Names
src/handler.js  6.81 KiB  src/handler  [emitted]  src/handler
Entrypoint src/handler = src/handler.js
[./src/handler.js] 42 bytes {src/handler} [built]
[./src/index.js] 1.64 KiB {src/handler} [built]
[moment] external "moment" 42 bytes {src/handler} [built]
Serverless: Watching for changes...
Serverless: Starting Offline: dev/us-east-1.

Serverless: Routes for hello:
Serverless: POST /{apiVersion}/functions/my-first-lambda-dev-hello/invocations

Serverless: Offline [HTTP] listening on http://0.0.0.0:3000
Serverless: Enter "rp" to replay the last request

and if I go to http://localhost:3000/hello, I get this response:

{"statusCode":404,"error":"Serverless-offline: route not found.","currentRoute":"get - /hello","existingRoutes":["post - /{apiVersion}/functions/my-first-lambda-dev-hello/invocations"]}

Any idea why this is not working? (I've perused https://serverless.com/framework/docs/ but couldn't quickly find an answer).

Kurt Peek
  • 52,165
  • 91
  • 301
  • 526

4 Answers4

21

I had this issue and if anyone comes across it, this github comment fixed my issue.

You can run $ sls offline start --noPrependStageInUrl or add the following to your serverless.yml file

custom:
  serverless-offline:
    noPrependStageInUrl: true

According to the comment:

I had this problem with anything 6+, this was due to the fact that it now defaults to appending the staging name to the url path. To revert to the old way, you need to add --noPrependStageInUrl to the cli or in the serverless file custom: serverless-offline noPrependStageInUrl: true to revert to previous setting. I'm testing it his out but @dherault the functionality is not reflecting what is actually happening in AWS.

I was using serverless-offline: "6.7.0" and my index.handler was as below:

const serverless = require("serverless-http");
const express = require("express");
const app = express();

app.get("/", function (req, res) {
  res.send("Hello World!");
});

module.exports.handler = serverless(app);

And my serverless.yml

plugins:
  - serverless-offline

custom:
  serverless-offline:
    noPrependStageInUrl: true

provider:
  name: aws
  runtime: nodejs12.x
  stage: dev
  region: eu-west-2

functions:
  app:
    handler: src/index.handler
    events:
      - http: ANY /
      - http: "ANY {proxy+}"

Apologies this isn't exactly a great answer but hopefully, someone comes across this and it is a solution to their problem.

Karl Taylor
  • 4,839
  • 3
  • 34
  • 62
11

Looks like you've got a whitespace issue in your serverless.yml file.

Try indenting path and method under the http block:

functions:
  hello:
    handler: src/handler.main
    events:
      - http:
          path: /hello
          method: any
Aaron Stuyvenberg
  • 3,437
  • 1
  • 9
  • 21
0

for setup a quick example using serverless template :

sls create -h

output :

create ........................ Create new Serverless service
--template / -t .................... Template for the service. Available templates: "aws-clojure-gradle", "aws-clojurescript-gradle", "aws-nodejs", "aws-nodejs-typescript", "aws-alexa-typescript", "aws-nodejs-ecma-script", "aws-python", "aws-python3", "aws-groovy-gradle", "aws-java-maven", "aws-java-gradle", "aws-kotlin-jvm-maven", "aws-kotlin-jvm-gradle", "aws-kotlin-nodejs-gradle", "aws-scala-sbt", "aws-csharp", "aws-fsharp", "aws-go", "aws-go-dep", "aws-go-mod", "aws-ruby", "aws-provided", "azure-nodejs", "cloudflare-workers", "cloudflare-workers-enterprise", "cloudflare-workers-rust", "fn-nodejs", "fn-go", "google-nodejs", "google-python", "google-go", "kubeless-python", "kubeless-nodejs", "openwhisk-java-maven", "openwhisk-nodejs", "openwhisk-php", "openwhisk-python", "openwhisk-ruby", "openwhisk-swift", "spotinst-nodejs", "spotinst-python", "spotinst-ruby", "spotinst-java8", "plugin" and "hello-world"

step1: so for generated a new nodejs example with an api :

sls create -t aws-nodejs-ecma-script -n service-name-hello-world

step2: install serverless-offline :

npm install serverless-offline -D

step3: in serverless.yml

plugins:
  - serverless-webpack
  - serverless-offline

step4 : start local server

serverless offline start -r us-west-1 --stage dev

github Example

git clone https://github.com/ysfmag/aws-serverless-nodejs-example
cd aws-serverless-nodejs-example
yarn 
yarn start 

serverless.yml

to define an api in your serverless framework you need to respect yaml format , and in the path variable you dont need to start with '/hello' just 'hello' will work .

functions:
  hello:
  handler: src/handler.main
  events:
    - http:
        path: hello
        method: get
ymaghzaz
  • 84
  • 10
0

Just pointing out that methods above the path behave differently than other way around for all other methods except GET (facepalm):

  • route not found:

    - http:
        method: ANY
        path: /
    - http:
        method: ANY
        path: '{proxy+}'
    
  • working:

    - http:
        path: /
        method: ANY
    - http:
        path: '{proxy+}'
        method: ANY
    
zub0r
  • 1,299
  • 1
  • 14
  • 20