Live data from Hacker News

Denial of service and source code exposure in React Server Components

react.dev

221–230 of 232 posts

Re: Denial of service and source code exposure in React Server Components

#221

Earlier quoted context omitted.

This is how client-server applications have been done for decades, it's basically only the browser that does the whole "big ole requests" thing. The problem with API + frontend is: 1. You have two applications you have to ensure are always in sync and consistent. 2. Code is duplicated. 3. Velocity decreases because in order to implement almost anything, you need buy-in from the backend AND frontend team(s). The idea…

The problem with all-backend is that to change the order of a couple buttons, you now need buy-in from the backend team. There's definitely a happy medium or several between these extremes: one of them is that you have full-stack devs and don't rigidly separate teams by the implementation technology. Some devs will of course specialize in one area more than others, but that's the point of having a diverse team. There…

Communicating over HTTP comes with pretty much as many physical boundaries as possible. The main problem, and power, of APIs is their inflexibility. By their design, and even the design of HTTP itself, they are difficult to change over time. They're interfaces, with defined inputs and outputs.

Say I want to draw a box which has many checkboxes - like a multi-select. A very, very simple, but powerful, widget. In most Web applications, this widget is incredibly hard to develop.

Why is that? Well first we need to get the data for the box, and ideally just this particular page of the box, if it's paginated. So we have to use an API. But the API is going to come with so much baggage - we only need identifiers really, since we're just checking a checkbox. But what API endpoint is going to return a list of just identifiers? Maybe some RESTful APIs, but not most.

Okay okay, so we get a bunch of data and then throw away most of it. Whatever. But oh no - we don't want this multi-select to be split by logical objects, no, we have a different categorization criteria. So then we rope in another API, or maybe a few more, and we then group all the stuff together and try to splice it up ourselves. This is a lot of code, yes, and horribly frail. The realization strikes that we're essentially doing SQL JOIN and GROUP BY in JS.

Okay, so we'll build an API. Oh no you won't. You can't just build an API, it's an interface. What, you're going to write an API for your one-off multi-select? But what if someone else needs it? What about documentation? Versioning? I mean, is this even RESTful? Sure doesn't look like it. This is spaghetti code.

Sigh. Okay, just use the 5 API endpoints and recreate a small database engine on the frontend, who cares.

Or, alternative: you just draw the multi-select. When you need to lazily update it, you just update it. Like you were writing a Qt application and not a web application. Layers and layers of complexity and friction just disappear.

Re: Denial of service and source code exposure in React Server Components

#222

Earlier quoted context omitted.

This is how client-server applications have been done for decades, it's basically only the browser that does the whole "big ole requests" thing. The problem with API + frontend is: 1. You have two applications you have to ensure are always in sync and consistent. 2. Code is duplicated. 3. Velocity decreases because in order to implement almost anything, you need buy-in from the backend AND frontend team(s). The idea…

You have two applications you have to ensure are always in sync and consistent. No, the point of the API is to loosely couple the frontend and backend with a contract. The frontend doesn't need to model the backend, and the backend doesn't need to know what's happening on the frontend, it just needs to respect the API output. Changes/additions in the API are handled by API versioning, allowing overlap between old and…

> No, the point of the API is to loosely couple the frontend and backend with a contract. The frontend doesn't need to model the backend, and the backend doesn't need to know what's happening on the frontend, it just needs to respect the API output. Changes/additions in the API are handled by API versioning, allowing overlap between old and new.

This is the idea, and idea which can never be fully realized.

The backend MUST understand what the frontend sees to some degree, because of efficiency, performance, and user-experience.

If we build the perfect RESTful API, where each object is an endpoint and their relationships are modeled by URLs, we have almost realized this vision. But it cost us our server catching on fire. It thrashed our user experience. Our application sucks ass, it's almost unusable. Things show up on the front-end but they're ghosts, everything takes forever to load, every button is a liar, and the quality of our application has reached new depths of hell.

And, we haven't realized the vision even. What about Authentication? User access? Routing?

> Not if the frontend isn't trying to model the internals of the backend.

The frontend does not get a choice, because the model is the model. When you go against the grain of the model and you say "everything is abstract", then you open yourself up to the worst bugs imaginable.

No - things are linked, things are coupled. When we just pretend they are not, we haven't done anything but obscure the points where failure can happen.

> Velocity increases because frontend works to a stable API, and backend doesn't need to co-ordinate changes that don't affect the API output. Also, changes involving both don't require simultaneous co-ordinated release: once the PM has approved a change, the backend implements, releases non-breaking API changes, and then frontend goes on its way.

No, this is a stark decrease in velocity.

When I need to display a new form that, say, coordinates 10 database tables in a complex way, I can just do that if the application is SSR or Livewire-type. I can just do that. I don't need the backend team to implement it in 3 months and then I make the form. I also don't need to wrangle together 15+ APIs and then recreate a database engine in JS to do it.

Realistically, those are your two options. Either you have a performant backend API interface full of one-off implementations, what we might consider spaghetti, or you have a "clean" RESTful API that falls apart as soon as you even try to go against the grain of the data model.

There are, of course, in-betweens. RPC is a great example. We don't model data, we model operations. Maybe we have a "generateForm" method on the backend and the frontend just uses this. You might notice this looks a lot like SSR with extra steps...

But this all assumes the form is generated and then done. What if the data is changing? Maybe it's not a form, maybe it's a node editor? SSR will fall apart here, and so will the clean-code frontend-backend. It will be so hellish, so evil, so convoluted.

