Serverless-webpack: Type Error - Path must be a string

Created on 2 Sep 2017  ·  32Comments  ·  Source: serverless-heaven/serverless-webpack

This is a Bug Report

Description

$ serverless package

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

  Path must be a string. Received { index: './index.js', data: './data.js' }

Additional Data

$ cat package.json | tail
    "cli"
  ],
  "author": "Adrian Sieber",
  "license": "ISC",
  "devDependencies": {
    "serverless": "^1.21.0",
    "serverless-webpack": "^2.2.2",
    "webpack": "^3.5.5"
  }
}
$ neofetch
                -/+:.           [email protected]
               :++++.           ------------------------
              /+++/.            OS: macOS Sierra 10.12.6 16G29 x86_64
      .:-::- .+/:-``.::-        Model: MacBookPro12,1
   .:/++++++/::::/++++++/:`     Kernel: 16.7.0
 .:///////////////////////:`    Uptime: 1 day, 10 hours, 8 mins
 ////////////////////////`      Packages: 930
-+++++++++++++++++++++++`       Shell: fish 2.6.0
/++++++++++++++++++++++/        Resolution: 2560x1440, 1680x1050, 1920x1080
/sssssssssssssssssssssss.       DE: Aqua
:ssssssssssssssssssssssss-      WM: Quartz Compositor
 osssssssssssssssssssssssso/`   WM Theme: Blue
 `syyyyyyyyyyyyyyyyyyyyyyyy+`   Terminal: iTerm2
  `ossssssssssssssssssssss/     CPU: Intel i5-5257U (4) @ 2.70GHz
    :ooooooooooooooooooo+.      GPU: Intel Iris Graphics 6100
     `:+oo+/:-..-:/+o+/-        Memory: 4385MiB / 8192MiB
help wanted

Most helpful comment

Hi @jnreynoso! What I see you are trying to do here is dynamic module loading, and with webpack you should avoid it as much as you can. If you are using serverless framework, your code should be atomic and service-based, because all those services (and AWS is the best example for this) have a bad cool startup times, and if you overcharge your services or do a monolyth, it can be a disaster in this aspect.

Javascript and node.js is going to static module loading, because of ES2015 imports and its implementation in node. Said that, webpack will appreciate you to use static moudle loading, and Models are one thing you should require as well.

Let's say you have two modules with association (sorry for ES2015 imports, you can replace they with explicit requires and export default by module.exports =):
_/app/models/message.js:_

import { DataTypes } from 'sequelize';
import sequelize from '../../utilities/database/mysql';


const Message = sequelize.define('Message', {
  title: DataTypes.STRING,
});

export default Message;

_/app/models/question.js:_

import { DataTypes } from 'sequelize';
import sequelize from '../utilities/database/mysql';
import Message from './message';


const Question = sequelize.define('Question', {
  title: DataTypes.STRING,
});

Question.hasMany(Message);
Message.belongsTo(Question);

export default Question;

As you can see, we avoid ciclic dependencies by adding all associations at the same file _question.js_. I'll explain you how to require them if you need those asociations but you only use the Message model. Also there's a require from sequelize. This import it's only an export of a sequelize instance with all the details to the database connection.

after that, we have the handlers for each function:
_/app/handlers/add-question.js:_

import Question from '../models/question';

