What is the standard web application backend framework for Node.js these days, is it Koa? As I understand it express is quite dated because of the callback structure.
I have been confused recently with the moving best practices. What is the server architecture Next.js is most often plugged into?
Who cares how old something is, if it works well, why not use it? You can easily avoid callback-hell by making the callback you pass in to expressjs async and use await inside of it, without much drawbacks. Just because the first closure into express is a callback doesn't mean you need to use callbacks everywhere, just use await/generators/whatever you want in your application code.
expressjs at this point is battle-tested, minimal, easy to understand (both in usage and internals) and have a huge community still. It's very mature and does what it does well.
I haven't used Express in ten years, but imo `async (req, res, next) => ...` would mainly be attractive if failure was automatically handled and you could `await next()` to post-process the response. Though it would still help you write async code inside that route.
Since iirc you send responses directly from handlers in Express, I'm not sure the latter is possible even with express-promise-router since you basically need downstream routes/middleware to return a Response object rather than send it directly.
I think Express is never going to unify with Koa's direction since it's just too disruptive to Express' ecosystem (the v5.x branch is stale) which is probably for the best.
The big problem is Express code like the following will either hang the HTTP request forever (--unhandled-rejections=warn), until the client times out and gives up, or crash your whole server, taking down any other HTTP requests in progress (--unhandled-rejections=throw):
const db = {
async getUsers() {
throw new Error("failed to connect to database");
},
};
app.get("/", async (req, res) => {
const result = await db.getUsers();
return result;
});
There are various monkey patches/wrappers you can use to make async errors work the same as sync errors, but it is easy to forget and hard to understand, especially for newbies. Many other frameworks handle async/sync errors in a more consistent way.
Already fixed in Express 5 (which is technically still in beta, but that just seems to be the maintainer being very conservative about a major release): https://expressjs.com/en/guide/error-handling.html
> Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error. For example:
> If getUserById throws an error or rejects, next will be called with either the thrown error or the rejected value. If no rejected value is provided, next will be called with a default Error object provided by the Express router.
I'm not sure whether you mean "not very convincing" in the sense of "not reassuring that it works" or something else, but judging from users' comments on this issue about the release plans, it seems to work just fine: https://github.com/expressjs/express/issues/4920
The maintainer said this three months ago in a comment on that issue:
> Express 5 is pretty much completed at this point, and we're just finishing up the last code merges in upstream modules in order to bump the dependencies finally in the 5.0 branch.
Like how Windows XP on an old computer still "works just fine"?
What does work just fine in terms of software that's ever updating mean? It doesn't matter if it's called 4 or 5 or any other label.
On the comments: my point is more that we should not base the NodeJs standard on something that's not regularly maintained / updated. There are infinite amount of things that can be improved on for a "standard" server framework used by millions. You don't say Node "is done" and leave it? People would definitely fork it or move on (as happened in the past).
Is it? It performs slower than e.g. Fastify. Don't see how that's doing well.
> Who cares how old something is, if it works well, why not use it?
It doesn't. That's the whole point. Software is always evolving and I doubt it's even close to complete. There hasn't been meaningful updates to Express in a long time. Why do people use it and/or promote it as a standard?
> Is it? It performs slower than e.g. Fastify. Don't see how that's doing well.
It's such a marginal difference when you consider real-life applications built with either express or Fastify though. Seldom is the actual bottleneck which HTTP library you use (unless you're at a really, really, really big scale), and more about how you structure your code, database and general architecture. I can count on one hand the number of times I've had to care deeply about the HTTP stack in order to optimize something, while I've probably had to optimize 100s of web products over the years, all done without touching the HTTP parts at all.
> It doesn't. That's the whole point. Software is always evolving and I doubt it's even close to complete. There hasn't been meaningful updates to Express in a long time. Why do people use it and/or promote it as a standard?
How to handle HTTP requests kind of doesn't. HTTP 1.1 continues to work, and will continue to be sufficient for at least 90% of all the use cases on the web for a long time.
I know that the JS community generally suffers from "If it hasn't been updated in the last year, it's probably broken and not modern enough" syndrome, but some software actually end up being "finished" and good enough for the task at hand. What improvements could you actually add to express without breaking the API interface that millions of applications depend on? Sometimes stability is wanted and needed, and that's how you get mature software. Who wants to rewrite their application and architecture every time some library you happen to use decides to "improve" it?
> How to handle HTTP requests kind of doesn't. HTTP 1.1 continues to work, and will continue to be sufficient for at least 90% of all the use cases on the web for a long time.
If you take out your 386 and boot up Dos 3.1 it'll; continue to work too. Let's just not improve, right?
> but some software actually end up being "finished" and good enough for the task at hand
Like dead? Do you ever stop learning and growing? Are you "finished" too? There are new vulnerabilities, new compatibilities, existing bugs, different use cases, etc - you think it's finished = turning a blind eye.
> It's such a marginal difference when you consider real-life applications built with either express or Fastify though.
That's how we get Electron and everything become a resource hog. Oh it doesn't matter. Oh marginal difference. No don't recycle, don't do good for the environment and anything in life because it's all marginal. Why even reply? It's a marginal difference.
> There hasn't been meaningful updates to Express in a long time.
Because for the most part - it doesn't need them.
Can you build a better express? Maybe. Lots of folks are trying that still, and you're welcome to use the tooling they put out.
Is express dead? Hell no.
Express is well documented, well supported, getting security patches, and comes up entirely clean for a new install (including most of the optional packages you might add).
Express has some sharp edges (it's been mentioned plenty above, but mainly because the framework predates the wide adoption of native promises in the JS space), but again, they're well known and relatively straight forward to work around.
---
Long story short, this:
> It doesn't. That's the whole point. Software is always evolving and I doubt it's even close to complete. There hasn't been meaningful updates to Express in a long time. Why do people use it and/or promote it as a standard?
Is an ignorant take. People promote it because it's a good tool.
Next you'll be telling me that I shouldn't use Bash because Bash 5.0 came out in 2018 and only got two minor point releases over the next 5 years. But that sounds dumb, huh?
> Next you'll be telling me that I shouldn't use Bash because Bash 5.0 came out in 2018 and only got two minor point releases over the next 5 years. But that sounds dumb, huh?
What's dumb is not being able to tell the difference. It's not about the numbers.
https://git.savannah.gnu.org/cgit/bash.git/ shows there are constant updates. Things are being worked on. Your example proves my whole point. There are updates every year for many years.
Express? Not so. There are many gaps in its history. And for a tool like Express that has way more surface area it is in need of a lot more testing and updating e.g. if NodeJs changes something it breaks.
Does Bash need to make sure it works with HTTP3, Brotli or many other things that came out? No.
> Because for the most part - it doesn't need them.
Right, let's go back to the Stone Age. You don't need clothes either - just a leaf. You're just so dismissive on innovation then why bother? You don't even need NodeJs. Back to assembly and punch cards...
> Is an ignorant take.
Yes, yours. As shown above by your example.
> People promote it because it's a good tool.
And how do you know that? Where are the stats? Or people just google for NodeJs server, see the top response being Express and just do that. The cycle then repeats. Have people promoting it actually benchmarked, compared and investigated all the tools before making this informed decision? I'm sure you've heard from each and everyone 1 of them to know the answer eh.
I'm sorry to point out that the first 5.0 alpha came out in 2014 and still hasn't been released. When people talk about the ecosystem/battle-tested-ness/quality of express I think that unfortunately doesn't extend to anything other than the latest major on npm. It's not "modern" but it's still a good tool.
This. Fastify is so great. Unfortunately, the documentation became worse after v2 because Matteo was too laissez-faire in approving new doc contributions.
Imo the docs need a major re-write, beginning at "getting started". New users don't know whether they should use "fastify-cli gen", "npm init fastify" or copy manually from the "getting started"-guide.
And people also tried to push their fastify-plugins to the top of the plugins-site by adding a "@username" for every plugin: https://i.imgur.com/hHyI6JO.png (left are the old docs, right are the current docs where ppl try to game the system).
Also, fastify-cli needs to rework the custom options in typescript projects imo.
I’ve been using this and deploying straight to vercel (where they run as serverless functions.) Took some obscure googling to get things like sequelize to work, not overall it seems to work pretty great. However I would like to be able to control the resources (e.g. adding more cpu etc) and haven’t yet found a way to do so.
I've used Koa in my last few work projects but will be either switching back to Express or moving onto Fastify for the next one. Like you mentioned, Koa does indeed feel dated. It's community never really took off, either; many of it's most popular helper/companion libraries haven't been updated in years.
I still use Koa out of habit since it was the only framework for a while that had first-class promise support.
One thing nice about Koa is that it's simple, so it's timeless in that way—it's not a moving target nor does it try to do something that needs a lot of core maintainers.
In the past 3 years I've built 4 apps (1 mid-size, 3 small-mid) using Apollo and IMO it's currently the best possible approach. I did a research for my latest app a half year ago looking for sensible alternatives and didn't really find any.
For anything CRUD or that can be modeled as such, I use FeathersJS (https://feathersjs.com/).
Real time updates are basically free, db integration is easy and adaptable. The service-oriented architecture with hooks just feels like a very natural way to think about an application.
They recently pushed their long awaited v5 update, which adds a Koa transport alongside the historic express transport, and improves the "code reuse between client & server" story for schemas/models/types.
The standard is express. I say that with some glibness, but its the only true answer: a ton of the other higher level frameworks and pluggable middlewares still rely on the core express Request/Response types. And there are a ton of higher level frameworks, if the number of distinct replies wasn't obvious.
I really like express + routing-controllers [1], if you're on typescript.
Many actively developed backend/backend for frontend frameworks like NestJS, Apollo Server, tRPC, GraphQL Yoga, etc. offer an additional layer of abstraction that typically relies on ExpressJS (or at least offer building on top of it) when deployed against Node.js, but they allow for deployment to other targets like serverless
Express was really cool and different from what was out there at the time (at least in terms of simplicity and JS support).
But nowadays there's not a lot of difference between them. Sure, some are more ergonomic than others in certain areas but the patterns are almost the same in all of them.
The only exception is when I’m working inside an established Node shop where they already have a prolific framework, I’ll reach for what they already have in that case to avoid fragmenting their stack. Almost always, that’s express.
fastify is performant, thoughtfully designed, and well architected.
Callbacks in JS aren't really dated, just many of the patterns associated with them are. The Promise constructor itself takes two callbacks as its arguments. Async/await are just syntax sugar over that constructor. Express supports promise-based mw callbacks just fine.
So I'd still say Express is the main choice. Personally I've never loved the Express API as it encourages a lot of heavy mutation (Koa does too), so I tend to be on the lookout for good alternatives - I haven't seen any that seem to be sticking (gaining significant enough community to bet on) more than Express.
There's a few things like Next/SvelteKit that cover all bases in one, from your web app backend -> SSR frontend -> client states, but if you're looking at pure backend for e.g. an API, Express is still going strong.
I've used this - https://adonisjs.com/ - I don't think it's "standard". Is there such a thing? I guess Express was a de-facto standard. I'm a bit biased since I come from Laravel background, so adonis feels familiar.
Express (fka Connect) is standard in the sense that the "middleware" API design was/is part of the CommonJs server-side JavaScript (SSJS) initiative [1] from which Node.js originated and insofar as you can write a middleware that plugs into Node.js' core http API and express.js at the same time since these invoke your code using the same basic callback, which was deemed desirable for portability back when Node.js wasn't the only SSJS framework around. Express/connect draws inspiration from Ruby's Sinatra/Rack and Python's WSGI, and is based on JSGI/JackJs.
Adding to this, these days, what's a complete nodejs MVC web framework that allows for easy integration of something like server-sided React for the front-end?
You write your own middleware stack, and router every time you start a new "typescript" server project?
Of course not. If you're not using a third party library you're using your own framework. The Node.js API is too barebone for any serious web application.
I can't tell how this has actually improved upon existing options like Koa. This reeks of object oriented programming just for the sake of object oriented programming.
I’m working on a moderately large-scale Nest.js project and deeply dig it, but I don't really see a lot of people talking about it, which gives me concern.
Glad to hear you say that, though, maybe I’m just out of the loop.
I have also started using it pretty recently, and haven't had too much issue finding folks talking about any issues I've run into (and either found fixes for my issue, or at least workarounds while folks are currently working on fixes).
However, it still by default uses Express under the hood, though you can change that to Fastify or whatever else you like.
I'm using it for my side project, a highly productive framework; I can also say it's in use in some _very_ large corporations, although I cannot give details about that.
Koa is relatively outdated at this point. Fastify is generally what I and most people I know look towards when starting something "new". Express is still in a lot of things, though.
The "Don't talk about deno" release. Quite an interesting feature list. I'm really interested if wasi + a permission prescription + package authorization might allow us sharable cli tools like git hooks and filters that we can trust.
The big new feature is a sandboxing with explicit CLI flag opt in to permissions, exactly like deno pioneered. Except there's zero mention of deno as a clear inspiration of the feature.
Jest is slow in part because it fully isolates each test file from every other test file. For small, highly-experienced teams those guardrails don't provide much benefit. For large teams, that isolation is essential to prevent cross-test contamination.
Another reason Jest is slow is due to how it's usually configured to build code using Babel or tsc. Like vitest, you can configure Jest to use esbuild which makes it much faster than using Babel or tsc.
I am not a big Jest fan, but it seems like the most pragmatic test runner when you have 100+ engineers working on a project.
Well I'm sure they knew about it -- maybe there's an impassioned debate on a GH Issue/Discussion somewhere on why they left it out.
The refactoring of expect to a reusable library (that hopefully everyone could depend on) would be awesome as well. AFAIK there are multiple expect() implementations out there right now, would be nice if there was one.
It’s great for tab completion and can be very smart about types, and newer ones like jest don’t have excessive chaining style. Less bad than it used to be
Everyone replaces `assert(isTrue: boolean)` with more specific assertions so that failures can produce helpful and specific error messages; something like assert.isEqual(a, b) can at least print a and b, but ideally can print the difference between a and b (which Jest does) -- where all `assert(a == b)` can produce is "Oops, assertion failed at line XYZ".
If you expect `assert(...)` to be magic enough to provide diffing and smart error messages because it's actually some kind of macro... then I guess we have different views on magic.
My point about completion is that if you do expect(a).<tab> with a smart library, the library can complete the list of assertions that apply to the type of `a`. Eg, if `a` is an Iterable, a good assertion library could complete `expect(a).toInclude(`. Jest doesn't do type-narrowed assertions (sad) but it could.
> stick a console.log or two in your test and run it again if you're confused
Falling back to manual debugging is essentially admitting the test framework isn't helpful.
I think expect(a).toXXX(b) will provide better velocity for most engineering teams. I'd rather see a detailed failure message at the end of a 15 minute CI run than an unhelpful one that needs code edits to understand. Especially as the team & codebase have grown, I've found the detailed failure messages helpful when people ask for assistance – experts are more likely to figure things out on sight, turning tens-of-minutes-to-resolve into seconds-to-resolve.
It's all a tradeoff. Here's one perspective that was influential on now I think about tests.
> The benefit of this style are better error messages. Instead of just “false is not true”, the testing framework can print values for x and y.
> I don’t find this useful. Using the check style testing, there are very few assertions actually written in code. Usually, I start with plain asserts without messages. The first time I debug an actual test failure for a particular function, I spend some time to write a detailed assertion message. To me, fluent assertions are not an attractive point on the curve that includes plain asserts and hand-written, context aware explanations of failures. A notable exception here is pytest approach — this testing framework overrides the standard assert to provide a rich diff without ceremony.
It's what I call a stupid language trick. Don't get me wrong, they can be fun, and I don't hold things like this against you. But when people want me to write production code like that I want to clock somebody with my keyboard. TDD and BDD types get hot and bothered for this sort of thing since it makes the code look like Gherkin or whatever contrived specification language they favor this month.
I dislike them because it falls into an uncanny valley that messes with my mind.
It's close enough to English that my brain tries to context switch from coding to writing English, but it's not close enough for that to actually work. It feels like I end up fighting my own mind.
I'm glad somebody has tried it so we know what it looks like, but personally I prefer the more "traditional" syntax.
Love this. I wonder if we'll eventually have a new merge à la io.js/node.js
Deno and Node are converging more and more on every release, such a deja vu. The main difference being that deno offers much more as a business than just the "deno" product itself, so an eventual merge wouldn't be so trivial.
I sure hope not. NodeJS is a FOSS project under the Linux foundation (via OpenJS) and is not driven by profit, while Deno is developed by a for-profit company (Deno Land Inc) which has bunch of closed sourced projects to fund the company.
Hopefully, NodeJS learned from the whole Npm Inc history and tries to remain more independent from for-profit companies.
The whole io.js thing was a very different thing than Deno. Contributors tired of slow pace of NodeJS forked NodeJS into another FOSS version, with no for-profit motives, which meant it could eventually be merged back once the people behind the two projects reconciled.
Very good points. Deno is first and foremost a cloud business who happens to offer a Javascript runtime of its own making.
It made us see a world where javascript can be (much more) secure and typed without a compilation step, and this is an important contribution I would like to still acknowledge.
Now the fact that NodeJS is learning from Deno and making strides to support capabilities (are you related? lol) is extremely exciting to me and it will surely improve the state of the Javascript/Typescript ecosystem and pull along a lot of projects/devs who would never have moved to something like this purely due to inertia and dev cost. One can dream.
While I wasn't aware that Deno was owned by a for-profit company, Deno itself is MIT licenced. Is there any particular reason why everything you just said cannot also be true for Deno?
I'm not exactly what I said about NodeJS that could apply to Deno.
> NodeJS is a FOSS project under the Linux foundation
Deno is not maintained by a non-profit organization, and Deno Land Inc are unlikely to want to let someone else control their project, that'd be kind of suicidal.
> is not driven by profit
The organization who maintain Deno is explicitly for-profit, meaning it has to be driven by profit. If they give the project to the Linux Foundation, I guess that could change, but again, unlikely they'd do something like that.
> NodeJS learned from the whole Npm Inc history and tries to remain more independent from for-profit companies
Deno arguably did learn from that story and did package management differently. They didn't avoid the whole for-profit angle, as again, the maintainers are working for a incorporation.
> Contributors tired of slow pace of NodeJS
As far as I can tell as an outsider, Deno seems to be moving forward at a decent speed and I haven't seen any noise in their community about a possible fork because of slow development pace.
> Deno Land Inc are unlikely to want to let someone else control their project
But it shouldn't matter whether or not they want it. You can fork Deno right now, change nothing but the name, and sell it as a commercial product. The MIT licence permits that. iirc SQLite is a private organisation that produces a public domain product. Should we quake in our boots and boycott SQLite? Nothing is stopping the proverbial io.js from drawing a line in the sand, forking Deno or SQLite, changing the name, and taking it from there.
EDIT: Similarly, React and React native are open source but controlled by Facebook. Swift is an open source programming language that's controlled by Apple. .NET Core is open source but controlled by Microsoft. Docker is open source but controlled by Docker Inc. MongoDB is open source but controlled by MongoDB Inc. GitLab is open source but controlled by GitLab Inc. OpenJDK is open source but controlled by Oracle. Redis is open source but controlled by Redis Labs.
Don't think there's ever gonna be a merge, but both Deno and Node contribute to the Web-interoperable Runtimes Community Group (WinterCG) so at least their JS Web API surface should converge.
Testing, and now permissions might hit LTS in October! The velocity of dev while maintaining an arguably stable API after over a decade now is just admirable.
I have been confused recently with the moving best practices. What is the server architecture Next.js is most often plugged into?