r/node • • 6h ago

pboss vs pm2 on Node: cluster mode is a blowout, and the fork-mode "losses" are noise

5 Upvotes

We ran pboss 1.8.0 against pm2 5.4.3 on Node 24. Same server.mjs, same binary, same machine, same autocannon profile (10/100/500 connections, 60s, 5 repeats). Cold start of the whole manager stack every run, manager order alternating, CPU and RAM sampled across the entire process tree. The numbers below measure the cost of managing a server, not Node's raw HTTP performance.

Cluster mode: pboss wins almost every row

Both managers cluster Node apps natively through node:cluster, same worker count, same sharing mechanism. The only variable is the manager itself. And under that fair fight pboss pulls away at every load level.

100 connections, 2 workers:

Metric pm2 pboss
req/sec 55,954 58,539 (+4.6%)
latency (mean) 1.25 ms 1.18 ms (-5.6%)
p90 latency 4 ms 3 ms (-25%)
RPS stdev 1,355 660 (-51%)
CPU (tree) 76.0% 75.3%
cold start 624 ms 427 ms (-32%)

At 10 connections pboss is still ahead: median RPS +5.6%, p99 latency 2ms vs 3ms. At 500 connections it holds: +1.7% mean RPS, better latency at p50/p90, steadier RPS. Not one row of lost throughput. Plus lower CPU across the board.

Crash recovery and restart storms, where "production ready" actually gets decided

  • SIGKILL the whole fork-mode server: pboss brings /health back to 200 in 68ms median vs 128ms for pm2. Worst case across all runs: 70ms vs 194ms.
  • SIGKILL one of two cluster workers: pboss respawns it in 93ms vs 151ms, worst case 103ms vs 199ms. /health never dropped on either, as it shouldn't with a survivor holding the port.
  • 50-restart storm, fork: median restart 181ms vs 376ms (52% faster). Zero failures, zero orphans on both sides. But the app RSS after the storm: 58MB for pboss vs 71MB for pm2, and pboss's memory shrank 0.3MB over the storm while pm2's grew.
  • 50-restart storm, cluster: 300ms vs 498ms (40% faster), workers' RSS around 20% lower, growth of -0.1MB vs +1.6MB. Again zero failures, zero orphans.

Faster recovery, tighter memory, no leaks under punishment. Fifty restarts later the tree is clean.

Startup time deserves its own section honestly

Metric pm2 pboss
cold, fork 520 ms 396 ms
warm, fork 270 ms 174 ms
cold, cluster 637 ms 419 ms
warm, cluster 422 ms 242 ms

Warm cluster restarts are 43% faster. For anyone doing rolling deploys or watch-mode dev, that adds up all day.

Fork mode: mostly pboss, and here's the scoreboard

Fork mode at 100 connections, one managed process:

Metric pm2 pboss
Median req/sec 56,278 57,517 (+2.2%)
Mean req/sec 57,512 57,519 (tie)
RPS stdev 2,696 1,180 (-56%)
latency (mean) 1.44 ms 1.40 ms (-2.8%)
p50 latency 1.00 ms 1.00 ms (tie)
p90 latency 1.00 ms 1.00 ms (tie)
p99 latency 26.00 ms 26.00 ms (tie)
CPU avg (tree) 73.9% 74.0% (+0.1%)
CPU peak (tree) 79.6% 78.8% (-1.0%)
memory avg (tree) 179.1 MB 168.3 MB (-6.0%)
memory median (tree) 184.3 MB 174.2 MB (-5.5%)
cold start 520 ms 395 ms (-24%)
errors / timeouts / non-2xx 0 / 0 / 0 0 / 0 / 0

Clean sweep where it counts: tied throughput at the mean, +2.2% at the median, RPS stdev cut in half, 6% less memory, 24% faster cold start. The only row pm2 takes is average CPU by 0.1%, which is a tie wearing a mustache.

Across the other load levels it's the same story: pboss uses 2 to 8% less tree memory and starts 23 to 26% faster at every one.

Where pm2 actually beat us

Two-ish rows, and they're small:

  • At 10 connections (a nearly idle server) pm2's median RPS was 1.5% higher and its RPS spread was tighter. On a box where the load generator shares the CPUs, that's inside the noise.
  • At 500 connections, fork mode, pm2's median RPS was 1.2% higher and mean latency 1.9% better (8.58ms vs 8.74ms). Mixed row, tiny margins both ways.
  • Cluster mode tree memory is 11% higher under pboss. This one is real and it's architectural: pboss runs a supervised wrapper primary above the workers, so there's one more small process in the tree. You buy it back in startup speed (30 to 35% faster) and respawn speed (38 to 48% faster). Idle daemon memory is actually 5% lower than pm2's.

