290 comments

[ 3.9 ms ] story [ 381 ms ] thread
Quite surprised at how much people agreed with this post. I personally don't think this is such a good idea - it's not that hard to write a general purpose API that covers 80% of obvious use cases, and have app-specific grouping of endpoints for the remainder. And I include things like rollilng multiple requests into one as part of the obvious use case. Because it's usually very easy to implement - either copy/paste, or 5 lines for more complex solutions like an orm, graphql, etc.

It seems like a terrible idea to couple the backend with the front-end so strongly.

Seems like an over correction. Yes trying to make your front end API be your public API is probably a bad idea, but making your front end API completely coupled to the structure of your app structure is probably a bad idea too. Best approach is probably in the middle somewhere.
It probably comes from misusing the API route and feeling they want to keep it simple, but instead heavily coupling things.

A motivated BE that wants to be productive avoiding unnecessary work would probably hate being "support for frontend" whenever they decide to change something and a motivated frontend would probably want to turn themselves to full stack powers to not depend on someone else.

It feels really bad as a pattern unless that webapp is your only concern and is constantly moving or something, or if there's no actually FE or BE but just few people maintaining stuff as a side concern (backend for frontend works there).

I read this and got to the end, and laughed because that's what we're working on (more a composition tool from different data sources using a headless CMS and a Knowledge Graph) - so in the end we do need a general purpose API since that's part of the USP.
I'd caveat that it can be a good idea to make it a general api if you're building a B2B app. These days, purchasing checklists often include "do you have a public api?" Saying no to that can be a sign of product immaturity that is worth trying to get ahead of, and having to maintain internal and external apis is a lot of overhead.

It also can be good as you scale to have an API interface you can have a services team learn how to work with to enable "special" flows for customers.

There are clearly reasons for a backend to expose an API; no one is disputing that. The article simply points out that it should not be the default.
> Imagine if you could just send it the whole “page” worth of JSON. Make an endpoint for /page/a and render the whole JSON for /page/a there. Do this for every page. Don’t force your front-end developers to send a bunch of individual requests to render a complex page. Stop annoying them with contrived limitations. Align yourselves.

Why not just send HTML as a single response at this stage? Sometimes it feels like we are doing web development more complex than it needs to be.

> Imagine if you could just send it the whole “page”

Could have stopped here

Maybe for separation of concerns between data and UI? The browser is not going to be the only client?
you can have seperation of data and UI and still send the whole page.

The vast majority of the time, the browswer is infact going to be the only client.

once you reach the point where its not, then build the api to suit the new client rather then trying to build one that does both.

True, you could insert something in the middle, that generates a page depending on the user-agent. But then it requires extra SSR logic.

But on the other hand, you can have a iOS or Android based app that depends on the same data. So the browser is not the only client in general, especially nowadays, I think?

That's the crux of the question. The BFF approach says that you remove logic from the client and put it in a server-side component. Not that you add extra logic. Then you might find that you need to copy that server-side component sideways for a new client, if the view layer in that client is sufficiently different to what you already have. The browser can be the only client of its BFF API; the mobile apps can either speak to the same API or have their own.

Dramatically more important than any theoretical, abstract consideration in deciding whether this can work is what your teams look like. The client team should almost certainly own the BFF API, which has implications for skillsets and maturity.

That's only for a react type of application if I'm not mistaken?

If so, that doesn't handle the general case (native apps for instance)

No, it's for everything.
Could you explain further? I don't understand.
That's what the database is, isn't it? The separation layer between the raw data and how it's projected into some interface (user or otherwise).
Yes in a way. There are a bit more layers in-between.

In fact, you can see it as 2 decoupled kinds of database:

- the storage database

- the working in-memory UI database that holds the data represented (e.g. on your screen (GUI)).

The goal in general is to modify the data contained in this UI database and then to push the changes to storage.

Well now you’re building a general purpose API
I think there is a middle-ground concern here: avoiding request waterfalls.
This breaks once you need to update parts of the page without reloading everything.
In that case you can just parse the DOM clientside and insert it into the page.
You can still return html partials from your endpoints and use htmx to swap content without a full reload :)
yes, but then it’s not one request per page
Only if there’s an update - htmx will encourage you to render full html pages but still gives you the ability to do in-place updates which is what most folks think a JS front end with a json Api backend and client side rendering is needed for.
JS can update the DOM dynamically.

Nothing prevents the main response from being served as regular HTML and subsequent XHR request for some parts of a page being JSON or some such data format. In fact I bet that sending server-side rendered HTML is faster than sending a full structured page as JSON which has to be decoded, parsed, and then converted to HTML.

I run a popular app (native ios and android) and a web version. I find having A single json serving api much easier to maintain. The frontends are isolated projects. The backend is just a simple layer with static data served as json. If you would have static html it means you need to update all static files whenever a ui change is needed. Makes it overly complicated. Caching becomes also a challenge and the so does the performance. Currently just running a few Hetzner instances to serve 50k requests per second. That would be really hard with a server side rendered html.
> If you would have static html it means you need to update all static files whenever a ui change is needed. Makes it overly complicated

We do use a templating engine and separate CSS files, so no, not at all, all static files does not need updating on every change.

> Caching becomes also a challenge and the so does the performance

Not at all, not even on black fridays.

> That would be really hard with a server side rendered html.

Why do you pose that hypothetical when our real-world experience is easy?

Do you build each interface with different tools (e.g. Swift, Java, and React), or one of the unifying tools (Flutter, React Native, etc)?
Reminds me early AYAX days.
Around 2005 was the first time I "accelerated" a website by requesting the whole page in the background and swapping out what had changed.

We eventually trimmed what was returned a bit, but just getting rid of the page transition was 90% of the work.

That's true. But that's only really a concert if you're building an SPA. If you're not, having multiple pages is generally faster than running some js framework to reload parts of the page. Plus that you get the benefit of every state combination having it's own URL. You don't need to write any custom state reconstruction code.

