Netlify Function using Rust (neon)

All Posts

Netlify Function using Rust (neon)

Updated: September 19, 2020 by Tony Alves

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 and neon-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: String
register_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 successul
let 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

© Tony Alves