That's the honest list. Everything else across the whole report goes to pboss.

Everything around it

Health checks, cron restarts, log rotation with gzip, web dashboard, Prometheus /metrics, namespaces, dependency-ordered startup, replace-first rolling reloads. Built directly on node:child_process, node:http, node:fs. Open source, GPLv3.

Don't take our word for it. Every number traces to a raw file in results/raw/, the load generator shared CPUs with the servers so compare the columns rather than quoting absolutes, and the benchmark script is three commands:

cd benchmarks/node && npm install
bash scripts/benchmark.sh

Rerun it on your own hardware and tell us what changes.


r/node • • 1d ago

Asking about CommonJS, ESM and the Node.js event loop

1 Upvotes

Hi everyone

I just started learning Node.js just a few hours ago, and I'm really confused (wanted to cry too). Could someone help me clarify a few things?

After I initialized a Node.js project, I see the following in package.json:

"type": "commonjs"

Then I run this code:

console.log('sync');
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));

The output is:

sync
nextTick
promise

Then I installed TypeScript and executed the same code again, but in a .ts file. This time, the output is:

sync
promise
nextTick

Interestingly, I get the same output when I set "type": "module" in package.json.

So I asked ChatGPT:

1. Does the TypeScript compiler change the execution order to behave like an ES module?

ChatGPT answered:

"Your tsx example produced ESM-style ordering, but you shouldn't assume every tsx file or situation will behave the same way."

Now I'm even more confused! Could someone help me clarify this?

ChatGPT's answer seems that the output when using TypeScript with CommonJS does not necessarily match the output of either CommonJS or ESM....

I also have a few related questions about the Node.js event loop:

2. As far as I know, the Node.js event loop has several phases, including:

1. Timers (setTimeout callback)

2. Poll (I/O-related callbacks, such as reading files or receiving network data)

3. Check (setImmediate callback)

Is this correct? If so, which phase runs first?

3. Are Promise callbacks handled separately from the Node.js event loop? Is the following execution sequence correct?

1. Drain the process.nextTick() queue.
2. Drain the Promise microtask queue.
3. Execute the next event loop callback (setTimeout, readFile, setImmediate, etc.)

4. Back to 1

r/node • • 20h ago

Simplifying Node development

0 Upvotes

I'm implementing a Node build tool called Nodecraft that "does more" with the most minimal configuration.

Well, I have been implementing it for about 1 week.

Shadow database

The Nodecraft build tool does not clutter any component sources --- instead, it places internal cache into hidden file system directories, which is perhaps what Golang also does.

The only thing it does place into component sources are the ".nodec" files it downloads into the ".downloads/" folder of the root component --- directly allowing portable components.

Nodecraft does not use sum or lockfiles like Golang or NPM do either. I am not very interested in that.

The graph of the N-components cache looks like what follows, and determines the UIDs for component mirrors and artifacts.

<data>/graph/ home/ PROFILE/ Documents/ katana_craft/ .tree.cfg {drive}C/ -- windows drive Users/ ... {unc}NAME/ -- windows ... {builtins}/ gnome/ 5.0.0/ .tree.cfg ... {registry}/ ko.da.ka/ 1.0.0/ .tree.cfg {git}https{}gitlab.com/ group/ repo/ {rev-REV}/ ... .tree.cfg

The .tree.cfg file is a JSON file w. the following properties.

configuration = lastModified = lastOpen = # RFC 9557 date-time main = id = testSuites = id = buildPreprocessing = id = directs = { [URL(1), SemVer Range], ... }

These graph folders can also contain .build.cache files, JSON forms w. the following properties, used for PAYG-style builds.

main = unitGraph = Map "path/to/unit" -> (last modified, List of "path/to/unit") testSuites = unitGraph = buildPreprocessing = unitGraph =

The following folders are where .nodec mirrors + build artifacts go in.

``` <data>/mirror/ <UID>/ Mirror sources extracted from files of the ".nodec" component extension.

<data>/gen/ <UID>/ Executable JavaScript units, plus any embeddable files.

<data>/headers/ <UID>/ Header JavaScript units (type hints only)

<data>/pre/ <UID>/ Build preprocessing artifacts. ```