This was the original design of REST as applied to the web. It was explicitly designed in such a way that it was forbidden to reload parts of the page. This makes it so that every state has it's own URL and you therefore can link to every state. Deep linking if you wish, for all of the web.

The is exactly what HTMX does and it works: allow returning a HTML response and statically annotating a part of the DOM to be replaced with that.

Granted, this requires JavaScript on the front end, but it's a framework that can be shared between different websites, so you're not writing any custom JavaScript for your site.

I think it's about API as such and not about how to serve the webpage.

No matter of JSON or HTML don't built a general purpose API if it's only your frontend.

Feels like the author misses the point of an SPA. If you have website where every "view" is a /page/a etc., SPA might not be the best choice. But in an SPA i can be dynamic, and trigger data retrieval/sending without route changes.

A very basic example is if I have a checkbox that allows the user to subscribe to push notifications. On click I sent a xhr request to the backend POST `/subscribe/1234` which registers the subscription. Next time I'll check `/subscriptions` or similar to see if the checbbox is in "checked" state (i.e. we are subscribed). This is basic functionality which requires a concise REST/JSON API, and has nothing todo with your page/route layout à la /page/a, /page/b etc.

Author here.

We do this for SPA. The POST can be supported just like you explained. Then you can either reload the "page" to see that the checkbox is checked, because backend will include that as part of the page, or (as an optimization) reload just that checkbox if you created a specialized resource for it. The entire page is still there for you to fetch upon transitions.

Among pages, you will still need a few individualized resources sprinkled around for optimizations/reloads/fragment-paginations. The difference between that and a complete API is that they will be entirely design driven. If a table needs paginating, and is built with data from multiple resources, you will not provide 2 resources, and expect front-end to paginate and glue them together. Instead, you provide a complete paginated resource for this table, where data is ready to be displayed as-is.

That us exactly how stuff worked around 2005. And no, you should not do a full form post just to toggle a checkbox.
Why not?
As a user, I can think if a couple of reason immediately:

- I don't like page (re)loads. They are usually slower and more likely to fail, compared to a lightweight request. Especially in scenarios with a bad connection

- If they fail, it's harder to retry, I see a connection timeout page. With a SPA I see an error message, potentially with a retry button. Or even better: the SPA retries for me a few times.

- I can continue to see all the rest of the page while the action is running

- I can potentially start other actions in parallel

- I prefer to not lose any progress of things on the side, e.g. text-boxes where I already entered/changed some text

- It's easier to inspect what goes wrong by looking at the network tab. Okay, most users don't do that, but for me it's still a pro

There are also advantages, but I think nowadays the cons outweight those for me.

I’m convinced this is HN-themed satire.
It seems sincere — unlike your comment, which comes across as very off-hand / dismissive.
Yeah, sorry, but I just don't agree with the spirit of the comment, and I think it's thinking like this which leads programmers to over-engineer web applications, which usually harms user experience and wastes company money.

Absolutist statements like "no, you should not do a full form post just to toggle a checkbox" are just silly. There is no minimum bound beneath which a standard web browser form submission doesn't make sense, and writing extra JavaScript code to not only manage an asynchronous network request but also handle subsequent behaviours for success, failure, timeout, etc., is additional complexity which incurs additional cost and a greater potential for system failure.

Sometimes these implementation details and associated costs are necessary, but in most cases they aren't, and it's not an ideal perspective economically to default to the more expensive and complex implementation, especially for dubious benefit.

> Absolutist statements like "no, you should not do a full form post just to toggle a checkbox" are just silly

That is because you didn't read or accept the frame that I set initially. I was very clearly refering to web apps that are SPAs (not websites!). Within that context, that statement is less absolutist and still true in 95% of the cases.

I mean, you asked a question and I gave some reason that I could think of. How exactly do you "disagree with the spirit of my comment"?

If you don't want to hear an answer, maybe you should just not ask.

I wasn’t talking about whether you were right or wrong. I was calling you out for being a dick and not contributing constructively to the discussion.

You’re talking past people, not taking the time to understand what they’re saying, and then being dismissive when they reply.

Ok. I don't see it that way. Most SPAs on the internet should never have been SPAs and it's usually poor judgement which leads a project in that direction. That poor judgement includes things like the characterisation of full page reloads as overly cumbersome and asynchronous requests as being lightweight. It also includes the expensive decision to try to reimplement the native browser behaviour that you get for free, as the commenter alluded to by suggesting asynchronous requests can be programmed to show a custom UI for error messages and use custom behaviour for retries of failed requests.

I believe I have indeed taken the time to understand what people are saying in these comments specifically and on this topic more broadly. And I just don't agree with the opinions presented. I think they're silly, in the same way that microservices are almost always adopted for silly reasons. The popularity of either approach doesn't negate their silliness.

For more on this topic, this article is good: https://www.timr.co/server-side-rendering-is-a-thiel-truth/

ruby on rails has been able to do all of this with remote forms for at least 10 years now
Very sceptic that RoR can obtain/interact with the browser push notification API as I stated in my example. You'd have to come up with glue JS code yourself, and that is where it gets messy. Pushnotifications just being one of many APIs that are client-only (and again please, if you don't need to interface with those APIs, then you might not need an SPA in the first place).
Just because "remote forms" have forms in their name it doesn't mean that they are conceptually the same thing. They are not, so obviously they can achieve those things, but they are built with js.
Having form elements automatically update the back end is extremely annoying UI imo. There's usually no indication that it's actually doing that, and if there is I generally don't trust it.
How did we get to the point where simple and effective server-side pages are either unknown as a way of doing things or are considered to be a technical heresy?

Younger developers scoff at us fossils and the olden ways, but this is exactly how this ignorance of classic, tried-and-true, performant patterns results in atrocious complexity and performance, only because "this is how things are done".

It HAS to be a BFF, it HAS to be 15 different microservices and 20 different AWS toys, right? There is no other way? Are you sure?

It seems like we are talking about the whole industry, but it's largely he Node ecosystem in my experience (which is effectively - the industry). It's not like there is not Rails development being done out there, with nothing but a trusty Postgres database.

