Join me while I learn HTMX and talk at you about it
Twitter: @htmxlabs
So I want to call this episode The Advantage of Being dumb. And what I'm talking about here is the client side. The client being dumb. The client side being the browser. Because when you are using hypermedia and sort of use embracing, you know, pulling in HTML, putting it into the right spot on the page, and using the DOM as your your main application state.
Lazarus:The client is ignorant of what's happening. It doesn't all it knows is there's HTML that it puts on the page, right? It doesn't know, it doesn't have like these, javascript models, with particular business logic in them, with your data, the browser all it knows is there are buttons and links and it will respond to your commands. Doesn't know anything about those commands. Meanwhile, you on the server side can be kind of omniscient, and just sort of, you know all the business logic, all the different models, you've got your back end set up.
Lazarus:That's fine, you you know everything like that, but the client side, the browser, is just a rendering machine. That's it. It just renders what you spit out to it. So this is in contrast to a front end kind of SPA, way of building. In a front end single page app, your JavaScript knows everything, so it's very smart.
Lazarus:You know, this is sort of the the 2 different kind of, you know, models we're talking about here. So in that model, you make an API call, you get back JSON, and then the JavaScript has you know, the JavaScript engine kicks in and it interprets the data it receives from your server. So if you've you've probably done this. Most people in web development use APIs all the time, JSON APIs. And, you know, if you've used Vue, React, Angular, Ember, all these things.
Lazarus:That that's sort of the the general model. So it's your responsibility to sync your front end and your back end. Your front end needs to know quite a bit about how your system is set up. It needs to know, you know, which which objects are in your system. You you probably have a model for each 1 and maybe a component that displays that model, or several.
Lazarus:You know, you have you have a lot of different ways to sort of show your stuff, so so so you're just pulling in the model, you know, a list of of these kind of, data models, and then your JavaScript is has its business logic to show the different things. So and if you have a if you let's say you have a you're part of a bigger company, or you just have sort of a front end back end split in your company, you know, this this API, this JSON API, that's where the 2 teams talk. That's how they talk in the API description. So these conversations between the the front end and the back end can get very animated because each 1 depends on the other. A small change on either side can break things.
Lazarus:So you have to be, careful. Right? So, you know, hopefully you get you've sort of experienced this, and and, can sort of get an idea for for that's the that is a very standard way of doing things. Everybody's used to this, if if you've worked in that sort of environment, and worked on projects that sort of use this SPA mode. So, let's give a specific example of of what the difference is between between the 2, modes of doing stuff.
Lazarus:So, this is the example I'll give is mostly from the hypermedia systems book, page 47. This is something that happens all the time. So let's say you have a table, and we'll just give it like, you know, let's say it's all startup ideas, like startup names. So you have a list, a bunch of people, a bunch of users have added these. Each 1 has a delete button.
Lazarus:Right? So this is just a single page we're talking about, but if so let's say you're working in on an SPA, so the table is smart. What you've done probably is you've pulled in a JSON array of just these startup names, and each 1 has an ID and maybe some other info, but, you know, it's basically just let's say it just hasn't the JSON you pulled in just has the ID and the name for now. So your JavaScript knows what every item in that table is. Right?
Lazarus:So it it has an idea these are, you know, startup names. You probably have a, a component somewhere or, some sort of class or model in there in your JavaScript that says what it is, and then it's got some functions to display, some different things. So it also knows what actions are available. So you've probably got on your front end in your JavaScript, description of that class or that model, you've probably got some functions like, show or in this case let's talk about delete. But somewhere in the JavaScript memory on the client in the browser it knows which items can be deleted.
Lazarus:So it adds the visible delete button to the DOM, so you're displaying it in a table, each of these, and then you click delete. The client sends the delete, you know, post URL or delete URL, whatever it is. It gets the JSON success back, and then the row is updated to the deleted state, whether that's removing it or, you know, putting an undelete button. Whatever that is, that's handled on the client side. Mostly your your server just sort of lets you know, okay good, the delete worked, so that so that you can handle it there.
Lazarus:So so far so good. Right? We're we're just this is all standard fine. Now how would you do that with an HTMLX hypermedia setup, where the client knows nothing? It doesn't have a model of the startup names and stuff like that.
Lazarus:That would be similar, but you'd render that table with each item, and each 1 would contain the HTML with the delete action, so you're not sending you're not just getting a JSON back and interpreting it, you're just getting a request to see the table and it you're getting back the HTML with everything, including the delete button. So when you click the delete, it would hit the server and then probably, you know, update that row of the table. And this is sort of standard stuff, right? We're both getting both of these approaches are getting to the same place. You have your list of items with a delete button, the delete works, you know, in the HTMX version it's maybe, updating that row and and adding the new buttons that are available there.
Lazarus:Okay. So far so good. Now, what happens in each of these scenarios when you make a change to your system? So to your list, let's say you want to make it so that you can only delete your own entries. So rather than just indiscriminately showing a delete button, now we want to say, okay, wait, check which user added this.
Lazarus:We don't want somebody to be able to delete somebody else's entries. Pretty reasonable. So so now the system needs to know which user created the item. And let's just assume that wasn't originally sent, so this is a change. So in an SPA, your JSON would need to be updated to pass the user ID who created it, and your client side would need to be updated to do a check and see if the delete button should be there.
Lazarus:It would need to update the logic and say, okay, you know, is does this user match the current user? Then show the delete button. So the API and the client in this case need to match. They need to sync exactly on this change. If the client doesn't have the updated logic, so the browser, if it in the javascript, if it doesn't have the updated logic, it will let you delete any of them because, you know, that's what it did before.
Lazarus:There's no change. The only, you know, the a the API just sent a new thing, the user ID. So if the client isn't updated, it just lets you delete any of them. So that's a bug, obviously. And then if the API doesn't have the updated field, then the client won't be able to check the user ID and it might error out because it's expecting to see a user ID.
Lazarus:So the point here is that those 2 things, whatever change that is, that has to be done exactly at the same time. The client has to be updated with the API. So I would say, you know, anyone or everyone who's worked with an SPA or worked with APIs will recognize this scenario. This is why APIs so often, or this is why they not always have, but they probably need versioning, because if your client has an outdated version then it will break. So you have to match the clients that are consuming them.
Lazarus:And so so what you'll do is you'll set up, you know, and then this is, like, bigger companies will do this, but it's necessary, you know, at a lot of a lot of different levels. You when you make a change in both, you just need to be able to make sure that that the change is reflected on the client, and the API. And some of them are trying to serve multiple front ends, some of these APIs. So those front ends, those clients, might update at different times. So you have to have multiple versions of the API out there, and and you define which version of the API you are consuming as the client side, and you have to be very careful which 1 is being used for this purpose.
Lazarus:So that's that all makes sense. Right? I mean, that's just standard practice. You just have to keep them in sync. That's fine.
Lazarus:Now, this is the key difference switching to a hypermedia setup, where the client knows nothing. There is there there's no syncing in that scenario. Instead, when you need to update the functionality, the server will just send the new HTML that's needed. Right? So in this case the server would check each row as it's, you know, initially drawing it, determine which get the delete button by checking the user ID, and that's it.
Lazarus:It'll show the delete button when the use when it should be there, and it won't show it when it shouldn't be there. There's no versioning. There's no syncing. The client, which is dumb, is just going to get the current state every time, from the server. Just, boop, there it is for whoever is is using it at that time.
Lazarus:You know, they're gonna get the updated thing. There's no versioning. There's no syncing. And this is true of everything in your app. Every little change you make, when you're using an SPA setup, you have to make sure that these things are synced.
Lazarus:So with the hypermedia setup there's no versioning, no syncing, and nothing in this scenario breaks because you are just sending the current state every time. You send what you need them to be able to do. So in this case, you know, you need some of them to be able to delete and some of them not. Soon as that page renders, you've given them the controls right there that they can use. So this is the flexibility of, you know, it's abbreviated HATE0AS, hatoa hadioa I don't know exactly the best way to pronounce that, but it's hypermedia as the engine of application state.
Lazarus:And and this just means your HTML, your DOM is the application state. You don't have this, like, big master application in memory in your JavaScript memory. So over time, you know, your app is going to get much more complex in every part of it. So when you're using APIs and syncing with clients, you'll need to communicate every change between the API and the client somewhere, whether that's between 2 teams, or whether you're just solo developing and it's just with you. You need to keep track of here's what my client knows, here's what my client can do, and here's the API that it needs.
Lazarus:Right? So whether that's versioning, or emails, or just documentation. And all that stuff, that the changes and the communication about the changes, you know, people can call this like API churn because the API itself is always going to change, of course. Like your app changes. It needs to change, but it needs to change in a different way.
Lazarus:It needs to change in a way that the client won't get left behind. It needs to understand understand these 2 things need to sync. So maybe you've done this, and maybe this is just, like, part of how you're used to working. But if you're using a hypermedia setup and just sending the exact controls you want the user to have using the DOM as your state, you just don't need to think about all of that. Your application is just updated immediately for everyone, and those changes and the versions and the documentation, that's not building up over time.
Lazarus:And that's the power of the the, like, dumb client. It's that flexibility. You know, it doesn't need to know the object model. You you've already got that on your server side. You have to have that on your server side anyway.
Lazarus:You know, the business logic of your app, the admin roles, all that stuff. You need you're already doing that on the server side. I don't care what system you're building. You're doing that on the server side. You should be.
Lazarus:You you know, you sort of better be, because their server side is is you you cannot trust the client side to make the sort of business decisions, validation, all this kind of stuff. You have to be doing it on the server side anyway. So rather than doing it on the server side and then duplicating it, copying it, putting it within the business logic, doing that on the server on the client side as well, the admin roles, the user permissions, all the stuff you're already doing on the server side, you can just forget about all that, print out what you want to show the user, give them the actions they need to do, and you can just let your browser be an efficient just rendering machine and render what you want. You know, let it be done. That's fine.