Solutons Lounge

Integrating Rust into Next.js: How-To Developer Guide


Web development was caught in the Rust storm and so far it looks like a great fit. Rust entered on a high note with standard JavaScript tooling like bundlers, compilers, test runners, and even runtimes being (re)written in Rust with massive performance gains. As a long-time programmer who worked in production projects with a bunch of different languages like PHP, Python, Objective C, Swift, and JavaScript my goal with learning a new language is always to start writing production code as soon as possible. Don’t get me wrong, fundamentals are important but where you see and learn the nuances of the language is solving real problems.

Recently my focus has been on the backend and APIs so my goal with Rust was to start writing new API endpoints for my projects in Rust instead of the usual JavaScript/Node.js. If you read any of my articles you might already know that I use Vercel to host most of my projects. I like the versatility it provides, allows me to deploy a bunch of different frameworks and mostly forget about the hurdle of managing infrastructure for the apps I am working on and just focus on delivering features. Naturally, I was curious if it is possible to build, deploy, and run Rust code on the Vercel platform and if I can integrate it into my current (mostly) JavaScript-based projects.

To my delight, there are multiple ways to deploy Rust code on Vercel. We will go through different options and then go into details with the option I chose to go with.

  • Using WebAssembly (Wasm) at the Edge. This allows us to write Rust code which we can then compile into .wasm binary which we can import as any other package/file into our JavaScript code.
  • Using custom Rust runtime. Aside from deploying Node.js and frameworks such as Next.js, Vercel supports a range of custom runtimes that allow the deployment of native code compiled from other languages like Go, Python, and also Rust. Some of those runtimes are Vercel-maintained and some, like Rust runtime, are community-maintained. Both work pretty well (I used PHP and Python runtimes before).
  • Using Rust runtime in the Next.js project. While using Rust runtime with Vercel is great, a lot of my projects are actually on frameworks like Next.js. To boost the number of projects in which I can use Rust I decided to integrate a template using Rust runtime with Next.js. That way I could gradually implement some features for my projects in Rust. It will also allow me to learn Rust more by using it each day.

Setup

We are going to start with a fresh create-next-app project to show the modifications needed to make this work. This will also provide you with steps you can take to do it in your project as well. Of course, I also have my template linked on GitHub below.

When you have your Next.js project up and running we can start to put Rust in the mix. If you were wondering this should all work on Next.js version 12.x, 13.x and 14.x and also with pages or app directory. To make use of custom runtimes we need to add vercel.json configuration file to our project.

We are also going to run npm install vercel -D to add vercel CLI to our dev dependencies. We will use it to run a local server for our Rust API endpoints. What this will do is tell Vercel to deploy any of our .rs files as serverless API functions with Rust runtime. The next thing is to create top-level api/ directory inside of our project. This differs from pages/api/ directory that you might already have in your Next.js project. If you have any code inside pages/api/ just keep it there as is and proceed to create top-level /api directory for our Rust runtime functions.

You will also need to have Rust installed on your local machine. The quickest way is with rustup, so you can go ahead and install it if you don’t have it already. Rust uses cargo as a package manager and build system. We get everything we need to build and run our Rust code locally with a single command. To enable cargo we need to create Cargo.toml configuration file in the root of our project.

If you are familiar with package.json it has some similarities. We add some general metadata like name and version, and we then add dependencies we need to build our API endpoints. In Rust dependencies are called “crates” and are hosted on crates.io We use tokio as async runtime, serde_json for JSON parsing/transform and vercel_runtime which is Rust equivalent of Vercel or Next.js APIs to manipulate requests and responses. Also, if you are using VSCode you can install rust-analyzer extension which will enable all the Rust language features and give you some more help and context when writing code.

Next, we are going to create our first Rust API endpoint. We will name it api/crab.rs as we wrote in our cargo config file.

Now, if you never wrote a line of code in Rust this might not be 100% clear but if you used Vercel or some HTTP server in any language it should be mostly readable. We create a handler function that will be called to respond when API request is made to GET /api/crab . It responds with 200 OK status code and returns a message in JSON format.

We are going to quickly test it by running npx vercel dev . If you created a new project this will prompt you to set up your Vercel project, just follow the prompts to set it up. When it goes through it will say something like “Ready! Available at http://localhost:3000”. If you open http://localhost:3000/api/crab in your browser after a few seconds you should see the response.

Response from our Rust API endpoint

If you refresh again you will see the response comes back almost instantly. That is because vercel dev under the hood used cargo to build and run our Rust file on the fly. This also automatically detects changes so it will only recompile when you change something in the code. So just write code and enjoy. You might have noticed there is a target/ folder in your root directory now, this is the standard output folder for Rust binaries so you want to add it to your .gitignore file. You can also add those to your .vercelignore (check the template at the end for reference).

Now that we have everything up and running let’s clean it up a bit and run it with the rest of our Next.js codebase. Rust runtime will take care of running our Rust code when deployed to Vercel but for dev, we will modify or npm run dev command a bit.

What we do here is that we run Rust API on different port with new dev:rust script. Then in addition to running next dev we also add it to our standard dev script. Now we can run everything in a single command that we are used to in Next.js projects.

Another thing is that we don’t want to go to localhost:3001 for our Rust endpoints which will be a different port than the default localhost:3000 for Next.js. We will fix it by adding a rewrite inside our next.config.js.

We apply rewrite only in development. On Vercel it will all be deployed to the same deployment so no need for a rewrite. Now you can go to http://localhost:3000/api/crab again and it will work the same way even though it is running on a different dev server. Adding rewrite to fallback also makes sure that any of your existing pages/api endpoints do not get overwritten by Rust API endpoints in development.

Now that we have everything up and running let’s take a look at some common patterns you might need (or would use) in day-to-day API development.



Source link

Exit mobile version