Ironically, our existing architecture patterns are beginning to resemble J2EE - a bulky, slow, expensive, boiler-plate-laden process where a simple site costs one million dollars and performs like shit.

Because JavaScript rules web dev and the JS community has pushed so many mainstream ideas that have turned out to be duds. It’s a major echo chamber due to its size relative to other web dev communities.

I also partly blame the careerist obsession around always learning new technology, because it’s becoming obvious that a lot of the “new stuff” over the last decade created more problems than solutions. Don’t get me wrong, learning is an important part of the job, but we’ve created a culture of constantly pushing new technology to pad resumes and feed our insecurities around irrelevance instead of solving problems.

It seems to me that the more people use <thing>, the less evidence of quality of <thing> is required by the people using <thing> - everyone just assumes everyone else knows what they're doing.

It becomes something akin to a religion - after all, 2 billion Christians can't be wrong, can they?

Eat shit - billions of flies cannot be wrong. (cit.)

More seriously: once a bit of tech becomes mainstream, it becomes politically easier to use it. Your manager has not heard of <cool tech>, but he did read about React in some business magazine. So when the choice is between React and bheadmaster's Funky Funkness Framework, he'll go with React.

Can you pick a less antagonistic example?
Would you prefer I used Judaism instead? Or Islam?

Or do you consider my point about network effect in tech stacks being similar to religious beliefs to be antagonistic by itself?

> everyone just assumes everyone else knows what they're doing.

It’s not just that. There are practical reasons to choose the bandwagon, even if you know it’s not the technically optimal approach.

If you’re planning on hiring junior and mid level engineers, for example, it’s a great help if they already know the technologies involved. Your hiring pool for React is a lot bigger than for a more obscure tool. Additionally, the popular tools also tend to be stable and long lasting, with good support.

So, there are compelling business reasons to choose what’s popular, even if you aren’t making assumptions about the technical quality just because its popular.

Hype, which the web/JS community seems particularly vulnerable to, also plays a big part. Yes proven tech is safe and reliable, but choosing it is so much less fun than getting swept away by the tide of whatever the newest "revolution" is. Don't you want to be part of the wave?
Personally, not for the day job. I love experimenting with new stuff in my free time but when the pressure is on to deliver for the business, I want to go with what I know works.
Not sure what a BFF is, but having separate frontend and backend development doesn't mean you have 20 microservices.
I think BFF might stand for Backend For Frontend in this context
"A BFF layer consists of multiple backends developed to address the needs of respective frontend frameworks, like desktop, browser, and native-mobile apps."

Ok. Yeah that's only for huge scale things.

I don’t find that to be an accurate representation of BFF. I’ve used BFF with a browser FE to simply weave several microservice endpoints into a tailor-made api for the frontend. This reduces the burden on the microservices by removing the need to make many endpoints for clients and helps reduce network calls on the FE
Most of the time time BFF just means that instead of having the frontend call endpoints A, B, and C. It calls a single endpoint D, which calls A, B, and C and handles aggregating and transforming the data into the specific form the frontend needs.
(comment deleted)
Eh, I just call that "good design" under most circumstances.
A BFF is just an API gateway that is tailored to a single frontend. It's especially helpful if the frontend is a native application as opposed to a web application, hence why it was a pattern championed by Netflix. If you have some native client out there and you can't guarantee that the user will update it, it's helpful for it to only talk to a single service so in case you want to evolve your backend architecture, you can update the BFF implementation and the existing native app won't be broken by your backend changes.

Having a native app out there in the wild on customer computers that is coupled to a bunch of different microservices is hell for when you want to change your backend without breaking your customers' experience and forcing them to update.

The backend-per-frontend thing makes sense for Netflix. They're huge scale like I said, plus they've been around a while, so a lot of backwards compatibility issues will arise. I can't see that being a default pattern to jump to, though.

Backend that handles all the microservices so the frontend isn't doing that job, sure. I didn't even consider doing it differently than that.

Huge scale typically leads to the opposite: more backend generalization, not catering to a specific front-end. That's why Facebook built GraphQL — because they run on anything, like fridges, TVs. That's specifically an attempt to avoid tailoring a backend to a frontend. The article argues that at small scale you shouldn't try to build a generalized backend for any imagined frontend. It's exactly how you would get unnecessary performance and complexity bottlenecks, when you really just need to serve your data the 1 or 2 ways that your front demands.
Yeah, our org has a big problem with over-generalization in our internal services that I've been pushing back on. Due to the large size of our org, they're under the false impression that our services are large-scale, but they're actually very small compared to anything external-facing.

When a partner team filed a feature request on our team, and I gave them an API that just does exactly what they need, they were like "that's it? No 1GiB graph response to DFS through?"

I think it's the difference between generations of people for whom JS was an extra thing to enrich HTML documents, vs. younger generations that see HTML as merely a rendering target for JS.
> How did we get to the point where simple and effective server-side pages are either unknown as a way of doing things or are considered to be a technical heresy?

We are not at that point nor we were ever at that point, or close to it.

Server-side rendering has been a hot topic in JavaScript framework circles for over a decade, and React famously solved that problem so well that it is now a standard, basic technique.

Also, just because you throw the same buzzword around that does not mean the problem stayed the same. Reassembling a javascript-generated DOM with a coherent state is not the same as sending a HTML document down the wire. And you know why people started assembling DOMs with JavaScript? Because sending HTML documents down the wire was too slow and did not allowed for any control over aspects that dictate perceived performance.

If you want to build a moderately complex interactive experience that doesn’t suffer from roundtrip latency on every single interaction, you need to recreate the interactive parts of DOM in JavaScript anyway (ignore wasm), else you need to do imperative updates which quickly become a nightmare. jQuery-oriented development is a classic for sure, doesn’t mean people want to go back to it.
> How did we get to the point where simple and effective server-side pages are either unknown as a way of doing things or are considered to be a technical heresy?

