Skip to content

Commit df00dec

Browse files
committed
v0.1.0
1 parent 5a6054c commit df00dec

File tree

3 files changed

+121
-48
lines changed

3 files changed

+121
-48
lines changed

README.md

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,110 @@
1-
# serverless-offline
1+
# Serverless Offline Plugin
2+
3+
When developing with Serverless deploying functions to AWS after each change might be annoying. This plugin allows you to simulate API Gateway locally, so all function calls can be done on localhost.
4+
5+
### Differences with Serverless-serve-plugin
6+
7+
See 'Credits and inspiration'.
8+
9+
### Requierements
10+
11+
Node v4 and over.
12+
13+
### Installation
14+
15+
In your Serverless project:
16+
17+
```
18+
npm install serverless-offline
19+
```
20+
21+
Then in `s-project.json` add following entry to the plugins array: `serverless-offline`
22+
23+
Like this:
24+
```
25+
"plugins": ["serverless-offline"]
26+
```
27+
28+
And in main project root do:
29+
30+
```
31+
sls offline start
32+
```
33+
34+
### Command line options
35+
36+
`--prefix` `-p`: Add prefix to the URLs, so your clients will not use `http://localhost:3000/` but `http://localhost:3000/prefix/` instead. Default: empty
37+
38+
`--port` `-P`: Port to listen on. Default: `3000`
39+
40+
41+
### Usage
42+
43+
Just send your requests to `http://localhost:3000/` as it would be API Gateway.
44+
45+
Using this plugin with [Nodemon](https://github.com/remy/nodemon) is advised to reload your local code after every change.
46+
47+
### Usage with Babel
48+
49+
Optionaly, your handlers can be required with `babel-register`.
50+
To do so, in your `s-project.json` file, set options to be passed to babel-register like this:
51+
```
52+
{
53+
/* ... */
54+
"custom": {
55+
"serverless-offline": {
56+
"babelOptions": {
57+
/* Your own options, example: */
58+
presets: ["es2015", "stage-2"]
59+
}
60+
}
61+
},
62+
"plugins": ["serverless-offline", /* ... */]
63+
}
64+
```
65+
To view the full list of babel-register options, click [here](https://babeljs.io/docs/usage/require/)
66+
67+
### Simulation quality
68+
69+
This plugin simulates API Gateway for many practical purposes, good enough for development - but is not a perfect simulator. Specifically, Lambda currently runs on Node v0.10.13, whereas *offline* runs on your own runtime where no timeout or memory limits are enforced. Mapping templates are not simulated, so are security checks. You will probably find other differences.
70+
71+
### Credits and inspiration
72+
73+
This plugin is a fork of [Nopik](https://github.com/Nopik/)'s [Serverless-serve](https://github.com/Nopik/serverless-serve), the differences are:
74+
75+
- Under the hood, *Serve* uses Express, *Offline* uses Hapi.
76+
- *Serve*'s `event` object (passed to your handlers) is undocumented ad often empty. *Offline*'s `event` object is defined by: `Object.assign({ isServerlessOffline: true }, request);` where `request` is [Hapi's request object](http://hapijs.com/api#request-object). This allows you to quickly access properties like the request's params or payload in your lambda handler:
77+
```javascript
78+
module.exports.handler = function(event, context) {
79+
+ var params;
80+
+
81+
+ if (event.isServerlessOffline) { // Offline
82+
+ params = event.params;
83+
+ } else { // Online
84+
+ /* Define your event object using a template in your s-function.json file */
85+
+ params = event.customKeyDefinedInTemplate;
86+
+ }
87+
+};
88+
```
89+
- *Serve* will pick the first non `default` response of an endpoint if `errorPattern` is undefined. Doing so, it neglects the `default` answer (so it does not work out of the box with `serverless project create`). This causes new projects to answer 400 using *Serve*.
90+
Example :
91+
```javascript
92+
"responses": {
93+
"400": {
94+
"statusCode": "400" // errorPattern is undefined: Serve will always answer 400
95+
},
96+
"default": {
97+
"statusCode": "200",
98+
"responseParameters": {},
99+
"responseModels": {},
100+
"responseTemplates": {
101+
"application/json": ""
102+
}
103+
}
104+
}
105+
```
106+
- *Offline* dropped support for *Serve*'s optional init script for now.
107+
108+
### Licence
109+
110+
MIT

index.js

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = function(ServerlessPlugin, serverlessPath) {
55
const path = require('path');
66
const context = require(path.join(serverlessPath, 'utils', 'context'));
77
const SCli = require(path.join(serverlessPath, 'utils', 'cli'));
8+
const SUtils = require(path.join(serverlessPath, 'utils'));
89
const Hapi = require('hapi');
910

1011
return class Offline extends ServerlessPlugin {
@@ -49,49 +50,18 @@ module.exports = function(ServerlessPlugin, serverlessPath) {
4950
port: this.port
5051
});
5152

52-
let prefix = this.evt.prefix || '';
53-
// if( !this.evt.prefix ){
54-
// this.evt.prefix = "";
55-
// }
53+
let prefix = this.evt.prefix || '/';
5654

57-
if (prefix && prefix.endsWith('/')) prefix = prefix.slice(0, -1);
55+
if (!prefix.startsWith('/')) prefix = '/' + prefix;
56+
if (!prefix.endsWith('/')) prefix += '/';
5857

5958
this.prefix = prefix;
6059

61-
// if( (this.evt.prefix.length > 0) && (this.evt.prefix[this.evt.prefix.length-1] != '/') ) {
62-
// this.evt.prefix = this.evt.prefix + "/";
63-
// }
64-
65-
// this.app.get( '/__quit', function(req, res, next){
66-
// SCli.log('Quit request received, quitting.');
67-
// res.send({ok: true});
68-
// _this.server.close();
69-
// });
70-
71-
// this.app.use( function(req, res, next) {
72-
// res.header('Access-Control-Allow-Origin', '*');
73-
// next();
74-
// });
75-
76-
// this.app.use(bodyParser.json({ limit: '5mb' }));
77-
78-
// this.app.use( function(req, res, next){
79-
// res.header( 'Access-Control-Allow-Methods', 'GET,PUT,HEAD,PATCH,POST,DELETE,OPTIONS' );
80-
// res.header( 'Access-Control-Allow-Headers', 'Authorization,Content-Type,x-amz-date,x-amz-security-token' );
81-
82-
// if( req.method != 'OPTIONS' ) {
83-
// next();
84-
// } else {
85-
// res.status(200).end();
86-
// }
87-
// });
88-
8960
return Promise.resolve();
9061
}
9162

9263
_registerLambdas() {
9364
const functions = this.S.state.getFunctions();
94-
const handlers = {};
9565

9666
functions.forEach(fun => {
9767

@@ -100,23 +70,18 @@ module.exports = function(ServerlessPlugin, serverlessPath) {
10070
const handlerParts = fun.handler.split('/').pop().split('.');
10171
const handlerPath = path.join(fun._config.fullPath, handlerParts[0] + '.js');
10272

103-
handlers[fun.handler] = {
104-
path: handlerPath,
105-
handler: handlerParts[1],
106-
definition: fun
107-
};
10873

10974
fun.endpoints.forEach(endpoint => {
11075
// const { method, path } = endpoint;
11176
const method = endpoint.method;
112-
const path = endpoint.path;
113-
const finalPath = this.prefix + (path.startsWith('/') ? path : '/' + path);
77+
const epath = endpoint.path;
78+
const path = this.prefix + (epath.startsWith('/') ? epath.slice(1) : epath);
11479

115-
if(process.env.DEBUG) SCli.log(`Route: ${method} ${finalPath}`);
80+
if(process.env.DEBUG) SCli.log(`Route: ${method} ${path}`);
11681

11782
this.server.route({
11883
method,
119-
path: finalPath,
84+
path,
12085
config: { cors: true },
12186
handler: (request, reply) => {
12287
SCli.log(`Serving: ${method} ${request.url.path}`);
@@ -131,7 +96,7 @@ module.exports = function(ServerlessPlugin, serverlessPath) {
13196
throw err ;
13297
}
13398

134-
const event = {};
99+
const event = Object.assign({ isServerlessOffline: true }, request);
135100

136101
handler(event, context(fun.name, (err, result) => {
137102
let finalResponse;

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "serverless-offline",
3-
"version": "0.0.0",
3+
"version": "0.1.0",
44
"description": "Simulates your API Gateway ressources locally to call your lambda functions offline",
55
"main": "index.js",
66
"scripts": {
@@ -23,7 +23,6 @@
2323
"homepage": "https://github.com/dherault/serverless-offline#readme",
2424
"dependencies": {
2525
"babel-register": "^6.5.2",
26-
"hapi": "^13.0.0",
27-
"velocity": "^0.7.2"
26+
"hapi": "^13.0.0"
2827
}
2928
}

0 commit comments

Comments
 (0)