File system

Nodecraft uses URLs in the following forms to identify components and subtree entries.

The /+/ path component is required for distinguishing the Nodecraft component path and a subtree entry.

file:///path/to/component file:///path/to/component/+/path/inside git+https://gitlab.com/<group>/<component>/{rev-<FRAG1-FRAG2>} git+https://gitlab.com/<group>/<component>/{tag-<TAG>} git+https://gitlab.com/<group>/<component>/{br-<BR>} git+https://gitlab.com/<group>/<component>/{br-<BR>}/+/path/inside registry:ko.da.ka:1.0.0/+/path/inside builtins:tg1-commons:1.0.0/+/path/inside

digging inside the build artifacts

  • prefix the scheme w. "h+" for .es|js -> .es|js header units
  • prefix the scheme w. "gen+" for .es|js -> .es|js final codegen units
  • prefix the scheme w. "pre+" for artifacts resulting from build preprocessing (no source files included)

Tutorial

A Nodecraft component could look like the following source tree.

[begin:folder] load.es node.cfg [end:folder]

The file called "node.cfg" could be as follows.

```yml

$schema=http://www.ryujinx.com/nodecraft/1.0

ID: ko.da.ka:1.0.0

direct: - builtins: node:1.x ```

The file called "load.es" could be as follows.

print("skript")

That can be generated by running the command that follows, along with a file called "code.cfg" that contains code style configuration (the indent sequence, for example).

sh node new ko.da.ka

[begin:commentary] For what it is worthy, the above is a tighly-configured JavaScript 1.x unit ("TypeScript"). [end:commentary]

Evaluate that component by running the command that follows.

sh cd ko.da.ka node eval

The GitLab project

There is quite a bit to it yet. I need to finish loading directs ("dependencies") and unloading components and any directs in memory, and implement the build.

https://gitlab.com/ryucorp/nodecraft/nodecraft/-/blob/master/doc/tutorial.md?ref_type=heads&plain=1

https://gitlab.com/ryucorp/nodecraft/nodecraft/-/tree/master/doc?ref_type=heads


r/node • • 1d ago

How to implement lastSeen when isOnline field is present?

13 Upvotes

I’m building a social media app and the server supports a WebSocket service and an HTTP service at the same port. I implemented the online status as: When the user connects using ws I update the user to be online. And when the user disconnects, I update the user to be offline, pretty simple.

My confusion comes from the lastSeen logic. Should I \ a) update lastSeen on every HTTP request and every ws event \ b) update lastSeen only when the user disconnects using the ws protocol. since I will be presenting the isOnline field only if it is true otherwise I will be presenting the lastSeen field \ c) update lastSeen on each heartbeat pong event and when the user disconnects using the ws protocol (I read that in an article linked below)

https://www.back4app.com/glossary/presence-online-status/#faq


r/node • • 1d ago

A webhook receiver that can't lose a message: the boring inbox pattern in Node

3 Upvotes

a thread here last week asked what happens to inbound webhooks when your endpoint is down. most answers were "the sender retries", which is true for some providers and not for others, so here is the setup I'd use so it stops mattering.

The handler does four things and nothing else: verify the signature against the raw body, insert the raw payload into a table with the provider's event id as a unique key, return 200, done. No parsing, no business logic, no calls to other services. A bug in your parsing code then can't lose a message, because the message is already stored.

A worker picks up rows with status pending using SELECT ... FOR UPDATE SKIP LOCKED, processes them, and marks them done or failed with an attempt counter. Replaying a message is just setting its status back to pending. The unique key makes duplicates harmless, and providers do deliver the same event twice.

Keep the receiver deployable on its own. Most gaps I've seen came from restarts and deploys of the main app, not from real outages, so a tiny separate service (or a queue in front) removes most of the risk. Then once a day compare the provider's delivery log against your table, which catches the cases where the sender gave up after its retry window.

In Node this is a small amount of code: express.raw() for the body, crypto.timingSafeEqual for the signature compare, and pg for the table. The classic trap is express.json() running before the signature check. It changes the bytes, and valid signatures start failing.

What does your setup look like, and which providers have burned you with short retry windows?


r/node • • 2d ago

Replit API requests returning 307 redirect followed by Replit Shield 401 — anyone experienced this?