Demand for interactivity. A real demand - that’s why desktop apps are now web apps.

It can be over applied, sure, but there are great reasons not to serve html templates. The trend is not some massive incompetence.

Incompetence is the wrong word, but in an industry that grows so fast that 3 years of experience is considered senior, many people aren’t even aware that the old ways are an option.

In my experience, the percentage of large react sites where someone sat down and seriously considered using old fashioned server side rendering, but decided on React because the site actually required a huge amount of interactivity is close to zero.

Every React site I’ve ever been involved with in in the last say 7 years was using React because “that’s the way it’s done”, and anything else was just a post hoc justification.

There's also an interactivity requirement (as in, this is impossible without interactivity) and an interactivity desire (as in, UX folks want an interactive feature).

So many websites now pretend they're in the former category, but would work just fine if they implemented a less-interactive form. That's how it was done in the old days! "No, you can't have that. Here's the best we can do." "Oh, that's fine."

But I expect more of what's driving complicated backend/frontend split design these days is shipping the org chart (in companies large enough to have frontend and backend teams) and technology-by-consultant (where the right technology is whatever you can get a consultant in).

I built a website that was super simple using server side rendering using Django. Then I got a bunch of feature requests that made it less simple and the client-side javascript started getting crazy spaghetti code.

I actually somewhat knew React before this project but besides not wanting to overcomplicate things I was hesitant because I didn't like pure client-side rendering. Then I learned NextJS makes it easy to mix/match client-side rendering and server-side rendering so I just switched to that.

So now, even for a simple website, I'll probably start with React because I don't want to dig myself into a needless hole.

I wrote about the experience here:

https://www.billprin.com/articles/why-i-ditched-django-for-n...

And yes that post upset some people who thought I should use htmx, but React is actually pretty easy and simple. Now most of my websites are React because I know it so it's easy and simple to use what you're used to.

Also, "I did it in React because that's the way it's done" isn't the worst reason because you benefit from that popularity, e.g. some library you need will have clear examples of integrating with React.

I think the frontend and JS community have certainly gone off some crazy rails at points, but I also think there's a popular sentiment on HN exaggerating how off-the-rails things have gone, usually expressed by people who don't actually develop many modern-looking websites. Most of the trends have gotten popular for somewhat rational reasons, and even that stuff that has gotten a little crazy like an explosion in dependencies, is really just about tradeoffs e.g. lots of dependencies cause lots of problems but also enable code re-use so it has pros and cons.

I’ve had the same experience of going fully server rendered to keep complexity down and ending up with having to turn down requests for more interactivity to prevent the code from turning into a spaghetti mess. Maybe not necessarily a bad thing to have less interactivity but it’s definitely a trade off to be very aware of.
Software engineering isn't a hard science, it's impossible to be 100% certain in one of these debates. You could be right and I could be wrong.

But I've been doing this for a long time, and that's what everyone says. I've heard this argument at least 100 times when someone is trying to defend their decision to build something overcomplicated. The number of times they ended up actually being justified is so small that I'm very suspicious of this argument.

I really enjoyed your blog post! (You have a broken link to a time zone post, FYI.)

If you're not expecting to need fancy client side stuff, does next.js give you as good a server side development experience as Django/Rails/Laravel? At that point, it would seem worth it "just in case", as you suggest, but in the past when I looked at it, the SSR stuff still felt a little cobbled together and experimental. I would love to hear from someone with firsthand experience switching, though!

Thanks for the kind words and the heads up on the broken link.

I think Next has a great DevEx, but I'm just using it for some projects as a solo dev without a ton of traffic so perhaps there's some rough corners I haven't experienced yet.

I do think they are going down some risky paths where they do things like rewrite your code files depending on whether you're on server or client, and in general with "framework-defined infrastructure" where if you write certain functions the framework "knows" that it's supposed to be on the server or in a background job or whatever. But so far I've gotten the benefits without the drawbacks, though I might be jynxing myself.

The current SPA paradigm was adopted to solve a number issues that engineering teams were facing. Your sentiment is one I'm seeing a lot more as of recently, which leads me to believe we're approaching the end of the current web development paradigm. There's too much time wasted writing _glue_ when developing SPAs: database queries, back-end controllers, data serialization, network requests, front-end state, shadow dom, etc. Lots of frameworks coming out that essentially remove the need to write glue code. I expect this trend to continue until we reach the point where you are mostly just coding a few things: schema, non-generalizable business logic, and presentation.
Blazor Server, while definitely not the right thing for all applications, is the best solution I've found to this.