Bearing in mind, this is something truly trivial for desktop applications to do. The models of modern web apps just cannot do this in a scalable, or reliable, way. But decades old technology like COM, dbus, and X can. We need to look at what the difference is and decide how we can utilize that.

Re: Denial of service and source code exposure in React Server Components

#223
post #107

Earlier quoted context omitted.

You can still have separate codebases for server and client in JS/TS...

> You can still have separate codebases for server and client in JS/TS... Indeed, but unlike Go/Python (backend) and TS/JS (frontend), the separation is surmountable, and the push to "reuse" is high.

> and the push to "reuse" is high

Other than types and stuff like zod validators there's not a lot of overlap between server and client code.

I agree with your point that iso code can be confusing. But beyond that I think you're just pushing an irrational anti JS narrative.

Re: Denial of service and source code exposure in React Server Components

#224
post #178

Earlier quoted context omitted.

I never saw brilliance in his contributions. Specially as React keeps being duct-taped. Making complex things complex is easy. Vue on the other hand is just brilliant. No wonder it's creator, Evan You went on to also create Vite. A creation so superior that it couldn't be confined to Vue and React community adopted it. https://evanyou.me

There's no need to take down and diminish other's contributions, especially in open source where everybody's free to bring a better solution to the table. Or just fork if the maintainers want to go their way. If your solution has its merits it will find its fans.

That's utopia.

While everyone is free to fork and maintain React. It's by no means an easy task, specially if it's not their job like Dan's is.

Plus, industry tends to gravitate towards what is popular. Network effects an all. So if a massively popular tool is subpar, the complications of it aren't without impact.

And no one is immune to criticism. LLMs are criticised for their sycophancy but some humans are no different when it comes to gatekeeping criticism.

Re: Denial of service and source code exposure in React Server Components

#225

Earlier quoted context omitted.

The problem with all-backend is that to change the order of a couple buttons, you now need buy-in from the backend team. There's definitely a happy medium or several between these extremes: one of them is that you have full-stack devs and don't rigidly separate teams by the implementation technology. Some devs will of course specialize in one area more than others, but that's the point of having a diverse team. There…

Communicating over HTTP comes with pretty much as many physical boundaries as possible. The main problem, and power, of APIs is their inflexibility. By their design, and even the design of HTTP itself, they are difficult to change over time. They're interfaces, with defined inputs and outputs. Say I want to draw a box which has many checkboxes - like a multi-select. A very, very simple, but powerful, widget. In most…

There's a lot of different decisions to make with every individual widget, sure, but I was talking about political boundaries, not physical ones. My point is that it's possible for a single team to make decisions across the stack like whether it's primarily server-side, client-side, or some mashup, and that stuff like l10n and a11y should be the things that get coordinated and worked out across teams. A lot of that starts with keeping hardcore True Believers off the team.

Re: Denial of service and source code exposure in React Server Components

#226
post #3

Earlier quoted context omitted.

That was indeed one of the main points of SPAs, but React Server Components are generally not used for pure SPAs.

Correct, their main purpose is ecosystem lock-in. Because why return json when you can return html. Why even build a SPA when the old school model of server-side includes and PHP worked just fine? TS with koa and htmx if you must but server-side react components are kind of a waste of time. Give me one example where server side react components are the answer over a fetch and json or just fetching an html page?

[deleted]

Re: Denial of service and source code exposure in React Server Components

#229
post #86

We pioneered a lot of things with Opa, 15 years ago now. Opa featured automatic code "splitting" between client and server, introduced the JSX syntax although it wasn't called that way (Jordan at Facebook used Opa before creating React, but the discussions around the syntax happened at W3C notably with another Facebook employee, Tobie). Since the Opa compiler was implemented in OCaml (we were looking more like Svelte…

Note that the exploits so far haven’t had much to do with “server code/data getting bundled into the client code” or similar which you’re alluding to. Also, RSC does not try to “guess” how to split code — it is deterministic and always user-controlled. The vulnerabilities so far were weaknesses in the (de)serializer stemming from the dynamism of JavaScript — ability to hijack root object prototype, ability to toStrin…

The problem we tried to solve with Opa was more general than RSC, probably too general.

    // Opa decides
    function client_or_server (x, y) { ... }
    // Client-side
    client function client_function(x, y) {= }
    // Server-side
    server function server_function(x, y) {... }
Without the optional side inference (which could also use both), it seems we had similar side constraints, and serializers/sanitizers. Probably with the same flaws as the recent vulnerabilities... Like all the OWASP AppSec circa 2013-2015 range of exploits in browser countermeasures when the browsers where starting to roll out defense in depth with string matching :)

Re: Denial of service and source code exposure in React Server Components

#230
post #117

Earlier quoted context omitted.

Yeah. Being able to write code that's polymorphic between server and client is great, but it needs to be explicit and checked rather than invisible and magic. I see an analogy with e.g. code that can operate on many different types: it's a great feature, but really you want a generics feature where you can control which types which pieces of code operate on, not a completely untyped language.

It is explicit and checked. You have two poison pills (`import "server-only"` and `import "client-only"`) that cause a build error when transitively imported from the wrong environment. This lets you, for example, constrain that a database layer or an env file can never make it into the client bundle (or that some logic that requires client state can never be accidentally used from the stateless request/response cycl…

> You have two poison pills (`import "server-only"` and `import "client-only"`) that cause a build error when transitively imported from the wrong environment.

Sure, but even though it's "build time" it feels more like runtime (indeed I think the only reason it works that way is your code is being run at build time). I should be able to look at a given piece of code and know locally whether it's client-side, server-side, or polymorphic, rather than having to trace through all its transitive imports.

Post reply on HN