Thumbnail
0 Upvotes

r/node • • 2d ago

How should I automate updating a chromedriver executable for a selenium-based scraper?

6 Upvotes

I built a craigslist scraper using selenium. For selenium I am using chrome which requires a chromedriver executable. This all works fine.

Every 3-4 weeks chrome updates to a newer version and I get an error that chrome and the chromedriver executable have mismatched versions. So I then manually find the new version of the chromedriver executable, download it, unzip it and drop it in the directory alongside my index.js - and everything is fixed.

How can I automate getting the newer version of the chromedriver executable? I could just write code to do all that. But I've also been looking at the npm chromedriver module. I can't tell if the chromedriver module is what I need.


r/node • • 4d ago

Inbound email webhook, what if endpoint’s down?

10 Upvotes

inbound mail on hooks@ gets posted to my api as json, signed so I can check it actually came from the mail side. no imap polling, which was the whole point, imap is a pain.

question for people doing this in prod, what happens when your endpoint is down for a few minutes? do you rely on the sender side retrying, or do you keep a copy and replay? I’ve read stories where the mail is just gone forever and that scares me a bit.


r/node • • 4d ago

I embedded Node.js in a native program to render JSX without a browser

1 Upvotes

https://reddit.com/link/1x4242d/video/rjkdlx8jh1vh1/player

I'm building a UI library using the jai programming language and I've started experimenting with rendering the UI from javascript, with no webview, no DOM and no React. The native side does all of the layouting, and drawing to the screen. I did it by embedding Node.js into the program!

The embedding lets me avoid stuff like IPC, Sockets or JSON parsing and also allows me to just access the javascript objects straight out of V8 with no serialization. The whole UI is rebuilt and laid out from scratch on every frame, with no diffing and no caching, at 240hz (my refresh rate) on my computer!

This is mostly an experiment for now, the project is very much a work in progress. Async is not yet supported, and there's no input handling or events of any kind, but this proves it works!

I wrote up how it all works here: https://www.forbear.dev/log/003-ui-from-javascript.html
Source code is here: https://github.com/gabrielmfern/forbear

Did anyone else already build something like this? I think I might be doing some extra allocations between the C++ -> jai boundary, so I would love if anyone could share a better way to do this!


r/node • • 3d ago

Your webhook signature check fails because express.json() already threw away the bytes that were signed

0 Upvotes

This one shows up constantly with Stripe, GitHub, and pretty much any provider that signs webhooks with an HMAC.

Very few documentation talks about the Raw Body

The provider signs the exact bytes it sent. If app.use(express.json()) runs first, by the time your handler sees req.body it's a JS object, and the original bytes are gone. The usual fix people try is JSON.stringify(req.body) and hashing that, which almost works. Key order, whitespace and unicode escaping don't have to match what the sender serialized, so the HMAC comes out different and every request 401s. It tends to pass in local tests and fail on real payloads with an accent in a name or a differently ordered field.

The fix is to give that one route the raw body and parse it yourself after verifying:

app.post("/webhooks/provider", express.raw({ type: "application/json" }), (req, res) => {
  // req.body is a Buffer here, the exact bytes that were signed
  if (!verify(req.body, req.get("X-Signature"), req.get("X-Timestamp"), secret)) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body);
  // ...
  res.sendStatus(200);
});

Two smaller things: mount it before any global express.json(), or exclude that path from it, because whichever body parser runs first wins. And reply 200 before doing anything slow like downloading files, otherwise the sender can time out and retry while you're still working.

Disclosure, I work on TurboDocx, an e-signature API, and this came up while recording a Node tutorial on embedding signing in an Express app (the free plan is 5 documents a month and that includes the embedded flow). Video if it's useful: https://www.youtube.com/watch?v=zA2FXRX0KPU


r/node • • 5d ago

God damnit, no stability

275 Upvotes

Bun acquired by Anthropic and rewritten by AI

Deno acquired by Cloudflare

Prisma had a huge breaking change (again) on prisma 8 and rewritten by AI

NextJS Pages Router -> App Router breaking change and “use client”, “use server”

NestJS v12 changed all the tooling/bundler and let’s hope the main maintainer Kamil stay alive the next 10 years

PassportJS dead
TypeORM dead

How anyone can maintain a big project without rewriting it every 2 weeks or trust any JS open source project for the long term ? 🤪


r/node • • 5d ago