You write all the code in one language (C#), and it streams the changes to the clientside as needed automatically. It means you can call the database in your 'frontend' HTML layouts directly and gets rid of all the glue code. You have no serialization problems because you are using the same classes everywhere, and the component structure keeps things very clean.

I would say I am about 5x more productive in Blazor Server than any other frontend technology. You don't need to write APIs at all. It's just like writing static server side rendered pages but you can do dynamic stuff. It is honestly like magic.

It does come with some huge downsides though, in that it needs to keep a websocket open to stream all the DOM changes from the server, and this also has memory/performance issues on the server (needs to keep state on the server all the time). Which basically rules it out of anything that needs to work over intermittent connections (mobile), and very high scale stuff (probably could be done with enough server RAM, but it's not the right tool for that).

However, it's perfect for boring line of business applications that will only be accessed on solid internet connection, which is many of them.

There is also Blazor Webassembly which removes the websocket part, and lets you code frontend and backend in C# still, sharing a lot of classes, though you do need some glue code to connect the database. I've heard people are having good results with gRPC for that; but haven't looked into it much (blazor server works fine for most apps IMO).

Not super famliar with blazor, but something like this sounds like what I would consider the halfway point to the next paradigm. I think you have to go further in simplification of the back-end as well.
Is this kind of what Vaadin does?
Hadn't actually heard of that. Might be similarish but Blazor uses standard html/css/JS and just sends DOM diffs down the wire. Don't think at first look Vaadin works in the same way.
Ah ok. So the server keeps track of what the client should look like and just keeps it up to date?

If so that sounds like a very nice model that would make it a lot easier to get back to just using simple HTML and delegate the complexity to the framework or server process. Are there other projects beside Blazor that use this type of approach?

The need for the open websocket and a heavy server is why I switched from Blazor to HTMX.
As far as I can tell, the SPA paradigm solves exactly one issue: Whiny users saying "but it flashes and reloads the header and navigation between page views and desktop/mobile apps don't.".

Since HTML never had a proper way to compose content from multiple sources-- frames being as close as it got-- we have to instead punt this, like so much else, to JavaScript hairballs to glue it all together.

It is also touted as a way to grow engineering talent pools by subdividing into frontend and backend teams. This is not super convincing to me in a context where the only frontend is HTML given the enormous amount of extra work that a SPA takes to develop. It is more convincing in a context where there are native applications in the mix that would benefit from an API. So what will a future that involves both MPA-style frontends and APIs look like? I think one possibility is that backends will handle data in a more declarative fashion. In theory, doing so would allow a lot of the "glue" to be generated, and ultimately allow more flexibility on the frontend.
It’s because you fossils keep thinking server-side rendering is the right call for everything and won’t consider other possibilities.
It’s because if you want to continue getting a job, you want to use what other people are using.

Using most static page frameworks is like using ColdFusion for your career advancement.

We have a boot strapped and profitable app with thousands of customers written in Django that has been operating fine since 2009 with new entrants raising and spending millions to build fancy BFF/Micro Service solutions, hiring sales people, losing money for years, only to publicly state they have finally gotten to 300 customers.

Job security is relative.

I'm torn. On the one hand, there are many websites I hate interacting with because they are unnecessarily structured as JS clients exchanging data with a remote server via a network link. On the other hand, it's easier for me to develop libraries and CLI clients of my own, these days getting quite far with Firefox dev tools' network view and "copy as curl," and only occasionally having to actually read the code in those JS clients. In the old days, I would have to resort to some ugly and comparatively brittle page scraping.

This new world sucks for the interactive user in a browser (which is me often enough), but it's great for the guy who wants to treat a website as just a remote provider of some kind of data or service (also me often enough).

(comment deleted)
Something about injecting HTML from an API, even from a controlled server, feels wrong.
But that’s the point: its not an API, it’s just an app web server.

There’s nothing wrong with a web server serving HTML; that’s their whole purpose.

Yeah, this goes back a couple of decades where "page a" was just "page-a.asp" or "page-a.php" and you did everything to do with that page in that file. Simple, and you could mostly make changes to page a without worrying that you'd break other pages.

Need page b? Copy page-a.asp to page-b.asp, gut the page-a logic, and put in the page-b logic.

Before you get too nostalgic for these simple days, do we rememember why we stopped developing web pages that way?

Because you can't have a lot of itneractivity with a nice responsive UI like this if your network has high latency and low bandwidth. So we pushed a lot of logic to the browser. Now it turns out it doesn't scale well, so maybe we should go back to copying page a to page b and calling it a day, same way we came back to static linking once disk space stopped being limiting factor.
> ...do we rememember why we stopped developing web pages that way?

Because we needed an API for our mobile apps, and once we had that, SPAs seemed convenient.

It was definitely, absolutely NOT because the page-centric way of building sites didn't work (it does), or because SPAs are better (they aren't).

I’m a systems engineer and SPA’s are a superior architecture to deal with from a delivery POV. It is very difficult to cache a dynamic web app with a CDN. There are ways, like SSI, managing variants, Purging, and so on, but a huge pain. And if you don’t have a sophisticated CDN and are using something like Cloudfront, forget about all those fancy features.

SPA? I can serve the whole thing with static files and remove 90% of the traffic from my web server. The API can be served from a different path from the static files. API requests are super tiny and more tolerant to fluctuating network conditions, and you can hide a lot of that with Javascript.

Exactly. Even non-SPA frameworks (such as NextJS with static builds) mean you can compile all of your frontend code to S3 (where it's a negligible cost) and only pay for the API instances.
I have a hobby project that's very static: a website for viewing webscraped snippets of writing from another site. I hosted it on Github Pages. I didn't want to use a static site generator which would've generated easily 10,000 files and with all the duplicated code would have put me above GitHub's 1GB limit for Github Pages. Similarly Cloudflare Pages has a 20,000 file limit.

So instead I combined it into various JSON files. Now I have a SPA downloading the JSON as necessary, and don't need any API/backend/database. I'm considering moving to sqlite now there are official WASM builds.

>Why not just send HTML as a single response at this stage?

Is it time to discover memcache again? I think it's about time.

This is exactly how next.js works. But even more importantly: Why not use GraphQl in this situation? This is exactly what it excels at:

1. The backend can be entity centric, like it does best 2. The frontend can request a page worth of data from diverse entities in a single go.

Got to pump CV with frameworks you know? Promotions don’t write themselves. Bonus point: job security. And when you leave who cares what poor shmuck will have to support it?
Because you want to paginate. Update the page piecemeal based on an event from another user. Because you want to use the same data on multiple pages, and want to write the authorization and filtering logic once.
Because for the last 10 years, “code schools” and boot camps have taught JavaScript/Typescript with React as “programming” where everything outside of your immediate dom is an api call. One not worth knowing about since it’s “backend”. The reality is SSR has never been a bad idea, we started that way. Now we are seeing reimplementations of it in various forms to “solve the api call hell” that was self induced. Just like the front-end bundle size problem was self induced.
The first 3 sentences of the article are implying that the whole thing is written with "if you must" attitude.

That said, a lot of companies have front-end teams, and those front-end teams commit years to building React components. So even if you render server-side, this is how you'd put data into those components. I'm a backend dev. I don't like some of what frontend is doing, but you gotta work with people, right?

My personal take: Don't put it all in the same box.

Just have a api/v1/rest/ base path for modular well defined resources that can be used across your application, without breaking changes, or even shared to third parties.

Then have a api/v1/features for coupled endpoints that return non standard objects, and with a more flexible lifecycle.

Chances are you're better off having both instead of trying to cram all the use cases into one approach

Most times you won't need a well-formed (REST) API to start with, and many times you will never need it. Most small projects should start without one and create one when need arises.
Sure, you won't "need" them. But it's very likely that you'll have resources that behave very well for rest like operations - user comes to mind, for things like user profile page, user settings, user detail, etc

A rest endpoint is likely handy to use across your application without having to replicate the same serialization over and over again.

Also many frameworks remove all the boilerplate for those operations (eg. Django class views), so creating one is a really good starting point.

I can't agree more: having a separate API for functionality and a separate one for non-standard (i.e. UI) is a great way to do this.
This is good general advice. Sometimes you are building a general library or API, and sometimes there just happens to be a network partition between two components that make one module. My general rule of thumb is that it takes 3-5 times as long to build a library/generic API over regular code. It also requires a very different mindset.

Should you do that sometimes? Absolutely! In big companies you probably have entire teams doing this for API’s that are entirely internal. Just recognise that it is a huge cost and only do it when the benefits are likely to outweigh the costs.

That’s not to say that you should never think about generalisation when writing normal code, (one of my pet peeves is interfaces taking a single “something” when a collection could just as easily have been used), there are many ways code can evolve and some up-front thought can save a good deal of refactoring later. Just don’t write a generic library when domain specific code will do.

This is entirely backwards.

Don't build a general purpose API so your front end can have half the the logic outside.

Just build it the way you would as if it were local, and then hook it up to a back end with a git-like synchronization mechanism. You know, the same way you do all your own development.

If you don't know how to do that, maybe you could learn that instead of arguing over REST vs GraphQL, or whether your line format should care what boxes are on the page.

All I know is, it's fantastic to be able to add features to a front end, persisted in a back end, without having to write code on both sides.

"What about validation??"

Guess what, if users each have their own workspace, isolated and data driven, you don't need to worry about one bad request ruining your whole service.

Could you point me to a sample project/source that does this “git-like synchronisation” you describe? At the moment this proposal feels vague.
Gotta say I disagree with this idea. RESTful APIs have become the norm for a reason, and in my opinion it's dangerous and damaging to suggest abandoning them for a whole swathe of back-ends.

Re: YAGNI. I worked on a product where, about 10 years ago, they decided to build proper APIs (for the first time), mainly to power their own front-end, but they also told the paying clients that they were welcome to use them. Didn't take some clients long to start using and loving those APIs. (Granted, they were enterprise clients, with their own significant dev resources, that's not the case for all software.)

Re: front-end doesn't really have freedom. Yes it does! If one day page A needs a list of X, and there's already a nice generic API endpoint to list X, then only a front-end code change is needed.

No, what we have is everything trending towards a monoculture where every solution is a minor iteration or flavour change to systems that solved problems for companies at scale such as meta/google.

We regularly see developers talking about optimising js loops, dom updates etc in products that are little more than blogs with a few interactive elements.

Most of us work on things that will get ripped out and redone within a couple of years, yet dev culture tells us we should be focusing on developing for every possible future with reuse and scale in mind, and that's unfortunately false.

At my company most code we did in 20 years ago is there and working. Providing b2b service for 5k customers.

Thanks god it’s Java.

Our competitors are lost trying to tinker with the old PHP or redoing everything with cutting edge node/js dead end techs.

You think node js is dead end tech?
I interpreted the statement as, “dead end tech, which happens to use node/js”.
> [pointless changes] in products that are little more than blogs with a few interactive elements.

I wonder if a lot of this isn't busy work to justify having engineers on staff.

You need engineers on staff to make changes when that is necessary, but you don't typically need to constantly be make such changes. At the same time the engineers can't be seen doing nothing, so this type of work is invented.

You are saying engineers should build software, that doesn't frequently require changes to fit some new use case? Outrageous. Then we would have to do proper engineering and make things flexible, anticipating potential use-cases. We would have to heed advice written in books such as PAIP (or more recently "Software Design for Flexibility"). Can we be expected to know literature of our own field?
I agree with everything but

> We regularly see developers talking about optimising js loops, dom updates etc in products that are little more than blogs with a few interactive elements.

In which I see the opposite. Developers don't optimize because "DevEx" is a feature of a codebase, but performance is treated as an afterthought.

> RESTful APIs have become the norm for a reason

What would you say those reasons are though? I think they mostly boil down to cost.

I agree with the article, but mostly for a reason the article didn't press on: performance and responsiveness. Making loads of small requests sucks, and on a mobile, it sucks times ten. HTTP/2 and whatnot have improved things but when you have even a small dependency tree of data, you can't avoid the network latency and it can quickly add up to a second or more of unresponsiveness.

Obviously this doesn't apply (or matter) in some circumstances, but in many applications I think it's worth the extra cost.

> and on a mobile, it sucks times ten

People often like to design for mobile devices but not for mobile connections.

There's a well-established answer to this that I've done for years that satisfies both and it's the BFF pattern. The crux of which is an orchestration tier. A simple pass-through API gateway which can interface with all your thoughtfully-designed, single-purpose APIs, extract and transform one or more payloads and serve up a single payload that maps one-to-one with a frontend. I've deployed this architecture for years on some large-scale sites. It works very nicely. Bonus is that you can manipulate and even swap out entire backend pieces without worrying about your frontend.
qlio [1] was a really elegant implementation of this pattern before it was named and popularized. That provided a nice decoupling of the FE/BE teams by allowing very quick iterations, with an early means to compose apis to simplify the marshalling.

[1]: https://tech.ebayinc.com/engineering/announcing-ql-io

I also disagree.

I agree that pretending you have multiple clients sets up a more difficult bar to meet. I agree that bar might be overkill for some projects.

The idea that it is overkill for all projects is a leap I can't follow. Optimizing for greenfield development speed is something inexperienced devs often do, and that's what this feels like.

The other added benefit is sharing the data with other teams, and of companies.

Everything has the same flow. And if a custom api call is needed for a particular frontend page, it can always be made. But won't be the norm.

Don't think I could disagree any harder. Requirements change. Business needs change. You want your FE to be able to evolve independently without having to wait for the BE to implement the precise logic you need.
As well as your FE team forced to wait on BE changes for new features, you've also created busywork for the BE team anytime there's a FE refactor/restructure to boot.
An organisation that separates frontend and backend teams will naturally converge to an architecture where the collaboration between frontend and backend is minimized (i.e., a generic REST API) -- Conway's law.

This doesn't mean that an actual cross-functional team that contains both frontend and backend developers can't use the proposed single-purpose API to run circles around the split team.

Regardless of cross-functional teams, I think having to make changes at an API level at the backend for UI changes will negate a lot of the benefit.

For clarity, I don't think you need to treat your own front-end like an external consumer & think it's fine to have bespoke endpoints as needed.

What I'm skeptical of is the author's suggestion that a backend endpoint brings back presentational info like "leftBoxTitle".

The way HN is build is the best way to build web applications.

There is a concept of a "page". A page is loaded as an html page. This thread for example is a page.

And then there is JavaScript which does all kinds of dynamic stuff on a page. Like when you update this commment or when you collapse/expand comments.

I disagree. I understand a lot of people enjoy the simplicity of HN, but when you find a developer who really knows how to take advantage of the additional "bells and whistles" that come with modern browsers, without making them annoying, the experience can be significantly better than HN.
Sites like Reddit, Twitter, Facebook would be 100x better if they would be built the HN way.

If these companies don't have the developers to show that "Bloated React Single Page General API sites" make a better UI then who has?

Can you elaborate on this? I'm wondering what those bells and whistles are, or how the experience could be "significantly better."
Like not having to go through two page loads just to add a comment.
I don't think this applies to bigger apps like an excel or Photoshop. I don't think HN is very "appy", to be honest.
is the first time that i see that idea, sounds crazy and out of the box imagine a real project using it..
Not so crazy. I can name at least 2 of the top 5 North American airlines running this in production for Mobile and Web supporting hundreds of millions of user requests, daily.
As a user, this approach often leaves things in a difficult state. The front end works, but automating data access is a hard slog through inconsistent API that's not fully revealed. Reporting is dependent on what is planned for the front end, not what is needed by the user. Because the front and back are tightly coupled, everything is fragile unless it's baked in.

I think many business apps should focus on having an API be a first-class citizen, and there needs to be better standardization in how they're organized and used. GraphQL could be part of that solution. It's also nice when the API lets you do the things you can do from the front end as well as more data-oriented tasks. For example, in Zoho Books, reporting isn't automated by the API, you'd have to roll your own or script the UI. The API should cover both reading and writing data items like transactions and downloading a PDF or Excel report using the reporting interface.

The past 2 years have seen some advancements in full-stack frameworks with streaming components. Such as NextJS with React Server Components or .NET Blazor.

Those would be my preference for an app that doesn't need a public API. If each partial layout and page component does it's own data loading server side, you can skip GraphQL or making a Full Page API and partial update APIs.

Part of the problem with this is organizational though. For the past decade teams have largely been split between frontend/backend who typically work in 2 completely different languages/frameworks. The steaming components approach is a switch back to full stack and a single language/framework. Also every language doesn't have one of these streaming component frameworks, so it's not really possible to do an app completely in Go for example.

Based on my personal experience general purpose APIs lead to backends for frontends, which is a colossal footgun unless orchestrated by a mastermind. Even if you did have that kind of mastermind, if that person decides to leave you're left with a very expensive puzzle for everyone else to solve.

Here's my take: don't build general purpose APIs, build general purpose domain actions. A user making an account is a domain action. Sending a notification to the user is a domain action. Make doing each separately easy. Make doing both at the same time easy. Do this with all of your domain actions and now you have an easy way of building exactly the kind of endpoints all your front ends need.

Curious about this take, since I've done it successfully many times. Orchestrating a backend for frontend is usually a pretty trivial task. It's usually just mapping one json to another.
Here's just some of the things that become non-trivial in such a scenario:

* Validation - you either replicate the same rules on the generalized API/BFF/FE and risk inconsistencies or figure out a way to extract that validation to a shared package

* Making API calls inside the BFF - usually generalized APIs have a lot of features in individual endpoints, some of which aren't always documented so you'd need a really good documentation that gets constantly updated or some kind of a connector package that makes it easy to discover those features

* Testing - most of your endpoints are just calls to another API, all you can do is mock those calls in your tests based on what you expect the API to return but this gets hairy if you're just copy pasting the same 200 line JSON body across multiple tests so you'll need some helpers/a package for that too. It also requires that you always have a person on your team who's very familiar with how the API you're making calls to works so you don't make false assumptions

You either need someone who's aware of every little detail that goes on in both the generalized API and the BFF or you need all of these constant abstractions to be able to scale properly in any shape or form without introducing bugs with every new feature.

The problem is not building a general purpose API, the problem is over thinking it. There’s a sweet spot where you build the API just slightly more flexible than your FE needs at the time, yet without wasting time on making it fit ‘all possible future use cases’.

Then you can later on whip up a mobile app if needed, let the clients use it, whatever. And evolve it accordingly.

I disagree. Trying to generalize a single implementation into an interface is always problematic. And that's exactly what inserting an API between the FE and BE is if your only UI is a web page.

If, later on, you want a mobile app, then an API can easily be deduced from your current, working UI, instead of trying to imagine what the UIs of your web FE and mobile app have in common.

This article is good advice to get a working prototype or a decent solo-dev MVP website that will be re-written later. I've spent the last two months cleaning up the work of a developer who thought this way. Here's what you run into:

* Front end and back end become conflated. Often times code that should run on the backend runs on the front end and vice versa. Often shortcuts like saving ui state using the same api endpoint you use to save data become complex, and slow down improvements to both the ui and the public API. Likewise data manipulation and in some cases even validation gets done on the wrong end, too.

* You confuse UI code with application functionality. Code used to save the order of columns, stuff you entered the last time a dialog was displayed, and the convenient data structures (i.e. easy, not best) for the front end leak into the back end. So you end up spending a lot of time building back end functionality that has literally no utility to anyone else other than the front end developer.

* You lose focus on your other users: developers who integrate and build on your product. Eventually, those developers become in-house people who are doing integrations and implementations for customers, and a messy API will really slow them down.

Better advice: separate the concerns and have a separate API UI backend. Build exactly what you need to deal with UI state, and component specific UI settings. Your front end team is a perfect proxy for a third party developer for your public API. Take advantage of that. If they can't use it, or they get paper cuts, then paying customers will have problems, too. You'll also be able to on-board developers faster (easier training) and have better documentation.

Do I understand your advice/recommendation correctly?:

frontend <--> UI API, only has what frontend needs <--> backend service with its own API

In that case: What design do you suggest for the backend's actual API?

For me back end and front end being conflated is a good thing. Because most of the time it ends up one application anyway.

If you need generic API you can make separate application that will be API.

So if you make domain driven design your front end is separate concern. API for third parties is separate concern.

Still in database you might have stuff not used by external parties but then you don’t expose it in your external API. But you can make models on your front end API.

Making “generic” solution might seem like saving time and money but as times go by and it turns out you have to fight with generic API to get things done because one is stuck in his ways - it definitely pays off.

I have some experience with it both as a user and a developer.

Code used to save the order of columns, stuff you entered the last time a dialog was displayed, and the convenient data structures (i.e. easy, not best) for the front end leak into the back end

As a user, I want the column order to persist when I’m moving between PCs or browsers or private windows.

You lose focus on your other users: developers who integrate and build on your product

Having done enough integrations, I prefer “what you see is how you work with it” mode. Because requirements usually come in a form of “there’s a button on page X, it produces a table, take it”. But when you approach it from the API side, this user story appears smeared across complex frontend code with numerous API calls and you basically have to reverse-engineer this part. Includes hours of comparing sparse docs against the network activity tab in the inspector pane. Very tempting to just $.click() the button and rip these <tr>’s out of it.

That said, I don’t think you don’t need APIs. But your either-or dichotomy is wrong. If you need an API, then build it. But it has nothing to do with how pages work. Because if pages do something clever, and there’s value in it, your API users will have to repeat it. Why not push that cleverness back to where it belongs.

And finally,

Front end and back end become conflated

It’s a whole system. Drawing an arbitrary line between graphics and logic not where the boundary between them is and repeating it in company’s structure – that creates a point of view where they conflate. If you don’t do that, there’s no conflation. Everyone just does the whole task like “add an input there from this db column”, without having to start complex interactions or negotiations. This separation is synthetic.

I've built both models (and more too) over decades.

There are lots of questions one might ask before deciding between these two (or other) approaches:

* Where does this team want the boundary between client and server in terms of user experience, server load and substantial UX interaction latency?

* Can this frontend team independently plumb a changes all the way through the database?

* Are backend teams always available for (or interested in) every minor update?

* Are both halves of the application deployed on the same schedule?

These are a handful of the first questions I'd ask before trying to push either solution at this point in time, the answers would likely lead to more questions.

(comment deleted)
When are we going to get away from all the YAGNI engineering that has sold short the promise of the Web for more rented silos, and get to something more like HATEOAS?
Think this is describing other frameworks that do this. Isn't this how ELM works? Or ELM architecture in general?

This is kind of how some 'functional' web api's work.

I'm finishing a project now, using exactly this design pattern. Sort of backed into it, motivated pretty much by the issues outlined. In addition, I wanted something that is clear and sustainable far into the future. The simplicity of this approach supports that. Quite liberating.

Complexity is the enemy.

True, one might reasonably propose a number of what-if objections. But for many (most?) projects), imo this is a good approach.

