Skip to content

Commit 5e9c694

Browse files
authored
Merge branch 'master' into add-tests
2 parents c062373 + 69f8110 commit 5e9c694

File tree

7 files changed

+131
-39
lines changed

7 files changed

+131
-39
lines changed

.github/stale.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ exemptProjects: false
2020
exemptMilestones: false
2121

2222
# Label to use when marking as stale
23-
staleLabel: wontfix
23+
staleLabel: stale
2424

2525
# Comment to post when marking as stale. Set to `false` to disable
2626
markComment: >

README.md

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,55 @@
1-
## Netlify Lambda CLI
1+
## Netlify Lambda
22

3-
This is a small CLI tool that helps with building or serving lambdas built with a simple webpack/babel setup.
3+
This is an optional tool that helps with building or locally developing [Netlify Functions](https://www.netlify.com/docs/functions/) with a simple webpack/babel build step.
44

5-
The goal is to make it easy to work with Lambda's with modern ES6 without being dependent on having the most state of the art node runtime available in the final deployment environment and with a build that can compile all modules into a single lambda file.
6-
7-
Since v1.0.0 the dependencies were upgraded to Webpack 4 and Babel 7.
5+
The goal is to make it easy to write Lambda's with transpiled JS/Typescipt features and imported modules.
86

97
## Installation
108

11-
**We recommend installing locally** rather than globally: `yarn add -D netlify-lambda`. This will ensure your build scripts don't assume a global install which is better for your CI/CD (for example with Netlify's buildbot).
9+
**We recommend installing locally** rather than globally:
10+
11+
```bash
12+
yarn add -D netlify-lambda
13+
```
14+
15+
This will ensure your build scripts don't assume a global install which is better for your CI/CD (for example with Netlify's buildbot).
16+
17+
If you don't have a [`netlify.toml`](https://www.netlify.com/docs/netlify-toml-reference/) file, you'll need one ([example](https://github.com/netlify/create-react-app-lambda/blob/master/netlify.toml)). Define the `functions` field where the functions will be built to and served from, e.g.
18+
19+
```toml
20+
# example netlify.toml
21+
[build]
22+
command = "yarn build"
23+
functions = "lambda" # netlify-lambda reads this
24+
publish = "build"
25+
```
1226

1327
## Usage
1428

15-
Netlify lambda installs two commands:
29+
We expose two commands:
1630

1731
```
1832
netlify-lambda serve <folder>
1933
netlify-lambda build <folder>
2034
```
2135

22-
**IMPORTANT**: Both commands depend on a `netlify.toml` file being present in your project and configuring functions for deployment.
36+
At a high level, `netlify-lambda` takes a source folder (e.g. `src/lambda`, specified in your command) and outputs it to a built folder, (e.g. `built-lambda`, specified in your `netlify.toml` file).
2337

24-
The `serve` function will start a dev server and a file watcher for the specified folder and route requests to the relevant function at:
38+
The `build` function will run a single build of the functions in the folder.
39+
40+
The `serve` function will start a dev server for the source folder and route requests with a `.netlify/functions/` prefix, with a default port of `9000`:
2541

2642
```
27-
http://localhost:9000/hello -> folder/hello.js (must export a handler(event, context callback) function)
43+
folder/hello.js -> http://localhost:9000/.netlify/functions/hello
2844
```
2945

30-
The `build` function will run a single build of the functions in the folder.
46+
It also watches your files and restarts the dev server on change. Note: if you add a new file you should kill and restart the process to pick up the new file.
3147

32-
There are additional options, introduced later:
33-
```bash
34-
-h --help
35-
-c --config
36-
-p --port
37-
-s --static
38-
```
48+
**IMPORTANT**:
49+
50+
- You need a [`netlify.toml`](https://www.netlify.com/docs/netlify-toml-reference/) file with a `functions` field.
51+
- Every function needs to be a top-level js/ts/mjs file. You can have subfolders inside the `netlify-lambda` folder, but those are only for supporting files to be imported by your top level function.
52+
- Function signatures follow the [AWS event handler](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html) syntax but must be named `handler`. [We use Node v8](https://www.netlify.com/blog/2018/04/03/node.js-8.10-now-available-in-netlify-functions/) so `async` functions **are** supported ([beware common mistakes](https://serverless.com/blog/common-node8-mistakes-in-lambda/)!). Read [Netlify Functions docs](https://www.netlify.com/docs/functions/#javascript-lambda-functions) for more info.
3953

4054
## Using with `create-react-app`, Gatsby, and other development servers
4155

@@ -53,9 +67,10 @@ When your function is deployed on Netlify, it will be available at `/.netlify/fu
5367

5468
Say you are running `webpack-serve` on port 8080 and `netlify-lambda serve` on port 9000. Mounting `localhost:9000` to `/.netlify/functions/` on your `webpack-serve` server (`localhost:8080/.netlify/functions/`) will closely replicate what the final production environment will look like during development, and will allow you to assume the same function url path in development and in production.
5569

56-
- If you are using with `create-react-app`, see [netlify/create-react-app-lambda](https://github.com/netlify/create-react-app-lambda/blob/f0e94f1d5a42992a2b894bfeae5b8c039a177dd9/src/setupProxy.js) for an example of how to do this with `create-react-app`. [setupProxy is partially documented in the CRA docs](https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development#configuring-the-proxy-manually).
57-
- If you are using Gatsby, see [their Advanced Proxying docs](https://www.gatsbyjs.org/docs/api-proxy/#advanced-proxying). This is implemented in the [JAMstack Hackathon Starter](https://github.com/sw-yx/jamstack-hackathon-starter).
70+
- If you are using with `create-react-app`, see [netlify/create-react-app-lambda](https://github.com/netlify/create-react-app-lambda/blob/f0e94f1d5a42992a2b894bfeae5b8c039a177dd9/src/setupProxy.js) for an example of how to do this with `create-react-app`. [setupProxy is partially documented in the CRA docs](https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development#configuring-the-proxy-manually). You can also learn how to do this from scratch in a video: https://www.youtube.com/watch?v=3ldSM98nCHI
71+
- If you are using Gatsby, see [their Advanced Proxying docs](https://www.gatsbyjs.org/docs/api-proxy/#advanced-proxying). This is implemented in the [JAMstack Hackathon Starter](https://github.com/sw-yx/jamstack-hackathon-starter), and here is an accompanying blogpost: [Turning the Static Dynamic: Gatsby + Netlify Functions + Netlify Identity](https://www.gatsbyjs.org/blog/2018-12-17-turning-the-static-dynamic/).
5872
- If you are using Next.js, see [this issue for how to proxy](https://github.com/netlify/netlify-lambda/pull/28#issuecomment-439675503).
73+
- If you are using Vue CLI, you may just use https://github.com/netlify/vue-cli-plugin-netlify-lambda/.
5974
- If you are using with Angular CLI, see the instructions below.
6075

6176
[Example webpack config](https://github.com/imorente/netlify-functions-example/blob/master/webpack.development.config):
@@ -74,8 +89,6 @@ module.exports = {
7489
};
7590
```
7691

77-
The serving port can be changed with the `-p`/`--port` option.
78-
7992
<details>
8093
<summary>
8194
**Using with `Angular CLI`**
@@ -137,7 +150,9 @@ The additional webpack config will be merged into the default config via [webpac
137150

138151
The default webpack configuration uses `babel-loader` with a [few basic settings](https://github.com/netlify/netlify-lambda/blob/master/lib/build.js#L19-L33).
139152

140-
However, if any `.babelrc` is found in the directory `netlify-lambda` is run from, it will be used instead of the default one. If you need to run different babel versions for your lambda and for your app, [check this issue](https://github.com/netlify/netlify-lambda/issues/34) to override your webpack babel-loader.
153+
However, if any `.babelrc` is found in the directory `netlify-lambda` is run from, or [folders above it](https://github.com/netlify/netlify-lambda/pull/92) (useful for monorepos), it will be used instead of the default one.
154+
155+
If you need to run different babel versions for your lambda and for your app, [check this issue](https://github.com/netlify/netlify-lambda/issues/34) to override your webpack babel-loader.
141156

142157
### Use with TypeScript
143158

@@ -176,6 +191,23 @@ You may also want to add `typescript @types/node @types/aws-lambda`.
176191

177192
3. (Optional) if you have `@types/aws-lambda` installed, your lambda functions can use the community typings for `Handler, Context, Callback`. See the typescript instructions in [create-react-app-lambda](https://github.com/netlify/create-react-app-lambda/blob/master/README.md#typescript) for an example.
178193

194+
Check https://github.com/sw-yx/create-react-app-lambda-typescript for a CRA + Lambda full Typescript experience.
195+
196+
## CLI flags/options
197+
198+
There are additional CLI options:
199+
200+
```bash
201+
-h --help
202+
-c --config
203+
-p --port
204+
-s --static
205+
```
206+
207+
### --port option
208+
209+
The serving port can be changed with the `-p`/`--port` option.
210+
179211
### --static option
180212

181213
If you need an escape hatch and are building your lambda in some way that is incompatible with our build process, you can skip the build with the `-s` or `--static` flag. [More info here](https://github.com/netlify/netlify-lambda/pull/62).
@@ -193,15 +225,38 @@ Don't forget to search our issues in case someone has run into a similar problem
193225

194226
## Netlify Identity
195227

196-
Netlify Identity is [not supported at the moment](https://github.com/netlify/netlify-lambda/issues/51) inside `netlify-lambda` function emulation, but for now you can [read the docs](https://www.netlify.com/docs/functions/#identity-and-functions) on how they should work.
228+
Make sure to [read the docs](https://www.netlify.com/docs/functions/#identity-and-functions) on how Netlify Functions and Netlify Identity work together. Basically you have to make your request with an `authorization` header and a `Bearer` token with your Netlify Identity JWT supplied. You can get this JWT from any of our Identity solutions from [gotrue-js](https://github.com/netlify/gotrue-js) to [netlify-identity-widget](https://github.com/netlify/netlify-identity-widget).
229+
230+
Since for practical purposes we cannot fully emulate Netlify Identity locally, we provide [simple JWT decoding inside the `context` of your function](https://github.com/netlify/netlify-lambda/pull/57). This will give you back the `user` info you need to work with.
231+
232+
Minor note: For the `identity` field, since we are not fully emulating Netlify Identity, we can't give you details on the Identity instance, so we give you [unambiguous strings](https://github.com/netlify/netlify-lambda/blob/master/lib/serve.js#L87) so you know not to rely on it locally: `NETLIFY_LAMBDA_LOCALLY_EMULATED_IDENTITY_URL` and `NETLIFY_LAMBDA_LOCALLY_EMULATED_IDENTITY_TOKEN`. In production, of course, Netlify Functions will give you the correct `identity.url` and `identity.token` fields. We find we dont use this info often in our functions so it is not that big of a deal in our judgment.
233+
234+
## Example functions and Tutorials
235+
236+
You can do a great deal with lambda functions! Here are some examples for inspiration:
237+
238+
- Basic Netlify Functions tutorial: https://flaviocopes.com/netlify-functions/
239+
- Netlify's list of Function examples: https://functions-playground.netlify.com/ ([Even more in the README](https://github.com/netlify/functions))
240+
- Slack Notifications: https://css-tricks.com/forms-auth-and-serverless-functions-on-gatsby-and-netlify/#article-header-id-9
241+
- URL Shortener: https://www.netlify.com/blog/2018/03/19/create-your-own-url-shortener-with-netlifys-forms-and-functions/
242+
- Gatsby + Netlify Identity + Functions: [Turning the Static Dynamic: Gatsby + Netlify Functions + Netlify Identity](https://www.gatsbyjs.org/blog/2018-12-17-turning-the-static-dynamic/)
243+
- Raymond Camden's [Adding Serverless Functions to Your Netlify Static Site](https://www.raymondcamden.com/2019/01/08/adding-serverless-functions-to-your-netlify-static-site)
244+
- Travis Horn's [Netlify Lambda Functions from Scratch](https://travishorn.com/netlify-lambda-functions-from-scratch-1186f61c659e)
245+
- [**Submit your blogpost here!**](https://github.com/netlify/netlify-lambda/issues/new)
246+
247+
These libraries pair very well for extending your functions capability:
248+
249+
- Middleware: https://github.com/middyjs/middy
250+
- GraphQL: https://www.npmjs.com/package/apollo-server-lambda
251+
- [Any others to suggest?](https://github.com/netlify/netlify-lambda/issues/new)
197252

198253
## Other community approaches
199254

200255
If you wish to serve the full website from lambda, [check this issue](https://github.com/netlify/netlify-lambda/issues/36).
201256

202257
If you wish to run this server for testing, [check this issue](https://github.com/netlify/netlify-lambda/issues/49).
203258

204-
If you wish to emulate more Netlify functionality locally, [check this repo](https://github.com/8eecf0d2/netlify-local).
259+
If you wish to emulate more Netlify functionality locally, check this repo: https://github.com/8eecf0d2/netlify-local. We are considering merging the projects [here](https://github.com/netlify/netlify-lambda/issues/75).
205260

206261
All of the above are community maintained and not officially supported by Netlify.
207262

bin/cmd.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,21 @@ program
5858
});
5959
});
6060

61+
// error on unknown commands
62+
// ref: https://github.com/tj/commander.js#custom-event-listeners
63+
program
64+
.on('command:*', function () {
65+
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '));
66+
process.exit(1);
67+
});
68+
6169
program.parse(process.argv);
70+
71+
// check if no command line args are provided
72+
// ref: https://github.com/tj/commander.js/issues/7#issuecomment-32448653
73+
var NO_COMMAND_SPECIFIED = program.args.length === 0;
74+
75+
if (NO_COMMAND_SPECIFIED) {
76+
// user did not supply args, so show --help
77+
program.help();
78+
}

lib/build.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,27 @@ function getBabelTarget(envConfig) {
1313
return unknown ? "6.10" : current.replace(/^nodejs/, "");
1414
}
1515

16+
function haveBabelrc(functionsDir) {
17+
const cwd = process.cwd();
18+
19+
return (
20+
fs.existsSync(path.join(cwd, ".babelrc")) ||
21+
functionsDir.split("/").reduce((foundBabelrc, dir) => {
22+
if (foundBabelrc) return foundBabelrc;
23+
24+
const indexOf = functionsDir.indexOf(dir);
25+
const dirToSearch = functionsDir.substr(0, indexOf);
26+
27+
return fs.existsSync(path.join(cwd, dirToSearch, ".babelrc"));
28+
}, false)
29+
);
30+
}
31+
1632
function webpackConfig(dir, additionalConfig) {
1733
var config = conf.load();
1834
var envConfig = conf.loadContext(config).environment;
1935
var babelOpts = { cacheDirectory: true };
20-
if (!fs.existsSync(path.join(process.cwd(), ".babelrc"))) {
36+
if (!haveBabelrc(dir)) {
2137
babelOpts.presets = [
2238
["@babel/preset-env", { targets: { node: getBabelTarget(envConfig) } }]
2339
];
@@ -48,7 +64,7 @@ function webpackConfig(dir, additionalConfig) {
4864
var webpackConfig = {
4965
mode: "production",
5066
resolve: {
51-
extensions: ['.wasm', '.mjs', '.js', '.json', '.ts']
67+
extensions: [".wasm", ".mjs", ".js", ".json", ".ts"]
5268
},
5369
module: {
5470
rules: [

lib/serve.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var expressLogging = require("express-logging");
44
var queryString = require("querystring");
55
var path = require("path");
66
var conf = require("./config");
7+
var jwtDecode = require("jwt-decode")
78

89
function handleErr(err, response) {
910
response.statusCode = 500;
@@ -76,7 +77,19 @@ function createHandler(dir, static) {
7677
};
7778

7879
var callback = createCallback(response);
79-
var promise = handler.handler(lambdaRequest, {}, callback);
80+
81+
// inject a client context based on auth header https://github.com/netlify/netlify-lambda/pull/57
82+
let clientContext = {}
83+
if (request.headers['authorization']) {
84+
const parts = request.headers['authorization'].split(' ')
85+
if (parts.length === 2 && parts[0] === 'Bearer') {
86+
clientContext = {
87+
identity: { url: 'NETLIFY_LAMBDA_LOCALLY_EMULATED_IDENTITY_URL', token: 'NETLIFY_LAMBDA_LOCALLY_EMULATED_IDENTITY_TOKEN' },
88+
user: jwtDecode(parts[1])
89+
}
90+
}
91+
}
92+
var promise = handler.handler(lambdaRequest, { clientContext }, callback);
8093
promiseCallback(promise, callback);
8194
};
8295
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"commander": "^2.17.1",
3131
"express": "^4.16.3",
3232
"express-logging": "^1.1.1",
33+
"jwt-decode": "^2.2.0",
3334
"toml": "^2.3.3",
3435
"webpack": "^4.17.1",
3536
"webpack-merge": "^4.1.4"

yarn.lock

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3384,16 +3384,6 @@ json5@^0.5.0, json5@^0.5.1:
33843384
version "0.5.1"
33853385
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
33863386

3387-
jsprim@^1.2.2:
3388-
version "1.4.1"
3389-
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
3390-
integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
3391-
dependencies:
3392-
assert-plus "1.0.0"
3393-
extsprintf "1.3.0"
3394-
json-schema "0.2.3"
3395-
verror "1.10.0"
3396-
33973387
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
33983388
version "3.2.2"
33993389
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"

0 commit comments

Comments
 (0)