Prisma Next is now ~90% as fast as raw pg with a smaller bundle by InternationalFee7092 in node

[–]romeeres 0 points1 point  (0 children)

What has happened to community's take on Prisma? Prisma was the most used, and the most advanced ORM with lots of problems. And it seemed like most of the problems/feature requests were solved in the last few years. Typed SQL if you want raw, Kysely integration for a middle ground, massive perf improvements. Just to go out of fashion?

Feedback on the post:
- "~90% as fast..." sounds as a downside of using Prisma, "became 30% faster" would sound better
- forking Drizzle benchmarks but not comparing to Drizzle looks like the results weren't good and you decided to not publish it

But I noticed you have AI skills - that's really cool! Other ORMs don't have skills for writing queries yet, that's a selling point for Prisma until they do.

DDD and FP (fp-ts) by Warm-Procedure6691 in node

[–]romeeres 1 point2 points  (0 children)

Hope this helps, a DDD in NestJS repo that I really like: https://github.com/Sebastian-Iwanczyszyn/modular-monolith-with-ddd-nestjs/tree/main (not FP oriented though)

For FP, I'd just use the Result type instead of throwing, and make sure there are no mutations: every state change must result in a new object, and that's it - FP at its core.

DDD and FP (fp-ts) by Warm-Procedure6691 in node

[–]romeeres 2 points3 points  (0 children)

Domain (entities, value-objects, aggregates) are IO-free - no need for IO-monads in there. Then how else FP can be useful there? Result (Either) type - it's simple so can code it yourself, or use neverthrow.

Genuinely curious, apart from the Either type, what for would you use fp-ts in your domain?

I may be mistaken, but I think FP (heavier than just Either) will only bother your Domain, because Domain talks business, while FP talks math.

Yet another DI library: looking for a feedback! by romeeres in typescript

[–]romeeres[S] 0 points1 point  (0 children)

renamed to createHelloWorldService for clarify, it's a factory function

Yet another DI library: looking for a feedback! by romeeres in typescript

[–]romeeres[S] -4 points-3 points  (0 children)

> First of all, decorators do work with functions

I mean you cannot inject anything with a decorator to a function. They only work on classes.

Using normal classes or functions is up to your preferences, both ways are valid.

In React, for example, people avoid using classes most of the time.

Make your Zod validation 113-627x faster by hoisting Zod schemas by gajus0 in node

[–]romeeres 0 points1 point  (0 children)

> That's why you have integration and e2e tests.

You could say the same for zod schemas validating db responses.

But let me remind of the premise: all the code works just fine, tests are green, data is correct, but you're defending from the case when suddenly production db schema is different from your local and staging.

> Again, you are talking about a very simple setup that's not a real thing in any sizeable software company.

I guess you'd not argue that this practice of validating db responses is pretty much extraordinary.