And the htmx-ification trend continues. I guess this is sort of the counter movement now to all the API-First/Data-First/SPA-First evangelism of the last few decades.

I don't quite understand why though. Or rather, why those posts always seem to have an intense emotional dislike to anything data-driven.

What exactly is wrong with just using React? Or, heaven forbid, plain Javascript, which browsers have spent the last decades optimizing it make working with REST APIs and JSON as easy as possible.

Can you expand on "just" using React? Are you saying "what is wrong with using React (the framework including React the library and all of its friends)?", or are you saying "what is wrong with just using React (the library) only?"
I was more thinking of React, the framework, with as little addons as possible. React's data model always seemed the most reasonable to me, if you have to use a framework or library at all.
My experience working on "medium sized" B2B apps in 10-20 people teams is that the cleanest way to design API-s is to have a regular REST API for the basic CRUD operations on the entities, and then have more specialized endpoints for specific pages, tables, dashboards etc. on top of that. Having specialized endpoints is kind of required, as the data often needs to be searchable, sortable, filterable and paginated, which would be pretty wasteful, or sometimes even impossible to do on the frontend. I find these kind of REST API-s pretty straight forward to design and implement for the most part, and as others pointed out, onboarding is extremely easy if the new developer on the team has worked in software dev at least for a year or two. This also makes it pretty easy and quick to transition to a fully public API if needed (although I have rarely seen this as a requirement).

Edit: fixed a typo.

This is BFF pattern described many years ago. Here's the standard way I build this kind of thing: Develop your APIs per functional or really per system. One for content, one for identity, one for ads or whatever. Then you build an orchestration tier that does pretty much what OP says. It stitches the various services together and returns a page model. The ideal is to reduce round-trips and cognitive load on FE dev. If you need the top 3 pieces of relevant content, plus an ad plus the user's first name, return all of that and nothing else. The last piece is just SSR. Use next or nuxt or whatever and you can output HTML on the server or stich it on the client with one codebase seamlessly.

https://samnewman.io/patterns/architectural/bff/