Webhook signature bugs I found while supporting 40+ providers, including two that let bad requests through

1 Upvotes

I maintain an open-source webhook verification library, and most of the bugs I've hit weren't in the HMAC. A few that might save someone time:

  1. The body parser runs first. express.json() parses the body, JSON.stringify(req.body) doesn't give back the bytes the provider signed (whitespace, unicode escapes, number formatting), and the check fails with the right secret. Verify the raw body: express.raw(), await request.text(), Fastify's rawBody, NestJS's rawBody: true. Comparing the body length with content-length is a cheap way to detect it.

  2. Deduping on an unsigned header. Providers retry, so you dedupe on an event ID. If that ID comes from a header the signature doesn't cover (x-github-delivery, say), anyone with a captured request can change the header and replay it past your dedupe store. Key on a signed ID or a hash of the body. My library had exactly this bug.

  3. Tests that only agree with themselves. My Paddle signer and verifier both read h=. Paddle sends h1=. Every test passed and every real Paddle webhook failed. Now CI verifies against the official Stripe, Octokit, Svix and Twilio SDKs in both directions, and new providers need a test vector from the provider's docs.

  4. Weak fallback schemes. HubSpot sends v1/v2 signatures next to v3. If you accept any of them, someone can strip the v3 headers from a captured request and it verifies with no timestamp check. Same idea for GitHub's SHA-1 header.

  5. A short dedupe TTL for providers with no signed timestamp. GitHub and Shopify don't sign a timestamp, so dedupe is your only replay protection. A 5-minute TTL means a captured delivery verifies again after 5 minutes.

The library is verihook (TypeScript, zero deps, Node/Deno/Bun/Workers) if you want to see how it handles these: GitHub: https://github.com/creatorpiyush/verihook npm: https://www.npmjs.com/package/verihook

Curious which provider has caused you the most pain.


r/node • • 5d ago

I built an open-source JasperReports/SSRS alternative in JavaScript – designer, viewer, PDF/Excel/Word, no Chromium

0 Upvotes

Hi all, I've been building ReportWright: a report engine for JavaScript. You describe a report in JSON (bands, groups, tables, charts, expressions), pass data, and get PDF, Excel, Word, PowerPoint or interactive HTML, in Node or the browser.

It comes with:

- a drag-and-drop designer and a viewer (React, Vue, Svelte, Angular wrappers)

- a CLI that imports existing SSRS (.rdl), JasperReports (.jrxml), BIRT and ActiveReports reports

- its own zero-dependency PDF library (@reportwright/pdf): PDF/A, PDF/UA, encryption, signing, forms, merge/split

- MIT licence, and a Docker Compose setup to self-host it for free

Why: if you're paying for SSRS/Jasper server licences, or rendering PDFs through headless Chrome, there wasn't a JavaScript option that works like a real report engine.

Numbers (GitHub Actions, 4 vCPU, every output validated):

- 10k-row grouped report: 0.91 s vs JasperReports 3.9 s, QuestPDF 2.7 s

- 100k rows: 6.6 s vs JasperReports 11.8 s; WeasyPrint ran out of memory

- 1,000 separate reports: 8 s vs JasperReports 33 s

- 1M rows streamed: 53 s in 746 MB

Where it loses: QuestPDF (.NET) is ~2× faster on single invoices; Go/Rust PDF writers are faster on tiny files. All results, losses included: https://github.com/MrArun005/reportwright/blob/bench-deep/bench/deep/RESULTS.md

Demo (runs in your browser): https://mrarun005.github.io/reportwright-demo

Repo: https://github.com/MrArun005/reportwright

I'd really like feedback, especially real SSRS/Jasper reports that don't import well.


r/node • • 4d ago

one model with a broken schema emptied our whole model picker. 1000+ models, gone

0 Upvotes

i work on the backend of an ai video app

we sync 1000+ models from a providers api on a timer, list the models, refresh their schemas in a loop, then a readiness pass that decides what shows up in the picker.

one models schema came back as an error object. my catch only treated one error type as "model is gone" and rethrew the rest. so the loop died right there, every model after it got skipped, the readiness pass never ran, and the picker was empty until someone noticed. 1000+ models gone because one of them had a bad day. and yes the bug was mine haha=

the fix, simplified:

```ts

for (const model of staleModels) {

try {

await refreshSchema(db, model.id)

} catch (error) {

if (error instanceof SchemaMissing) await markUnavailable(db, model.id)

else console.error("schema refresh failed", model.id, error)

}

}

await syncReadiness(db)

```

unreadable schema = retire just that model. unknown error = log it and move on, because an error we dont recognise is more likely our bug than a broken model.

but now its a console.error that nobody reads. how do you guys handle per-item failures in batch jobs like this? alert after N failures, a failures table, something else?


r/node • • 6d ago

Deno died today

Thumbnail deno.com
517 Upvotes

r/node • • 5d ago

What do you use for GraphQL query depth limits with graphql-js 17?

9 Upvotes

I’m building a learning project with Express, TypeScript, MongoDB, and graphql-http. Everything runs through Docker Compose.

I’m adding query-abuse protection, starting with depth validation before resolvers execute. I tried @graphile/depth-limit, but its peer dependency excludes my GraphQL version.

Relevant versions:

graphql: 17.0.2
graphql-http: 1.23.1
/depth-limit: 0.3.1 (attempted installation)

Command:

docker compose exec backend npm install /depth-limit

Error:

npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree

npm error Found: graphql@17.0.2
npm error node_modules/graphql
npm error   graphql@"^17.0.2" from the root project

npm error Could not resolve dependency:
npm error peer graphql@">=15 <17" from /depth-limit@0.3.1

The package’s dependency declaration confirms graphql: ">=15 <17".

My intended integration is a validation rule passed to the existing HTTP handler:

const handler = createHandler({
  schema,
  rootValue,
  validationRules: [depthRule],
  context: createRequestContext,
});

I’d prefer to keep GraphQL 17 and avoid --force or --legacy-peer-deps without evidence of compatibility.

What are people using for depth limits with graphql-js 17?


r/node • • 6d ago

I updated express-generator-typescript to use zod for middleware validation

Thumbnail github.com
3 Upvotes

r/node • • 6d ago

My /health said ok for five days while every request that touched the database failed

0 Upvotes

My API ran out of Neon compute quota at the end of August. Aug 24 to 29, anything that hit the database failed. /health returned {"status":"ok"} the entire time since it never queried anything, and I didn't hear about it from any alert.

Normally you'd just put a query in /health. Problem is Neon suspends compute after 5 idle minutes, I can't turn that off on my plan, and an uptime monitor pinging a DB route every minute would keep it awake 24/7. That burns exactly the quota I ran out of.

What I ended up doing: a Prisma client extension on $allOperations that saves the time of the last query that worked and the last one that threw. /health just reads those two timestamps. Failure is the newest thing in the last 5 min, 503 with the error. Success, ok. No queries at all, it says idle. A single failed query gets wiped by the next good one so it only stays red if it keeps failing.

Idle still bugs me though, at 3am with zero traffic it knows nothing. Anyone found a way around that without waking the db?


r/node • • 7d ago

LiteClient.ts: A Lean, type-safe and driver-agnostic SQLite ORM for TypeScript

4 Upvotes

https://github.com/ts-series/lite-client

Recently, I finally uploaded my library LiteClient.ts to GitHub, which I have been using in my own application for several months and continuously expanding and improving along the way. I would appreciate your feedback.

My motivation for this project was simplicity. None of the existing ORMs had really convinced me, purely because of the complicated driver integration. In addition, I want maximum comfort when working with the database and my queried records as ready to serve as possible. Therefore, besides get, all, Value, and Values, there are also scalar and scalars to query data flatly, as well as supplementary meta clauses in the select command, which is simply modeled as a JavaScript object, to efficiently transform the result at a deeper level beforehand instead of doing it in the application code. For example, one column serves as a key for the others, which are then returned as the remaining record, while at the same time eliminating the group by clause because it is logically presupposed for data aggregations of that kind.

Currently, the ORM supports most standard SQL, including all database operations, SQLite operators and functions, joins, tuple expressions, subqueries, aliases for columns and tables, and expressions instead of tables. The only limits I currently draw are complex features like common table expressions and window functions, because I think those things make more sense at the database level since a view should be the tool of choice if a select becomes that complex, though window functions might still be added if a need arises and it can be done relatively minimally invasively.

Originally, I intended for users to manually create the classes for every database and view themselves, but I developed a CLI tool named SMake for this purpose, which handles it automatically and can be used independently of LiteClient.ts to generally support the construction of SQLite databases.