module.exports.addQuestion =
  ({ body }, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    Question.create(body)
      .then(question => callback(null, {
        body: JSON.stringify(question),
      }));

_/app/handlers/add-message.js:_

import Message from '../models/message';
import '../models/question';

module.exports.addQuestion =
  ({ body, pathParameters }, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    body.QuestionId = pathParameters.id;
    return Message.create(body)
      .then(message => callback(null, {
        body: JSON.stringify(message),
      }));
  };

as you can see here, we import question to get the associations with Question and Message. As we are statically importing, we can't import Question inside Message module because we will end up with a ciclic import, and our program will fail. So that's the best way to solve this problem.

and finally, the _serverless.yml_ functions extract:

addQuestion:
  handler: app/handlers/add-question.addQuestion
  timeout: 10
  events:
    - http:
        path: questions
        method: post
        cors: true
addMessage:
  handler: app/handlers/add-message.addMessage
  timeout: 12
  events:
    - http:
        path: messages
        method: post
        cors: true

As you can see, we require each model, and only if we need it. If we do that, webpack will know how to pack your function and you will have at the end all the modules required inside your packed function.
If you pack individually, you'll endup with a tiny zip with only the required files, and your project will reduce the cool startup time and have a better impact in performance.

I know it's a bit frustrating at the begining, but you'll have a better, clean and mantainable code, and also working models in serverless. Also a good thing to keep in mind is the context.callbackWaitsForEmptyEventLoop = false; because without that, you can have a lot of problems and timeouts.

I haven't tested the code, so it's only a reference. If you need further assistance I'll be pleased to help you. Also, if you can't code that way, or you have other requirements, I can help you with those specific requirements. This is the way I do, but not the only way. As HyperBrain has said, you can include all the modules in webpack at the begining of your packaging and have all the models inside, but it's not a good idea in terms of mantainability and stability, I think.

Hope this helps!
Cheers.

All 32 comments

Hi @adius, thanks for submitting the issue 👍 . Can you post your webpack.conf.js and the function definitions from your serverless.yml? It seems that there is something wrong with the handler definition or the function handler definition in your project setup (or webpack configuration).

@adius Do you have any chance to provide the information I asked for - or did you solve the problem in the meantime?

@HyperBrain I encountered this issue using version 2.2.3 and entry: slsw.lib.entries. I didn't encounter this issue in 3.0.0. (Thanks for maintaining this project!)

@kevlened You're welcome 😄 . I'll close this issue as it seems to be solved with the latest version.

If anyone else has some further comments feel free to continue any discussion here.

Hi, my project is presenting a similar error:

  • [ 'TypeError: Path must be a string. Received undefined',

webpack configuration
```javascript
const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals');

module.exports = {
entry: slsw.lib.entries,
target: 'node',
externals: [nodeExternals()],
module: {
loaders: [{
test: /.js$/,
loaders: ['babel-loader'],
include: __dirname,
exclude: /node_modules/
}]
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, '.webpack'),
filename: '[name].js'
}
}
package.json json
{
"name": "serverless-api",
"version": "0.0.1",
"description": "serverless-api serverless api.",
"scripts": {
"start": "./node_modules/.bin/sls offline start",
"inspect": "babel-node --inspect /usr/local/bin/serverless offline start"
},
"dependencies": {
"babel-runtime": "^6.26.0",
"jsonwebtoken": "^7.4.1",
"options-resolver": "^1.0.1",
"serverless-webpack": "^3.1.0",
"sequelize": "^4.13.0",
"tedious": "^2.0.0"
},
"devDependencies": {
"axios": "^0.16.2",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.24.1",
"babel-preset-es2017": "^6.24.1",
"fs": "0.0.1-security",
"jwk-to-pem": "^1.2.6",
"serverless": "^1.23.0",
"serverless-aws-alias": "^0.5.1-alpha1",
"serverless-offline": "^3.16.0",
"webpack": "^3.6.0",
"webpack-node-externals": "^1.6.0"
}
}

and serverless.yml yaml
service: serverless-api

plugins:

  • serverless-webpack
  • serverless-offline

provider:
name: aws
region: us-east-1
runtime: nodejs6.10
stage: develop
environment: '${file(.env.yml):${self:custom.stage}}'

custom:
stage: '${opt:stage, self:provider.stage}'
serverless-offline:
port: 4000
babelOptions:
presets:
- es2015
- es2017
webpackIncludeModules: true

package:
exclude:
- .vscode/*
- docs/
*
- templates/*
- tests/
*
- .editorconfig
- .env.yml
- .env.yml.example
- .eslintrc.js

functions:
get-users:
name: serverless-api-get-users
description: Funcion de prueba
handler: functions/users/get.handler
events:
- http:
method: GET
path: users

resources:
Resources:
ApiGatewayRestApi:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Name: 'Serverless RESTful API (${self:custom.stage})'

````
I'd appreciate your help.

Hi @jnreynoso ,

can you run SLS_DEBUG=* node_modules/serverless/bin/serverless package --verbose and post the stacktrace?

```shell
➜ serverless-api git:(master) ✗ SLS_DEBUG=* node_modules/serverless/bin/serverless package --verbose
Serverless: Load command run
Serverless: Load command config
Serverless: Load command config:credentials
Serverless: Load command create
Serverless: Load command install
Serverless: Load command package
Serverless: Load command deploy
Serverless: Load command deploy:function
Serverless: Load command deploy:list
Serverless: Load command deploy:list:functions
Serverless: Load command invoke
Serverless: Load command invoke:local
Serverless: Load command info
Serverless: Load command logs
Serverless: Load command login
Serverless: Load command logout
Serverless: Load command metrics
Serverless: Load command remove
Serverless: Load command rollback
Serverless: Load command rollback:function
Serverless: Load command slstats
Serverless: Load command plugin
Serverless: Load command plugin
Serverless: Load command plugin:install
Serverless: Load command plugin
Serverless: Load command plugin:uninstall
Serverless: Load command plugin
Serverless: Load command plugin:list
Serverless: Load command plugin
Serverless: Load command plugin:search
Serverless: Load command emit
Serverless: Load command config
Serverless: Load command config:credentials
Serverless: Load command rollback
Serverless: Load command rollback:function
Serverless: Load command webpack
Serverless: Load command webpack:invoke
Serverless: Load command webpack:watch
Serverless: Load command webpack:serve
Serverless: WARNING: Plugin ServerlessWebpack uses deprecated hook before:deploy:createDeploymentArtifacts,
use package:createDeploymentArtifacts hook instead
Serverless: WARNING: Plugin ServerlessWebpack uses deprecated hook after:deploy:createDeploymentArtifacts,
use package:createDeploymentArtifacts hook instead
Serverless: Load command offline
Serverless: Load command offline:start
Serverless: Invoke package
Serverless: Invoke aws:common:validate
Serverless: Invoke aws:common:cleanupTempDir
Serverless: Bundling with Webpack...
Time: 455ms
Asset Size Chunks Chunk Names
functions/users/get.js 7.65 kB 0 [emitted] functions/users/get
[0] ./functions/users/get.js 587 bytes {0} [built]
[1] ./models/index.js 1.16 kB {0} [built]
[2] (webpack)/buildin/module.js 517 bytes {0} [built]
[3] external "fs" 42 bytes {0} [not cacheable]
[4] external "path" 42 bytes {0} [not cacheable]
[5] external "sequelize" 42 bytes {0} [not cacheable]
[6] ./helpers/response.js 2.21 kB {0} [built]
Serverless: Fetch dependency graph from /home/jnreynoso/Coders/inticode/serverless/serverless-api/package.json
Serverless: Packing external modules: sequelize@^4.13.0
Serverless: Package took [10079 ms]
Serverless: Copy modules: /home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service [268 ms]
Serverless: Prune: /home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service [325 ms]
Serverless: Zip service: /home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service [758 ms]
Serverless: Packaging service...
Serverless: Remove /home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack
Serverless: Invoke aws:package:finalize
Serverless: Invoke aws:common:moveArtifactsToPackage

````

That's strange. The package did not lead to a crash, so it seems that the error happens during Serverless' deploy phase - so the plugin's packaging went through.

Can you do the same with a deploy command instead of package? Maybe it's something within Serverless itself.

BTW: serverless-webpack should be in devDependencies, not dependencies as it is only used during build time.

then how would you do to work with babel in production? without serverless-webpack?

serverless-webpack is only the transpile/package plugin that invokes webpack accordingly and packages the artifact that is uploaded to AWS. You only need babel-runtime in the dependencies, as the transpiled code is completely self-contained. serverless-webpack is not used in the compiled code, just needed to get it compiled and packed properly.

Is it just babel-runtime needed?, or do I have to prepare my compiled version to deploy(with serverless-webpack), i'm new to this, i'd appreciate your help.

All serverless plugins belong to devDependencies (as well as serverless). They make up the build system. So you'll do a npm install on your build machine, so that everything from dependencies and devDependencies gets installed.
As soon as you then do a serverless deploy --stage=<mystage> the webpack plugin will take care that the code is compiled and only production dependencies are packaged into the zip files that are uploaded to AWS (the Lambda function code).
In general "dependencies" is everything that is needed by your code to run (in your example sequelize, tedious, etc.) and "devDependencies" is everything that is needed to build or debug (e.g. serverless, serverless-webpack, serverless-offline, babel-loader, etc).

thank you very much @HyperBrain!, and with respect to Type Error - Path must be a string, What could I do?

Does it happen with serverless deploy or serverless offline? In the first case please use SLS_DEBUG=* ... deploy --verbose to get more information about the crash.

with serverless offline, when I remove the serverless-webpack(serverless.yml) and only use serverless-offline, everything works correctly, but with serverless-webpack no.
shell Serverless: GET /users/jean (λ: get-users) Serverless: The first request might take a few extra seconds [offline] requestId: 051800779008461584 [offline] contentType: application/json [offline] requestTemplate: [offline] payload: null [offline] Invalidating cache... [offline] Loading handler... (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get) Serverless: Error while loading get-users [ 'TypeError: Path must be a string. Received undefined', 'at assertPath (path.js:7:11)', 'at Object.basename (path.js:1355:5)', 'at Object.<anonymous> (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:103:21)', 'at Object.module.exports.module.deprecate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:138:30)', 'at __webpack_require__ (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:20:30)', 'at Object.<anonymous> (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:73:15)', 'at __webpack_require__ (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:20:30)', 'at /home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:63:18', 'at Object.<anonymous> (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:1:68)', 'at Module._compile (module.js:570:32)', 'at loader (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/babel-register/lib/node.js:144:5)', 'at Object.require.extensions.(anonymous function) [as .js] (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/babel-register/lib/node.js:154:7)', 'at Module.load (module.js:487:32)', 'at tryModuleLoad (module.js:446:12)', 'at Function.Module._load (module.js:438:3)', 'at Module.require (module.js:497:17)', 'at require (internal/module.js:20:19)', 'at Object.createHandler (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/serverless-offline/src/functionHelper.js:35:21)', 'at handler (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/serverless-offline/src/index.js:499:40)', 'at Object.internals.handler (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/handler.js:96:36)', 'at request._protect.run (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/handler.js:30:23)', 'at internals.Protect.run (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/protect.js:64:5)', 'at exports.execute (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/handler.js:24:22)', 'at each (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:384:16)', 'at iterate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:36:13)', 'at done (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:28:25)', 'at internals.Auth._authenticate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/auth.js:210:16)', 'at internals.Auth.authenticate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/auth.js:202:17)', 'at each (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:384:16)', 'at iterate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:36:13)', 'at done (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:28:25)', 'at internals.state (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/route.js:357:16)', 'at each (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:384:16)', 'at iterate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:36:13)', 'at Object.exports.serial (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:39:9)', 'at internals.Request._lifecycle (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:387:11)', 'at internals.Request._execute (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:302:21)', 'at Domain.request._protect.enter (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/connection.js:261:25)', 'at Domain.run (domain.js:221:14)', 'at internals.Protect.enter (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/protect.js:80:17)', 'at Server.<anonymous> (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/connection.js:259:30)', 'at emitTwo (events.js:106:13)', 'at Server.emit (events.js:191:7)', 'at HTTPParser.parserOnIncoming [as onIncoming] (_http_server.js:546:12)' ]

You should check functions/users/get.js:103:21 and log the parameter(s) of the function that is called there. It seems that it is not or wrongly set.
Maybe it is a Lambda environment variable that you've set that is not evaluated correctly in offline mode.
If that's the case (e.g. if you use ${cf:xxxxx}) you can use the serverless-export-env plugin to overcome the issue.

this line is generating the error var basename = path.basename(module.filename);
modes/index.js (get.js:103)
````javascript
'use strict';

var fs = require('fs');
var path = require('path');
var Sequelize = require('sequelize');
var env = process.env.NODE_ENV || 'develop';
var db = {};

console.log("====================================================================================================")
console.log("====================================================================================================")

var basename = path.basename(module.filename);

var sequelize = new Sequelize(
process.env.DATABASE_NAME,
process.env.DATABASE_USERNAME,
process.env.DATABASE_PASSWORD,
{
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
dialect: process.env.DATABASE_DIALECT,
// this option tells sequelize to store
// and parse all dates values in PERU TIMEZONE,
// otherwhise it will store dates in UTC
// allow multiple statements in a single query
dialectOptions: {
multipleStatements: true,
useUTC: false
}
}
)

fs.readdirSync(__dirname).filter(function (file) {
return file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js';
}).forEach(function (file) {
var model = sequelize'import';
db[model.name] = model;
});

Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

````

when i modified the routine so that it no longer uses basename
javascript fs.readdirSync(__dirname).filter(function (file) { return (file.indexOf(".") !== 0) && (file !== "index.js") })
I got this new error
javascript Serverless: Error while loading get-users [ 'Error: Cannot find module \'/bin\'', 'at Function.Module._resolveFilename (module.js:469:15)', 'at Function.Module._load (module.js:417:25)', 'at Module.require (module.js:497:17)', 'at require (internal/module.js:20:19)', 'at Sequelize.import (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/sequelize/lib/sequelize.js:387:62)', 'at /home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:123:15', 'at Array.forEach (native)', 'at Object.<anonymous> (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:122:4)', 'at Object.<anonymous> (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:137:30)', 'at __webpack_require__ (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:20:30)', 'at Object.<anonymous> (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:73:15)', 'at __webpack_require__ (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:20:30)', 'at /home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:63:18', 'at Object.<anonymous> (/home/jnreynoso/Coders/inticode/serverless/serverless-api/.webpack/service/functions/users/get.js:1:68)', 'at Module._compile (module.js:570:32)', 'at loader (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/babel-register/lib/node.js:144:5)', 'at Object.require.extensions.(anonymous function) [as .js] (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/babel-register/lib/node.js:154:7)', 'at Module.load (module.js:487:32)', 'at tryModuleLoad (module.js:446:12)', 'at Function.Module._load (module.js:438:3)', 'at Module.require (module.js:497:17)', 'at require (internal/module.js:20:19)', 'at Object.createHandler (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/serverless-offline/src/functionHelper.js:35:21)', 'at handler (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/serverless-offline/src/index.js:499:40)', 'at Object.internals.handler (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/handler.js:96:36)', 'at request._protect.run (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/handler.js:30:23)', 'at internals.Protect.run (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/protect.js:64:5)', 'at exports.execute (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/handler.js:24:22)', 'at each (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:384:16)', 'at iterate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:36:13)', 'at done (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:28:25)', 'at internals.Auth._authenticate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/auth.js:210:16)', 'at internals.Auth.authenticate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/auth.js:202:17)', 'at each (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:384:16)', 'at iterate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:36:13)', 'at done (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:28:25)', 'at internals.state (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/route.js:357:16)', 'at each (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:384:16)', 'at iterate (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:36:13)', 'at Object.exports.serial (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/items/lib/index.js:39:9)', 'at internals.Request._lifecycle (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:387:11)', 'at internals.Request._execute (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/request.js:302:21)', 'at Domain.request._protect.enter (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/connection.js:261:25)', 'at Domain.run (domain.js:221:14)', 'at internals.Protect.enter (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/protect.js:80:17)', 'at Server.<anonymous> (/home/jnreynoso/Coders/inticode/serverless/serverless-api/node_modules/hapi/lib/connection.js:259:30)', 'at emitTwo (events.js:106:13)', 'at Server.emit (events.js:191:7)', 'at HTTPParser.parserOnIncoming [as onIncoming] (_http_server.js:546:12)' ] Serverless: Replying error in handler

I think webpack compiled everything together, so there is no module.filename anymore.
As a solution you should use the Copy Webpack Plugin to copy the model files plain to your output and just reference them by a hard-coded directory. Webpack does not know that you try to load the model files manually.

So, let the copy webpack plugin copy your files to "./models" and use fs.readdirSync(path.join(__dirname, '..', '..', 'models')) without a filter to retrieve the model files. Another option would be to "require" the models explicitly: then webpack will bundle them into the package and you'll need no fs lookup at all.

Which DB do you use with sequelize? You have to add that to your dependencies as well and include it in your serverless.yml as:
```
custom:
webpackIncludeModules:
forceInclude:
- the_DB_module_needed

I just did it I still get the same error('Error: Cannot find module \'/bin\'',)

Hi @ahomatthew, @jpicornell,
in the issues you discussed, you used sequelize in combination with the plugin. Would you mind to help out here (lower half of this issue) and show a "good" way how to put sequelize, models and the db module correctly into a project? Thank you very much 😃

@jnreynoso Just pinged some people who might have the right solution at hand faster than I do.

thank you very much @HyperBrain, I await your response.

EDIT: for some reason webpack recognize __dirname with root ('/'), I had to add a SEQUELIZE_MODELS variable for everything to work properly
yaml develop: .... ... .. . SEQUELIZE_MODELS: "/home/jnreynoso/Coders/inticode/serverless/serverless-api/models"
````javascript
fs.readdirSync(process.env.SEQUELIZE_MODELS).filter(function (file) {
console.log("====================================")
console.log(file)
return (file.indexOf(".") !== 0) && (file !== "index.js")
}).forEach(function (file) {
var model = sequelize'import';
db[model.name] = model;
});

Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
``` make these changes and work, but I do not think it's a good idea to set the full path inSEQUELIZE_MODELS`, webpack mock __dirname

I made this hack and updated my webpack config and everything worked correctly

javascript // Webpack Hack var dirname=path.join(__dirname.replace('/.webpack/service/functions/users',''),'models')
````javascript
const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals');

module.exports = {
entry: slsw.lib.entries,
target: 'node',
node: {
console: false,
global: false,
process: false,
__filename: false,
__dirname: false,
Buffer: false,
setImmediate: false
},
externals: [nodeExternals()],
module: {
loaders: [{
test: /.js$/,
loaders: ['babel-loader'],
include: __dirname,
exclude: /node_modules/
}]
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, '.webpack'),
filename: '[name].js'
}
}
````

@hyperbrain, @jnreynoso, I can post a working example of what I've built tomorrow or Wednesday. Currently a little OOC.

I would really appreciate it @ahomatthew

Hi @jnreynoso! What I see you are trying to do here is dynamic module loading, and with webpack you should avoid it as much as you can. If you are using serverless framework, your code should be atomic and service-based, because all those services (and AWS is the best example for this) have a bad cool startup times, and if you overcharge your services or do a monolyth, it can be a disaster in this aspect.

Javascript and node.js is going to static module loading, because of ES2015 imports and its implementation in node. Said that, webpack will appreciate you to use static moudle loading, and Models are one thing you should require as well.

Let's say you have two modules with association (sorry for ES2015 imports, you can replace they with explicit requires and export default by module.exports =):
_/app/models/message.js:_

import { DataTypes } from 'sequelize';
import sequelize from '../../utilities/database/mysql';


const Message = sequelize.define('Message', {
  title: DataTypes.STRING,
});

export default Message;

_/app/models/question.js:_

import { DataTypes } from 'sequelize';
import sequelize from '../utilities/database/mysql';
import Message from './message';


const Question = sequelize.define('Question', {
  title: DataTypes.STRING,
});

Question.hasMany(Message);
Message.belongsTo(Question);

export default Question;

As you can see, we avoid ciclic dependencies by adding all associations at the same file _question.js_. I'll explain you how to require them if you need those asociations but you only use the Message model. Also there's a require from sequelize. This import it's only an export of a sequelize instance with all the details to the database connection.

after that, we have the handlers for each function:
_/app/handlers/add-question.js:_

import Question from '../models/question';

module.exports.addQuestion =
  ({ body }, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    Question.create(body)
      .then(question => callback(null, {
        body: JSON.stringify(question),
      }));

_/app/handlers/add-message.js:_

import Message from '../models/message';
import '../models/question';

module.exports.addQuestion =
  ({ body, pathParameters }, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    body.QuestionId = pathParameters.id;
    return Message.create(body)
      .then(message => callback(null, {
        body: JSON.stringify(message),
      }));
  };

as you can see here, we import question to get the associations with Question and Message. As we are statically importing, we can't import Question inside Message module because we will end up with a ciclic import, and our program will fail. So that's the best way to solve this problem.

and finally, the _serverless.yml_ functions extract:

addQuestion:
  handler: app/handlers/add-question.addQuestion
  timeout: 10
  events:
    - http:
        path: questions
        method: post
        cors: true
addMessage:
  handler: app/handlers/add-message.addMessage
  timeout: 12
  events:
    - http:
        path: messages
        method: post
        cors: true

As you can see, we require each model, and only if we need it. If we do that, webpack will know how to pack your function and you will have at the end all the modules required inside your packed function.
If you pack individually, you'll endup with a tiny zip with only the required files, and your project will reduce the cool startup time and have a better impact in performance.

I know it's a bit frustrating at the begining, but you'll have a better, clean and mantainable code, and also working models in serverless. Also a good thing to keep in mind is the context.callbackWaitsForEmptyEventLoop = false; because without that, you can have a lot of problems and timeouts.

I haven't tested the code, so it's only a reference. If you need further assistance I'll be pleased to help you. Also, if you can't code that way, or you have other requirements, I can help you with those specific requirements. This is the way I do, but not the only way. As HyperBrain has said, you can include all the modules in webpack at the begining of your packaging and have all the models inside, but it's not a good idea in terms of mantainability and stability, I think.

Hope this helps!
Cheers.

Hello @jpicornell @HyperBrain @jnreynoso @ahomatthew than you for your answer
This issue is really fantastic, it has helped me a lot to solve some problems I had with sequelize and serverless.

I would like to resolve a question: it's necessary to use sequelize.close () in each function?
What is the better solution?
@jpicornell

Hello @mizhac . It's not necessary to close the connection if you specify context.callbackWaitsForEmptyEventLoop = false; at the begining of your function. This makes serverless not to wait until every event loop is finished, and it will be leaved opened until the aws system reuses the node process (and you will have faster invokation times) or drops the node process. There's no specific reuse time, but it's useful when you have concurrent api calls.

To take advantage to this, you should always declare your connections outside the function, like this:

import Sequelize from 'sequelize';
const sequelize = new Sequelize(/*your connection details*/);

module.exports.addQuestion =
  ({ body, pathParameters }, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    // do your sequelize code here
    PromiseFulfilled.then(() => callback(null, { body: "{}" }));
  };

many thanks @jpicornell , I already have it clear, implement my project following the guidelines you gave and everything is working well!

In my case, I updated serverless and it worked.

Was this page helpful?
0 / 5 - 0 ratings