Netlify Function using Rust (neon)
Let's build a Netlify function to use Rust neon. Netlify doesn't support setting up Rust for functions exactly how we would on AWS Lambda
like Netlify does with GO. We will use a Node
compatible package builder called neon-cli
to provide a package for use from our javascript handler function. Netlify doesn't have support for the Rust compiler (yet) in the build environment during our CI/CD, so we will have to prebuild our Rust library local and commit the package with our repository. Although this isn't optimal for our workflow, the pre-build solution will work just fine.
Note: There is a way to use GitHub actions to build our Rust package, but outside the scope of this tutorial. We could then use actions to build our Netlify functions and deploy to Netlify.
Steps we'll take
- Setup our
yarn
workspaces andneon-cli
- Setup our
functions
workspaces for Netlify - Create the Rust library using neon
- Setup our Netlify function
- Run our function build
Requirements:
- Rust and Cargo installed
- yarn (for workspaces)
Setup our yarn
workspaces
We'll be using yarn to setup our project, so create a project directory and create your package using yarn.
$ mkdir rust-netlify-function-neon$ cd rust-netlify-function-neon$ yarn init -y
Edit your package.json
to include the neon-cli
and setup our workspaces location as functions
package.json
{"name": "rust-netlify-function-neon","version": "0.1.0","description": "Netlify function using Rust neon","author": "talves","license": "MIT","private": true,"workspaces": ["functions", "functions/*"]}
Setup Netlify for our functions
We will setup a Netlify config (netlify.toml
) in the root of our repository (optional but recommended).
netlify.toml
[build]command="npm run build:site"publish="site"functions="functions"
Setup Netlify build for our site
This could be the build for a real site in a workspace, but we're just using this for an example so we'll do a simple site creation in one command.
package.json
{"name": "rust-netlify-function-neon","version": "0.1.0","description": "Netlify function using Rust neon","author": "talves","license": "MIT","scripts": {"build:site": "mkdir site && echo '<h1>Our Rust Netlify Function using neon</h1>' > site/index.html"},"private": true,"workspaces": ["functions", "functions/*"]}
Run our function build
We now want to run the function build neon build
so we can include the package in our function bundle inside our repository. Since we don't want our build to run on install of the dependencies, we will replace the install
script with build
for running local.
functions/hello/package.json
{"name": "hello","version": "0.1.0","description": "Hello world Netlify function using neon","main": "hello.js","author": "talves","license": "MIT","dependencies": {"neon-cli": "^0.3.3"},"scripts": {"build": "neon build"}}
Create the functions
directory so we are ready to create our neon crates.
$ mkdir functions$ cd functions$ yarn init -y
Create the functions directory workspace package.json
for our functions root and add our neon-cli
dependency.
{"name": "functions","version": "1.0.0","main": "index.js","license": "MIT","devDependencies": {"neon-cli": "^0.3.3"}}
Then install our dependencies:
$ yarn install
Create the Rust neon library
$ yarn workspace functions neon new hello
Answer the questions from neon-cli
and create your function workspace.
? version 0.1.0
? description Hello world Netlify function using neon
? node entry point (lib/index.js) hello.js
? git repository
? author (talves)
? email (me@example.com)
? license (MIT)
You should see output as seen below based on our input:
The main Node entry point is at: hello/hello.js The main Rust entry point is at: hello/native/src/lib.rs
To build your project, just run npm install from within the hello directory. Then you can test it out with node -e 'require("./")'.
You will see a new directory at functions/hello
with a hello.js
function and a directory that holds the rust library in native
.
Here is what our functions/hello/native/src/lib.rs
looks like originally without the comment.
#[macro_use]extern crate neon;use neon::prelude::*;fn hello(mut cx: FunctionContext) -> JsResult<JsString> {Ok(cx.string("hello node"))}// TODO: make our hello function accept name: Stringregister_module!(mut cx, {cx.export_function("hello", hello)});
We want to change our library to accept a name
string so we change the src/lib.rs
contents with the following:
src/lib.rs
#[macro_use]extern crate neon;use neon::prelude::*;fn hello(mut cx: FunctionContext) -> JsResult<JsString> {// Attempt to cast the first argument to a JsString. Then// get the value if cast is successullet name = cx.argument::<JsString>(0)?.value();let return_val = format!("Hello {}!", name);Ok(cx.string(return_val))}register_module!(mut cx, {cx.export_function("hello", hello)});
We also want to remove the function .gitignore for the build artifacts, so we will allow them, but ignore the target directory.
node_modules/target/
Setup our Netlify function
Replace the contents of the hello.js
file with our Lambda function using the correct path to where our neon package will exist.
functions/hello/hello.js
const { hello } = require('./native/hello.node');exports.handler = async function ({ queryStringParameters = {} }) {try {const { name = 'World' } = queryStringParameters;const message = hello(name);return {statusCode: 200,body: message,};} catch (error) {console.log(error);return { statusCode: error.statusCode || 500, body: error.message };}};
Run our function build:
$ yarn workspace hello build
Done!
Commit our code up to GitHub and go to Netlify and setup our new site using the repository.
If you'd like to skip over this work, here is a repo including all the source code and also a one-click button in the Readme to deploy to Netlify and create your own copy on GitHub.
Test your function by going to your site at:
https://your-site-name.netlify.app/.netlify/functions/hello
Or
https://your-site-name.netlify.app/.netlify/functions/hello?name=Everyone