UNPKG

netlify-lambda

Version:

Build and serve lambda function with webpack compilation

319 lines (216 loc) 17 kB
## Netlify Lambda 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. The goal is to make it easy to write Lambda's with transpiled JS/TypeScript features and imported modules. ## Installation **We recommend installing locally** rather than globally: ```bash 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). 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. ```toml # example netlify.toml [build] command = "yarn build" functions = "lambda" # netlify-lambda reads this publish = "build" ``` ## Usage We expose two commands: ``` netlify-lambda serve <folder> netlify-lambda build <folder> ``` 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). The `build` function will run a single build of the functions in the folder. 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`: ``` folder/hello.js -> http://localhost:9000/.netlify/functions/hello ``` 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. **IMPORTANT**: - You need a [`netlify.toml`](https://www.netlify.com/docs/netlify-toml-reference/) file with a `functions` field. - 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. Files that end with `.spec.*` or `.test.*` will be ignored so you can [colocate your tests](https://github.com/netlify/netlify-lambda/issues/99). - 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. <details> <summary><b>Environment variables in build and branch context</b> Read Netlify's [documentation on environment variables](https://www.netlify.com/docs/continuous-deployment/#build-environment-variables). `netlify-lambda` should respect the env variables you supply in `netlify.toml` accordingly (except for deploy previews, which make no sense to locally emulate). However, this is a [relatively new feature](https://github.com/netlify/netlify-lambda/issues/59), so if you encounter issues, file one. </details> <summary> <b>Lambda function examples</b> </summary> If you are new to writing Lambda functions, this section may help you. Function signatures should conform to one of either two styles. Traditional callback style: ```js exports.handler = function(event, context, callback) { // your server-side functionality callback(null, { statusCode: 200, body: JSON.stringify({ message: `Hello world ${Math.floor(Math.random() * 10)}` }) }); }; ``` or you can use async/await: ```js export async function handler(event, context) { return { statusCode: 200, body: JSON.stringify({ message: `Hello world ${Math.floor(Math.random() * 10)}` }) }; } ``` For more Functions examples, check: - https://functions-playground.netlify.com/ (introductory) - https://functions.netlify.com/examples/ (our firehose of all functions examples) - the blogposts at the bottom of this README </details> ## Using with `create-react-app`, Gatsby, and other development servers ### Why you need to proxy (for beginners) `react-scripts` (the underlying library for `create-react-app`) and other popular development servers often set up catchall serving for you; in other words, if you try to request a route that doesn't exist, the dev server will try to serve you `/index.html`. This is problematic when you are trying to hit a local API endpoint like `netlify-lambda` sets up for you - your browser will attempt to parse the `index.html` file as JSON. This is why you may see this error: `Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0` If this desribes your situation, then you need to proxy for local development. Read on. Don't worry it's easier than it looks. ### Proxying for local development > ⚠️IMPORTANT! PLEASE READ THIS ESPECIALLY IF YOU HAVE CORS ISSUES⚠️ When your function is deployed on Netlify, it will be available at `/.netlify/functions/function-name` for any given deploy context. It is advantageous to proxy the `netlify-lambda serve` development server to the same path on your primary development server. 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. - 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 - 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/). - If you are using Next.js, see [this issue for how to proxy](https://github.com/netlify/netlify-lambda/pull/28#issuecomment-439675503). - If you are using Vue CLI, you may just use https://github.com/netlify/vue-cli-plugin-netlify-lambda/. - If you are using with Angular CLI, see the instructions below. [Example webpack config](https://github.com/imorente/netlify-functions-example/blob/master/webpack.development.config): ```js module.exports = { mode: 'development', devServer: { proxy: { '/.netlify': { target: 'http://localhost:9000', pathRewrite: { '^/.netlify/functions': '' } } } } }; ``` <details> <summary> <b>Using with `Angular CLI`</b> </summary> CORS issues when trying to use netlify-lambdas locally with angular? you need to set up a proxy. Firstly make sure you are using relative paths in your app to ensure that your app will work locally and on Netlify, example below... ```js this.http.get('/.netlify/functions/jokeTypescript'); ``` Then place a `proxy.config.json` file in the root of your project, the contents should look something like... ```json { "/.netlify/functions/*": { "target": "http://localhost:9000", "secure": false, "logLevel": "debug", "changeOrigin": true } } ``` - The `key` should match up with the location of your Transpiled `functions` as defined in your `netlify.toml` - The `target` should match the port that the lambdas are being served on (:9000 by default) When you run up your Angular project you need to pass in the proxy config with the flag `--proxy-config` like so... ```bash ng serve --proxy-config proxy.config.json ``` To make your life easier you can add these to your `scripts` in `package.json` ```json "scripts": { "start": "ng serve --proxy-config proxy.config.json", "build": "ng build --prod --aot && yarn nlb", "nls": "netlify-lambda serve src_functions", "nlb": "netlify-lambda build src_functions" } ``` Obviously you need to run up `netlify-lambda` & `angular` at the same time. </details> ## Webpack Configuration By default the webpack configuration uses `babel-loader` to load all js files. Any `.babelrc` in the directory `netlify-lambda` is run from will be respected. If no `.babelrc` is found, a [few basic settings are used](https://github.com/netlify/netlify-lambda/blob/master/lib/build.js#L11-L15a). If you need to use additional webpack modules or loaders, you can specify an additional webpack config with the `-c`/`--config` option when running either `serve` or `build`. See this issue for an example of [how to write a webpack override file](https://github.com/netlify/netlify-lambda/issues/64). The additional webpack config will be merged into the default config via [webpack-merge's](https://www.npmjs.com/package/webpack-merge) `merge.smart` method. ### Babel configuration 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). 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. 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. ### Use with TypeScript We added `.ts` and `.mjs` support recently - [check here for the PR and usage tips](https://github.com/netlify/netlify-lambda/pull/76). 1. Install `@babel/preset-typescript` ```bash npm install --save-dev @babel/preset-typescript ``` You may also want to add `typescript @types/node @types/aws-lambda`. 2. Create a custom `.babelrc` file: ```diff { "presets": [ "@babel/preset-typescript", "@babel/preset-env" ], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-transform-object-assign", "@babel/plugin-proposal-object-rest-spread" ] } ``` 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. Check https://github.com/sw-yx/create-react-app-lambda-typescript for a CRA + Lambda full Typescript experience. ## CLI flags/options There are additional CLI options: ```bash -h --help -c --config -p --port -s --static ``` ### --config option If you need to use additional webpack modules or loaders, you can specify an additional webpack config with the `-c`/`--config` option when running either `serve` or `build`. ### --port option The serving port can be changed with the `-p`/`--port` option. ### --static option 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). ## Netlify Identity 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). 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. 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. ## Debugging To debug lambdas, prepend the `serve` command with [npm's package runner npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) `npx --node-arg=--inspect netlify-lambda serve ...`. Additionally: 1. make sure that sourcemaps are built along the way (e.g. in the webpack configuration and the `tsconfig.json` if typescript is used) 2. webpack's uglification is turned off with `optimization: { minimize: false }`. If using VSCode, it is likely that the `sourceMapPathOverrides` have to be adapted for breakpoints to work. Netlify Functions [run in Node v8.10](https://www.netlify.com/blog/2018/04/03/node.js-8.10-now-available-in-netlify-functions/) and you may need to run the same version to mirror the environment locally. Also make sure to check that you aren't [committing one of these common Node 8 mistakes in Lambda!](https://serverless.com/blog/common-node8-mistakes-in-lambda/) Don't forget to search our issues in case someone has run into a similar problem you have! ## Example functions and Tutorials You can do a great deal with lambda functions! Here are some examples for inspiration: - Basic Netlify Functions tutorial: https://flaviocopes.com/netlify-functions/ - Netlify's list of Function examples: https://functions-playground.netlify.com/ ([Even more in the README](https://github.com/netlify/functions) as well as our full list https://functions.netlify.com/examples/) - Slack Notifications: https://css-tricks.com/forms-auth-and-serverless-functions-on-gatsby-and-netlify/#article-header-id-9 - URL Shortener: https://www.netlify.com/blog/2018/03/19/create-your-own-url-shortener-with-netlifys-forms-and-functions/ - 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/) - 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) - Travis Horn's [Netlify Lambda Functions from Scratch](https://travishorn.com/netlify-lambda-functions-from-scratch-1186f61c659e) - [JAMstack with Divya Sasidharan & Phil Hawksworth | Devchat.tv](https://devchat.tv/js-jabber/jsj-347-jamstack-with-divya-sasidharan-phil-hawksworth/) - Great discussion on the problems that Netlify Functions solve - [**Submit your blogpost here!**](https://github.com/netlify/netlify-lambda/issues/new) These libraries pair very well for extending your functions capability: - Middleware: https://github.com/middyjs/middy - GraphQL: https://www.npmjs.com/package/apollo-server-lambda - [Any others to suggest?](https://github.com/netlify/netlify-lambda/issues/new) ## Other community approaches If you wish to serve the full website from lambda, [check this issue](https://github.com/netlify/netlify-lambda/issues/36). If you wish to run this server for testing, [check this issue](https://github.com/netlify/netlify-lambda/issues/49). 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). All of the above are community maintained and not officially supported by Netlify. ## Changelog - v1.0: https://twitter.com/Netlify/status/1050399820484087815 Webpack 4 and Babel 7 - v1.1: https://twitter.com/swyx/status/1069544181259849729 Typescript support - v1.2: https://twitter.com/swyx/status/1083446733374337024 Identity emulation (& others) - v1.3: https://github.com/netlify/netlify-lambda/releases/tag/v1.3.0 ## License [MIT](LICENSE)