npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

render-workflows-rust

v0.1.2

Published

Unofficial: write Render Workflows tasks in Rust, compiled to a native Node addon. Not affiliated with or endorsed by Render.

Readme

render-workflows-rust

Write Render Workflows tasks in Rust.

Unofficial. Not affiliated with or endorsed by Render.

#[napi(catch_unwind)]
pub fn sum_squares(numbers: Vec<i64>) -> i64 {
    numbers.iter().map(|n| n * n).sum()
}
npx render-workflows-rust init my-tasks
cd my-tasks && npm install && npx render-workflows-rust build
render workflows start my-tasks/sumSquares --input='[[2, 3, 4]]' --local

Why this exists

Render Workflows ships an SDK for TypeScript and Python only. A workflow service accepts elixir, go, node, python or ruby — Rust is a first-class Render runtime for web services and background workers, but is not offered for Workflows, and there is no Rust SDK to register tasks with.

buildCommand and runCommand are free-form strings, though, and the runtime only constrains the build image. So the Rust compiles to a native Node addon through NAPI-RS, and a two-line JavaScript entrypoint registers its exports with Render's own SDK. No Docker. API provisioning and render workflows dev both keep working.

The addon runs in the task process. There is no subprocess, no serialisation across a pipe, and the boundary itself costs about 28 ns per call — measured on Render, against 3 ns for a plain JavaScript call.

The one rule

Write #[napi(catch_unwind)], not #[napi].

Without it a panic — from an unwrap(), an out-of-range index, anything — aborts the Node process instead of failing the run, taking every other run in flight on that instance with it. With it, the message arrives at Render as the task's error, verbatim:

status   failed
error    deliberate panic from Rust

render-workflows-rust build warns about every bare #[napi] it finds, and refuses to build on the two related traps: a Cargo.toml without crate-type = ["cdylib"] (cargo would emit an rlib, which Node cannot load), and panic = "abort" in the active profile (which silently defeats catch_unwind).

Prefer napi::Result for anything you expect to fail. A panic is for a violated invariant; an Err fails one run cleanly.

Tasks

Every #[napi] function the addon exports becomes a task, named as JavaScript sees it — sum_squares registers as sumSquares. Arguments and return values are JSON, capped by Render at 4 MB per invocation.

index.js is the whole entrypoint:

require('render-workflows-rust/runtime').runTasks('./build/tasks.node');

Per-task options go in package.json, which is the one unsatisfying part of this release — the settings live apart from the function they describe:

"renderRust": {
  "tasks": { "fetchUrl": { "retry": { "maxRetries": 2 } } },
  "exclude": ["helperNotATask"]
}

Commands

The package installs two commands. render-workflows-rust is the name used throughout these docs; render-rust is a shorter alias for the same thing, for when you are typing it often.

npx resolves a package name, so first contact needs the full one:

npx render-workflows-rust init my-tasks   # fetches the package
npx render-rust build                     # inside a project, once installed

| | | | --- | --- | | render-workflows-rust build | Compile the crate to build/tasks.node, skipping if unchanged | | render-workflows-rust dev | Build, then start Render's local task server | | render-workflows-rust init [dir] [--template <name>] | Scaffold from an example | | render-workflows-rust rust | Which Rust this project uses, and why | | render-workflows-rust rust --list | What the archive offers |

Configure through renderRust in package.json:

{
  "renderRust": {
    "crate": ".",
    "out": "build/tasks.node",
    "profile": "release",
    "features": [],
    "target": null,
    "cargoFlags": [],
    "rustVersion": "1.98.0"
  }
}

Choosing a Rust version

Three places, highest first — the flag for trying one once, the environment for varying a build without a commit, and package.json for the answer that should travel with the project:

npx render-workflows-rust build --rust-version 1.97.0
RENDER_RUST_VERSION=1.97.0 npx render-workflows-rust build
"renderRust": { "rustVersion": "1.97.0" }

A version can be exact, latest, or a channel — stable, beta, nightly. An exact version needs no network to interpret, so a pinned project keeps building when the archive is unreachable.

A pin set explicitly wins over a Rust already on PATH. If they differ, the requested version is downloaded. Only the built-in default defers to a local toolchain — it exists so a first build has something to fetch, not to override a Rust you installed deliberately.

Downloads are checked against the archive's published SHA-256 before being unpacked, hashed while the archive streams to disk so it costs no extra I/O.

Deploying

A workflow service with:

| | | | --- | --- | | runtime | node | | build command | npm install && npm run build | | start command | npm start |

Use npm install, never npm ci. The toolchain, the cargo registry and every compiled dependency are vendored into node_modules, which is the only directory Render's build cache preserves. npm ci deletes it before each build, turning every deploy back into a cold one with no error to explain why.

Measured on a real free-tier deploy:

| | | | --- | --- | | Cold build | 25 s — 365 MB toolchain fetched in 2 s, unpacked in 12 s, cargo 10 s | | Warm rebuild | 1 s — toolchain cache hit, cargo not invoked | | Cached | 577 MB toolchain + 25 MB registry + 100 MB build artifacts | | Addon | ~600 KB |

What a workflow instance actually is

nproc reports 32 or 48. The cgroup quota is 1.0 CPU and 2 GB:

cpu.max     100000 100000
memory.max  2147483648

std::thread::available_parallelism() reads that quota and returns 1, so sizing a thread pool from it is correct and sizing one from nproc is not. The build container is more generous — 2 CPU, 8 GB — which is why builds are quick and tasks are not.

Threads are genuinely available, and the threads example measures whether they help. On one CPU, the honest answer is usually no.

Examples

Each is a working project, a deployable service, and an init --template:

| | | | --- | --- | | default | Four tasks, no dependencies beyond napi | | threads | Real OS threads, and an honest measurement of them |

Requirements

Node 20.17+ (napi-rs's floor; Render currently runs 26.x). No local Rust needed — one is fetched if cargo is not on PATH.

License

MIT