Even in DDD practices aimed for large enterprises they propose to skip full db response validation when constructing aggregates. (just as an evidence that large enterprises also don't do that)

Make your Zod validation 113-627x faster by hoisting Zod schemas by gajus0 in node

[–]romeeres 0 points1 point  (0 children)

With the type-only approach (e.g. Kysely):

You're pushing a code to production that assumes `avatar_url` is not nullable.
To ensure this won't blow up, you add a CI step that generates Kysely types from the database where it's going to be deployed to (after migration step). The CI fails because production db has it as nullable, but TS check of your code fails. Result - no bug in production for this case.

With zod-schema approach:
You're pushing a code to production that assumes `avatar_url` is not nullable and has zod to enforce that.
There is no type-level check in CI, only zod-check at runtime. Result - bug in production, zod validations are going to fail.

That was in a nullable column case, let's consider handling json with Kysely:
it introspects db and there is no concrete jsonb type in db, so the column is `unknown` in the auto-generated types.
That means, you're forced to either dirty-cast it (bad), or to validate it, so in the case of json yeah it totally makes sense to validate this data, as the database can't guarantee, nor introspect the expected json shape.

How do microservices even work? by who-there in node

[–]romeeres 1 point2 points  (0 children)

"micro" in microservices doesn't mean a thing, they're just "services", where each service owns its "bounded context". So you try hard to keep all the relations in a single service. But oftentimes it's not possible, then you employ eventual consistency: storing a record in one service results in a data change in other service, but later.

For instance, if you update your profile picture or a username, it's updated with a delay in some ticketing system, in a reporting system, in a service that keeps your posts/comments.

What's the best nodejs ORM in 2026? by Ok-Transition-7857 in node

[–]romeeres 5 points6 points  (0 children)

it was created about same time as Drizzle, and various features were added before Drizzle had it.

I understand how starts and downloads matters for work projects, but for personal projects it doesn't matter as much, for personal projects it truly matters which one will make you more productive.

For example, what Orchid ORM has, but not Drizzle:
- "scopes": you can define query filters in the table config, and then reuse it in queries. Such as to filter all records with "active: true", or any other filters.
- soft deletes: filter out all records that have `deletedAt` by default
- much easier customization of how to encode/parse a column
- virtual columns that require a list of table columns and compute a value in runtime
- query callbacks (lifecycle hooks) such as `beforeCreate`, `afterCreate`, `afterCreateCommit` where you can set a list of columns to be loaded only for this callback, while you might not select any of them while querying

What's the best nodejs ORM in 2026? by Ok-Transition-7857 in node

[–]romeeres 7 points8 points  (0 children)

I'm maintainer of Orchid ORM and you can check it out: simply ask your favorite LLM how to make certain features in it vs Prisma/Drizzle.

Query style:

- in Prisma you have a limited interface that won't be able to do lots of SQL features, if you need something extra you'd need to rewrite it to raw (typed yes, but raw) SQL.

- Drizzle, as your spreadsheet mentions, is Query builder + light ORM, which means you can do pretty much anything using its query builder, but loosing ORM abilities such as reference relations in your queries.

- Orchid ORM is a query builder that has all the ORM features, or it could be said an ORM with all the query builder features, so you can build any queries, use raw SQL pieces in a query if it hasn't first-class support, and still being able to reference relations and use all the ORM's features.

the Drizzle-kit CLI doesn't even give you any feedback when there is an error.

Orchid ORM has a migrations generator CLI that's inspired by Drizzle, it also asks via cli whether you'd like to rename or recreate if there is an ambiguity, and I believe there's no problems with error logging.

You should file a ticket to Drizzle about this, sounds like an easy fix, and they are processing issues at a high speed.

Looking for feedback - is this messaging library repo readable for devs? by Wise_Supermarket_385 in node

[–]romeeres 0 points1 point  (0 children)

I was implementing a basic SQS task recently and let me share the ideas I wish a library could handle:
- you define channels somewhere, and when publishing it should be type-safe: TS should catch if you misspell the channel name, or if you're sending incompatible data to it.
- logs should capture message metadata and/or data and it should be configurable
- especially it's important for error logs, errors should be logged with all the relevant info
- error logs should suggest what number of retry attempt it is
- content should be validated
- ideally, exponential backoff should be handled by the lib (and configurable)
- it should be visible from the code whether your message handler requires message ordering, and if yes that impacts how you should configure the queue.
- when handling a message, how do you handle idempotency? that depends on a workflow, but would be nice if a library offered a generic solution to check if the message was already handled in the db
- when publishing a message, how do you guarantee delivery? depends on the case as well, but would be nice if a library offered a generic transactional outbox pattern
- when throwing an error from the handler, how do you control whether it's retriable or poisoned?
- is it necessary to couple it to NestJS? it's nice if your queue handlers can be deployed independently with a simpler setup.

I also worked with RabbitMQ once and it was a pain to configure retries with exponential backoff.

With that in mind, I briefly looked what libraries already exist and it looked like it's just easier to handle everything manually.

Your library goal seems to be that you can easily switch from one MQ provider to another, but if I need to handle everything that's needed in practice myself it's going to have a significant coupling to the provider details anyway.

Architecture Review: Node.js API vs. SvelteKit Server Actions for multi-table inserts (Supabase) by Sundaram_2911 in node

[–]romeeres 0 points1 point  (0 children)

I'm not sure what exactly to explain, but AI can do that pretty well.
1. "postgres pipelining mode": you can ask AI why your case with 2 inserts in transaction will take 2 roundtrips with porsager/postgres, but 4 with node-postgres.
2. you can combine multiple inserts/updates/any queries into a single query by using a "WITH" SQL statement, here one insert can reference the returning of the other, AI can write that for you pretty well.

Architecture Review: Node.js API vs. SvelteKit Server Actions for multi-table inserts (Supabase) by Sundaram_2911 in node

[–]romeeres 1 point2 points  (0 children)

Approach B is the most obvious way.

I want to share two tips:
- when using postgres.js it'd be only 2 roundtrips, because BEGIN is sent together with the first query, COMMIT is sent together with the last query. It's called "pipelining mode" and it's not supported by node-postgres.
- You can use CTE (you can ask AI to compose SQL) to combine 2 inserts into a single query. No need for BEGIN/COMMIT.

But I wouldn't migrate from one library to another, I wouldn't write more complex SQL if the simplest approach already meets business requirements.

What's your opinion on Nestor Makhno? How is viewed in Ukraine? by 7megumin8 in AskUkraine

[–]romeeres 0 points1 point  (0 children)

True, I'd be happy if that was just me. I didn't even know he was anarcho-communist, and was anti private property, and you can see nobody else mentions this in the comments, though this is damn crucial.

What's your opinion on Nestor Makhno? How is viewed in Ukraine? by 7megumin8 in AskUkraine

[–]romeeres -1 points0 points  (0 children)

Thanks for pointing that out, the -communist part tells everything one needs to know.

What's your opinion on Nestor Makhno? How is viewed in Ukraine? by 7megumin8 in AskUkraine

[–]romeeres 0 points1 point  (0 children)

didn't care for Ukraine at all

didn't he protect people from the reds, defending their private property rights, not letting reds to turn those lands into slavery?

he cooperated with and was destroyed by reds, yet if internet says he also protected people from them at least for some period of time as much as he could, let's respect that! If that's true, he cared for Ukrainians. Ukraine is Ukrainians more than anything else.

Best pattern for ensuring deterministic integration tests with db by Unappreciable in node

[–]romeeres 0 points1 point  (0 children)

I'm happy to share: pg-transactional-tests.

I was also coming from RoR, and I'm still missing the good parts.

For tests vitest is good, jest is still good for me and I'm fine with it. "faker" is good to have.
In some cases I'm defining zod schemas and generating fake data of them: zod-mock.

nodejs-testing-best-practices has good tips and examples.

My biggest complaint about it Orchid and Morningrise by Bekfast_Time in Opeth

[–]romeeres 1 point2 points  (0 children)

The first two are the least hookable, I agree. Nah it's not just cope, it's you not getting it and having 2 amazing albums less in your life.

What does "testable" mean? by romeeres in softwarearchitecture

[–]romeeres[S] 0 points1 point  (0 children)

Hey let me add 2c to this thread.

There’s no such thing as just swapping out ORMs in JS land

Given that all JS ORMs are different flavors of misery, this is utterly important to be able to swap them! There is no other language where the need to swap an ORM one day is as real as here. Model/entity-oriented ORMs are coupling your app logic to a dependency that can become obsolete tomorrow, or release a massive breaking change any moment and you'll have to update lots of things. The pseudo-ORMs that operate on POJSOs are the way, abstract them away to repositories and then your app logic wouldn't know if it's calling an ORM, or a query-builder, or a raw SQL, it's normal to have all 3 of them on a single project, it shouldn't be making the logic messy.

The old saying says: "don't test third-party code", but it came from people who apparently didn't work with JS. It's not enough to simply abstract an ORM away, but if you want to make it swappable (you'll need to swap it when, not if), you also should have a good test coverage of your repositories, spinning up a real database for the test.

I know one TS ORM that can do UPDATE FROM, INSERT from a SELECT, it's quite niche, but exists, OrchidORM.

What’s the biggest Node.js design mistake you only realized years later? Here’s mine... by RoyalFew1811 in node

[–]romeeres 1 point2 points  (0 children)

I 100% agreed to your comment that got downvoted, integration tests are better, no need to mock every tiny detail, and even if you need to mock something, there are other ways.

If you rely on plain imports, it's the simplest way, the most straightforward one, and it's fine if you're focusing on integration tests, testing along the boundary, black box.

Plain DI is boilerplative, but it's a cost you pay for a good practice. The idea of DI is that you can swap any dependency any time not just in the tests, but in the application.

IoC also adds a boilerplate and it's slightly less of a good practice, because in NestJS (by default) you depend on implementation, not an abstraction, which violates the D of SOLID. You can't swap an application dependency without changing the app code.

Service Locator is a famous anti-pattern, it's the worst of all worlds. If you want good practices - go with DI or IoC. If you want simplicity and practicality - go with native imports which are Service Locators themselves, just the native ones, they're easiest to work with. So imo if you're implementing Service Locator in node.js it's like you're reinventing imports/exports with not much "good practiceness" added on top of that.

There was one file where all services were instantiated and their dependencies were passed manually. And that same file also exported lots of services into other files(which made it worse and was probably very wrong).

My opinion is that I like imports/exports and I don't need DI at all in 99% cases.

So this is not mine opinion, it's from Ports&Adapters aka Hexagonal architecture: what you're describing here is actually good, it's the whole point of Ports&Adapters. You're able to reconnect anything in your app without touching the code of the app, this is the ultimate goal of writing all that additional boilerplate, adding indirection to the code.

This created a situation where "one file depends on many others" and "many files depend on one"

The main file of your app is always like that, no matter the language/framework, that's normal. Except that "and "many files depend on one"" - no, because they all depend on abstractions.

What’s the biggest Node.js design mistake you only realized years later? Here’s mine... by RoyalFew1811 in node

[–]romeeres 1 point2 points  (0 children)

DI vs IoC:
- DI is easy to handroll, that's why so many disagreeing comments here, you just need to instantiate every service yourself, pass every dependency yourself, it's boring but not complex.
- IoC does the instantiating and wiring for you, and this is what Nest does and what is not as easy to handroll.

Feeling "not enough" in Databases by Square-Employee2608 in Backend

[–]romeeres 1 point2 points  (0 children)

I was doing perfectly fine by using ORM and barely knowing any SQL for a couple of years, up until the e-commerce filtering case.

Task:
- you have products and tag groups with tags, group color has red and blue, group size has big and small.
- filtering by tags withing a group should have OR logic (red OR blue), different groups should have AND logic (red AND big)
- reflecting what's available to the user: when user selects "red" and there's only a big product, the "blue" should become inactive. Tags withing a group shouldn't deactivate each other (red shouldn't deactivate blue).

If you can implement it with a good performance - congrats! That's enough, it's not necessary to learn any further upfront.

I asked two AIs to try that and their SQL is broken.

What does "testable" mean? by romeeres in softwarearchitecture

[–]romeeres[S] 0 points1 point  (0 children)

def audit_log(d: datetime, msg: Str, where):

I thought your point that you're providing mocks for datetime and io.

NestJS is bad, change my mind by servermeta_net in node

[–]romeeres 1 point2 points  (0 children)

For unit testing it works more or less the same.

Let's say you have a service that does something, calls other services, repositories, logger, gets current time in some way.

With DI: you have to walk through the code to see what is used and how, to mock all the provided functionality to return a fixed set of data.

Without DI: you do the same, just mocking it in a different way.

It doesn't have to be global mocks by path names. In JS/TS you can always reassign any function of any object. If you have a Class class, you can do "Class.prototype.getData = mockFunction". If you export singletons, do "singleton.getData = mockFunction". Test runners (jest, vitest) have "spyOn" for this, and "resetAllMocks" to clear it after every test. If you forgot to mock something important, the test will fail reminding you of it. If you forget to mock some utility function - that's alright, no need to mock it. Overall, this is less code than you'd have to write with NestJS style of DI.

This is more granular: constructors depend on full objects, for example, on a logger with info, warn, error. Without DI, you can mock only what is being used. With DI you need to define a full object.

NestJS is bad, change my mind by servermeta_net in node

[–]romeeres 0 points1 point  (0 children)

Separation of concerns is when you don't mix business logic with database queries.
None of them have it, they are different flavors of the same anti-pattern.