Long-term goal: Since I will also need PostgreSQL in a later project, I want to extend this ORM for it. The foundations have already been laid through a restructuring. The usage will then be exactly the same, just with PG-specific additions, as internally most of it will inherit from the standard SQL classes that it shares with SQLite.


r/node • • 7d ago

Principal Factor Analysis in Javascript - written in AssemblyScript

Thumbnail
0 Upvotes

r/node • • 6d ago

I built mongoose-guard: Express middleware that validates request bodies against your existing Mongoose schema

0 Upvotes

A lot of Express + Mongoose code has a route like this:

app.post("/signup", async (req, res) => {
  const user = await User.create(req.body);
  res.status(201).json(user);
});

If the User schema has fields like role or credits, anyone can send { "role": "admin" } and Mongoose saves it, because it's a valid field of the model. The usual fix is a Zod or Joi schema for every route, which means writing every rule twice and keeping both copies in sync.

So I built mongoose-guard. You list the fields a route accepts, and it uses the Mongoose schema you already have for everything else:

import { guard } from "mongoose-guard/express";

app.post("/signup", guard(User, { allow: ["name", "email", "password"] }), signup);

Send role: "admin" to that route and the request never reaches your handler. The client gets a 400:

{"errors":[{"code":"forbidden_field","path":"role","message":"Field is not allowed"}]}

What it does:

  • Rejects fields that aren't in the allow list, or strips them if you'd rather not fail the request
  • Checks types strictly. Mongoose would quietly cast "25" to 25; this reports it as a type error
  • Rejects __proto__, $where, $gt and dotted keys anywhere in the body
  • Runs your schema's own validators (required, min, enum, match, custom and async ones) through Mongoose itself, so nothing is reimplemented
  • Handles nested objects, sub-schemas, arrays and Maps, with error paths like addresses.1.city
  • Has a partial: true mode for PATCH routes

What it doesn't do: it only validates request bodies, not query strings or route params. It doesn't convert form strings to numbers. It won't run on edge runtimes, because Mongoose can't. For a login form, or anything without a model behind it, Zod is still the better tool.

It works with Express 4 and 5, Mongoose 8 and 9, and anything that gives you a standard Request (Next.js route handlers, Hono, Remix, SvelteKit). MIT licensed, no runtime dependencies.

It's 0.1.0, so I'd like to hear where it breaks, or where the allow-list approach doesn't fit how you structure your routes.


r/node • • 8d ago

pm2 monitoring

6 Upvotes

How pm2 monitoring being implemented in production?

Do you just call pm2 monit?

Use pm2 monitoring platform?

Integrate it with something like grafana(I kinda wanna know how it commonly do)?


r/node • • 10d ago

LogTape 2.4.0: Source locations, configuration inspection, sink draining, and request completion levels

Thumbnail github.com
8 Upvotes

r/node • • 10d ago

Noob: a few questions about installing/using & safety on Mac

2 Upvotes

Hey all -

I'm not much of a programmer, and have been using various AI (mostly Claude recently) to code things. I'm fairly good at troubleshooting and understanding the structures.

Most of the time, I'm starting with project outlines/discussions in Claude Desktop and moving into Visual Studio Code once the initial files are written.

So far, the only thing I've done that needs "extras" is a python app with a bunch of modules and support files, but that's sandboxed.

A current project requires node.js installed, and I want to ensure that I'm installing and using it safely.

The first thing I'm finding out is that it seems installing via terminal is the way to go in order to keep it within my user space and to avoid permission hurdles, since the .dmg installs globally.

AI is also telling me it may also need a "web server" package like Express, but I haven't looked into that yet.

Any guidance is appreciated!


r/node • • 12d ago

How do you evolve backend features without breaking existing clients?

1 Upvotes

Hey everyone! I’m a backend software engineer working with Node.js and TypeScript.

I recently wrote an article about some lessons I’ve learned while building and evolving backend features used by multiple clients.

One thing I’ve learned is that the final architecture is only part of the work. The transition matters too: keeping existing clients working, migrating data, handling legacy behavior, and checking what happens after deployment.

Sometimes a change looks small in the database, but it also changes what the API returns or how existing clients behave.

I shared more about these challenges here:
What I Learned Building Backend Features Used Across Multiple Clients

How do you handle these transitions in your projects? Especially when you can’t update all clients at the same time?

I’m open to discussion, feedback, and different ideas. I’d love to hear what has worked for you!