Kurt Peek
Kurt Peek

Reputation: 57541

"Serverless-offline: route not found." running an AWS Lambda function in offline mode

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).

Upvotes: 15

Views: 21313

Answers (4)

zub0r
zub0r

Reputation: 1379

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
    

Upvotes: 0

Karl Taylor
Karl Taylor

Reputation: 5279

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.

Upvotes: 21

Aaron Stuyvenberg
Aaron Stuyvenberg

Reputation: 3777

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

Upvotes: 12

ymaghzaz
ymaghzaz

Reputation: 84

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

Upvotes: 0

Related Questions