FastHTML first look – Answer.AI dev chat #4

[Jeremy]

Fastlight Updates

Hello again, another Dev Chat with Jono and Alexis, and a lot of stuff to share today. Actually, maybe a good place to start would be some updates from yesterday, which I haven’t shown anybody yet. I kind of felt like it was going slowly, but then when I was done, I was like, oh, I actually got quite a lot done. So that’s often the way, isn’t it? You just kind of keep slowly battling away. So in Fastlight, tell me if you guys, like, I don’t know if this is obvious and easy or not, but basically I added this .dataclass thing.

[00:01:02]

And so what that does is it creates a type. That type is a dataclass. You can make stuff. So the reason why is like, I, you know, for a few reasons. One is like, to kind of type completion, at least in Jupyter, it’s nice to be able to like see what I’m meant to be typing in, you know. And it’s nice, you know, for Python to know about what types are expected and stuff like that. Particularly, as we’ll see later, for it’s necessary for the web application framework stuff I was building, as we’ll see. So basically, yeah, given that this is something that you pass keyword arguments into, and the keyword arguments are the same as the, you know, obviously the field names.

[00:02:05]

And you’ll see all of them are defined as the database type or none. Things like SQL model, Sebastian’s thing, are much more careful about actually knowing if it’s nullable or not. And like, if it’s not nullable, it doesn’t say so, blah, blah, blah. I very intentionally don’t do that. Because this int, actually, you don’t have to pass it when you create it. It’s created by the database for you. And so in SQL model, you have to have different types for like the update version and the create version. And in this case, just like, you know what, I’m not going to be strict about this, you know. So anyway, that way, you know, I’ve got a dictionary here for ACDC albums. There are a lot more. I don’t know why they only have two in this database.

[00:03:00]

It’s a bit of a disappointment, but at least they’re good ones. So you can pass those in for a particular ACDC album as keyword arguments. And you get back an object. Okay, so that’s basically how you can convert the result of a call to your model, querying something in the model. Now you’ve got back an object.

[Alexis]

Data Class Equivalence

Can I interrupt with a basic question here?

[Jeremy]

Anytime.

[Alexis]

So when you define the variable album underscore dc, is that album underscore dc completely equivalent to what I would get if I declared a data class in the usual way by saying…

[Jeremy]

Indeed, I’ve even written a function called data class source that takes the data class and returns the source code to recreate the same data class. Okay, so it is. So that’s, there’s no magic here.

[00:04:03]

There’s actually, the data classes thing in the standard library has something called make data class in it.

[Alexis]

Okay. In that case, would you say it’s like more idiomatic if album dc was capitalized because it’s a class definition in constructor or… Yeah, maybe.

[Jeremy]

Yeah, maybe. I’m not really going to use it for much as you’ll see, so don’t worry too much about that. Okay, just wanted to double check my understanding. Yeah, yeah, that’s good. Yeah, so I couldn’t find something that spits out the source code for a data class. So I just wrote a little one. It doesn’t do everything, but it does enough to cover what this particular one does, at least. So this would actually recreate that. So the reason that’s potentially interesting is then that I added this create module thing, which literally just opens a file, prints from data classes, import data class, and then prints the source code to it.

[00:05:13]

And so that means that you could now, as you see here, create a module. And so create mod is actually going to do every single, every single table, and if you want to also every single view, it’s going to print out the data class source for all of them. So here, if I go create mod db, then this will create a file that contains all of them. Now in Jupyter, that’s entirely useless.

[00:06:05]

But if you use VS code, it’s very useful, because you can now do that, right, or import star, or whatever. And I’ve now got, you know, even in VS code, you’d now have autocomplete.

[Jono]

Fastlight vs SQL Model

So this is like the other way around to something like SQL model, where you define the data classes, and then it builds the database from that. This is like, oh, you have the database, we’ll build the data classes from that.

[Jeremy]

Yeah, exactly, exactly. And I don’t love code gen, but in the end, if you want VS code support, you have to do one of them. And the reason I prefer this one, for me, is that this way I can create my database schema using a GUI, or using SQLite utils, or using SQL statements, or using a migration system, or whatever.

[00:07:05]

It doesn’t matter. And at any point, I could just reflect that into my module with a single line of code. So I think that’s good.

[Alexis]

Composability

OK. The $5 word for that benefit would just be to say composability, right? Because by just being able to consume the SQL model as it comes in, you can use anything you wanted to create it.

[Jeremy]

My brain’s not working well enough to quite see why that’s composability, but I will take your word for it.

[Alexis]

Well, it is in the sense that what you’ve created now composes with any of a variety of tools that someone else could have made for defining the SQL schema to begin with.

[Jeremy]

OK, I guess I could do that. Now, we already had this thing where you could, I think, or is this new?

Data Class Casting

Did we already have this thing where I had dundercall? And dundercall is basically the same as doing a SELECT WHERE ORDER BY LIMIT OFFSET. But the thing that’s now been added is that when you call dot data class, it optionally, and by default, also stores the data class inside the table object.

[00:08:24]

And if that’s happened, then whenever you call dundercall, it automatically casts it. So now you can see when I call ALBUM LIMIT equals 2, the things I get back are not dictionaries anymore. They’re actually data classes.

[Jono]

So this is slightly opaque that, oh, if you happen to have made a data class from this at any time in the past, you now have different behavior when you call it. Yep, exactly.

[Jeremy]

And it’s not meant to be opaque. It’s actually like, another way to think of it would be like, oh, ignore the thing that’s returned from this.

[00:09:05]

And in fact, probably what you’d want to do most of the time is, I’m not sure I documented this, so let’s do this now.

Data Class Behavior

Oh, here we go. It’s at the end. If you want to have all tables, just call all DCs. And probably, you would actually just not bother with the return type. This is how you turn it on. You just run that. So you probably always just run that. And so that applies then not just to dundercall, also applies to .get, which gets my primary key. So those are the two things that let you get data classes. So then I’ve also added a lot of additional behavior to, particularly to update, insert, and upsert.

[00:10:02]

Upsert Behavior

So if you haven’t come across upsert, it’s not a SQL keyword. It’s a concept, which is that a lot of databases, including SQLite, and originally I think it came from Postgres, have some kind of thing that lets you insert things. And if the thing is already there, then update the existing thing where already there is referring to the primary key. So let’s create a new table called cats. So one of the nice things about SQLite utils is the fact that cats is not there doesn’t matter. Right? Okay, we’ve still got a table cats. It just doesn’t exist. Right?

[Jono]

Yeah. This took me a second or two to wrap my head around. Like, okay, well, I can check if it’s there or not by seeing if cat is in dt dot, or is in dt.

[Jeremy]

Well, you know, I’ve just added that feature. Yes.

[00:11:00]

So I can say cats in db. How is it dt? Yeah. So I added this.

Dunder Contain Support

I added a dunder contain support to this yesterday.

[Jono]

Oh, nice. Oh, well, I was surprised it worked and I was very happy. I didn’t realize it was there because you just added it.

[Jeremy]

Yes. And I think you can also do it on the table itself. Yep. Okay. So then I can create it. And then I can look at the schema. I added this thing. I added it to fastcore. Highlight. It’s really badly made. Oh, that’s right. Highlight as a markdown output. This is actually a markdown output. You can say what language. This is just a minor convenience. So you can see the schema that this has done. And so if I now rerun cats in dt, it’s now true. Or the stringify version is now true. Okay. So we’ve created a cats table. So when we, what did I say the same applies to?

[00:12:04]

[Jono]

That it’s automatically casting to the data class.

[Jeremy]

Yes.

[Jono]

Or that it takes keyword arguments.

[Jeremy]

Right. Okay.

[Austin]

Okay.

[Jeremy]

SQLite Utils Feature

Maybe we should just mention this curious feature about SQLite utils.

[00:13:23]

Okay. So then something else I added is now when you insert or upsert, you get back the inserted row. Now we haven’t called .dataclass yet on, I mean, we called it earlier, but that was before we, all VCs. We haven’t, but the cats didn’t exist yet at that point. So therefore we got back the dictionary.

[00:14:00]

So here’s an example of using upsert. But yeah. So now if we do the data class thing, let’s just show you, there’s no point storing that. It’s just enabling it. So now if I go cat equals cats.get1. So now I can set my attributes because this is a data class and I can pass that in. Because the first thing to be passed in is a, okay, that type annotation is now wrong. Let’s fix that, shall we? BIM fastlight slash keyword arguments def, there it is, def upsert. Okay. So that’s a dictionary of string to any or a data class, which isn’t of any particular type.

[00:15:03]

So I think you just have to call it any. Cool. So you can see like, there wasn’t much code for me to write to add this behavior. So this is actually something quite nice I added to fastcore yesterday. When you patch something, so in this case, I’m patching table. If there’s something already exists, it stores the original version of that function now as underscore, underscore with that function name. This is a really nice way for me to now patch new behavior. So it’s just calling the original version, but it’s, you know, doing the data class thing. And at the end. No, it’s fine to patch twice. It checks if there’s already an orig upsert there.

[Alexis]

So is the default behavior when you do the second patch to clobber, but you have access to the old patch or is the default behavior to advise meaning that it executes both of them?

[00:16:11]

[Jeremy]

So if you repatch, then orig upsert will still be the orig orig upsert. So it will, it clobbers the old patch.

[Alexis]

Okay. So clobber is the old one, but the old one’s there if you want to access it from the new one.

[Jeremy]

The, the grandparents there, but the old patch is not there. If you repatch the patched method, then the original patch.

[Alexis]

Oh, okay.

[Jeremy]

I see. Okay. Got it. Okay. So cool. We can drop the table. So that yeah, that was basically stuff that I added as I started to implement the next thing I’ll show you.

To-Do List Application

All right.

[00:17:02]

So this is where things get fun is we have a to-do list application. This to-do list application is like, it’s got nice kind of resizing behavior. It all feels very modern. When I click on things, there’s no full screen refresh. I can add new to-do’s to people fast HTML. People support ad just appears. Click on that. Change it. Enter. And it’s kind of like, it’s like, it feels like a very, like you can click on anything at any time and I haven’t found a way to break it. Do you know what I mean? And it’s, as you can see, it’s fast. And obviously I haven’t spent much time styling this. I haven’t spent any time styling this, but it’s like looks and feels like a, you know, reasonably modern kind of web application.

[00:18:09]

The entire thing is written in 69 lines of code, many of which are blank. And it’s all Python and there’s no templates. There’s no JavaScript and there’s no CSS. It’s, it’s literally a single file of Python. So this is, thanks to a thing I want to show you guys, which I know both of you have seen bits of, called fast HTML.

FastHTML Introduction

And the goal of fast HTML and this kind of ecosystem is to allow people to create single file web applications without the shortcomings of like Gradio and Streamlet and stuff, which are really great systems.

[00:19:05]

But by the shortcomings of those, it’s like that they’re for like creating dashboards and proof of concepts and whatever. If you like, you wouldn’t create your whole startup as a Gradio app, probably, or a Streamlet app. You know, and like when people get to the bit where it’s like, okay, I now want to have a different component that works in this different way and takes advantage of this JavaScript library. It’s like, oh, okay, that’s possible. Now you have to learn this entire new massive complicated framework that’s harder than it would have been to just use it in the first place. So the idea of this is to let you like start building something as easily as Streamlet or Gradio would, but naturally support growth from there. There’s no point where it’s like, okay, you’re done. Now you’re going to have to use TypeScript or now you’re done. You’re going to have to learn to create your own IPy widget plumbing or, you know, like Solara, like FastUI.

[00:20:03]

And it’s actually loosely based on the framework I created, Jesus, 25 years ago for Fastmail, which supported one of the busiest sites on the internet. You know, it’s this approach scales out in any way you like. So that’s the goal. It’s basically reuse your Python knowledge. You can use any CSS framework. You can use any JavaScript library. You can use web components. You can use, you know, all that stuff. And if you create something with, you know, your Python that you think other people might like, you can stick it up on PyPy and other people can pip install your style sheet framework or your web component library or whatever. So, and ditto for other peoples. So the particular one that at the moment, I think it’ll probably ship with is the CSS. It’s called Pico. Super simple, lightweight thing that I’m pretty sure quite soon we’ll have like a Daisy UI, you know, pip install, fast HTML, Daisy UI, whatever.

[00:21:11]

Or like if there are certain JavaScript things you like, you know, you can easily wrap them with this and pip install those. There’s no build step. There’s no code gen, you know. So in fact, let’s take a look. So if I change this here to to-do list demo and I’ll pop up the terminal, you can see this running in the background here, right? And as soon as I hit save, it’s refreshed itself. And back over here, there it is, right? It’s, yeah, because there’s no build step or anything to do. It just keeps going. So, all right.

To-Do List App Implementation

So let me show you how the app is built.

[00:22:03]

[Alexis]

Gradio and Streamlet Shortcomings

And- Can I interrupt again?

[Jeremy]

One or two quick questions? Anytime. You don’t need permission to interrupt.

[Alexis]

All right. Then I’ll interrupt a little more liberally. So I’m aware of what Gradio does. I think I’ve just like used it once or twice, but I haven’t never explored it in detail enough to have an understanding of, you know, why it might not be the kind of thing that scales up from a quick start to like a full application. Sure. So I was wondering, hard to summarize, but if you could give a sense of what is it about the way it works or Streamlet works that you feel like makes it, doesn’t give you that sort of continuous path from quick start to a bigger, more complicated thing.

[Jeremy]

Yeah, absolutely. That’s not how it works.

[00:23:31]

Sorry.

[Jono]

So while you’re finding that, Alexis, Gradio is very oriented around getting some inputs to a function and then displaying back the outputs. Like it’s ideal for I have a model that makes predictions or I have something that generates something. You can do some state tracking and things like that, but it gets, it’s very, very nice for like this kind of interface here is so easy in Gradio.

[00:24:01]

But trying to move to something where, I don’t know, you’re storing stuff in the user’s cookies or you’re doing state or you’re keeping track of multiple things or you want multiple pages in your app. It does get very cumbersome very quickly. Yeah.

[Jeremy]

So here’s an example of something that has an upload button or you can click an example and you can click submit and it runs a machine learning model on it. It’s indeed a doggie. This is a classic kind of Gradio interface. And if you look at how it’s implemented, you create an image, you create a label, and then you create an interface that we’ll call this function. These are your inputs, this is the output, and these are examples. It’s a very specific kind of application it can create.

[00:25:04]

And for that particular kind of application, it creates very quickly and easily. It’s not a general, like you could say, create Instagram in this, you know. If you wanted to create Instagram, well, Meta at the time before it was Meta, I guess it was originally created by Instagram, they used Django, right? So Django, as you all know, is a general purpose web framework from which you can build anything that your brain can think of. So FastHTML is like Django, it lets you build anything. Streamlet is similar to Gradio. It’s the same kind of idea of like, sketch out the basic inputs and outputs and maybe one function to call everything, that’s it.

[Alexis]

So one thing I think about when I think about this design space is that it sounds like Streamlet and Gradio give you abstractions that are very convenient to work with, but they don’t fundamentally map on to the underlying abstractions of how a website is built.

[00:26:15]

Like, you know. Precisely. They have an HTML and everything.

[Jeremy]

FastHTML vs Gradio/Streamlet

So let’s take a look at this example. So let’s take a look at this example of how, compare it to this, let’s instead see how was how was this screen created. So this screen has got a header, it’s got a thing here with a button and an input, it’s got a list of to-dos, and optionally got some details about the to-do underneath. So this one here, and it’s on the root of our website, so here is the implementation of that.

[00:27:06]

And it contains, so and then it would like, it’s, if you look at the HTML, it contains a main. The main contains an h1, the h1 contains an article, the article contains a header, an unordered list, and a footer. So the thing that you write here is actually the same as the HTML. So okay, give me a main with an h1, the body is an unordered list containing some to-dos, and above that will be a header containing a form with an input and a button. And we’ll give the whole thing a title, which you can see up here.

[00:28:04]

So yeah, in this case the thing that we’re actually working with is is HTML, and also we’re working with things like posts and IDs. So yeah, we are working with, you know, the same elements that anybody building something with React or Django or whatever is, and that’s why with this you can build anything, and it’s, you can, the abstractions that you’re building with are not like, okay, totally different. They’re tiny wrappers over the basic foundations of the internet, right?

[Alexis]

Okay, and those are those things that look like function applications are creating data structures that are then, that you then convert into HTML later.

[00:29:00]

Yes, which we will see.

Hiccup Library

Okay, yes. Yeah, so this reminds me of a library called Hiccup that works in a similar way, not a Python library.

[Jeremy]

So I have a second question. What language is that in?

[Alexis]

It’s in Clojure, C-L-O-J-U-R-E, and it uses the native data structures of vector and dictionaries to represent a tag that, to represent an element and its attributes.

[Austin]

Yeah.

[Alexis]

So when you want to build a big HTML page, you kind of build it just by essentially doing things that look like nested function calls that are actually just returning the basic data structures of vector and dictionary and a list that mirrors the same thing.

[Jono]

Yep. And this is very different to, like, some libraries will have a, you know, a form object, right, that’s a Python object or a Pydantic model or something like that, and then it also has, like, on the front end, a view, you know, component and some JavaScript and a WebSocket stream to synchronize data between the Python object and the actual front end.

[00:30:12]

And then the view or Svelte or whatever your JavaScript framework is, does the magic and eventually produces the HTML form. You know, so there’s a lot of machinery in the middle there, which is sometimes very nice. But yeah, in this case, it’s like, oh, you just write the thing that produces that HTML.

[Alexis]

Well, one of the kind of aesthetics that are, you know, design approaches that can work well when it’s possible is to try to embrace basic data structures as much as possible, rather than introduce, like, new types, because then the basic data structures are, again, $5 word, composable with generic manipulators that you already have for basic data structures, like in Python, that would be like comprehensions, right? If you’re representing your list in your HTML that will be eventually turned into HTML is just a Python list, then you can go and modify every element in your list just with a comprehension.

[00:31:01]

[Jeremy]

So in fact, every one of those HTML functions is returning a three-element Python list. Okay. The three-element Python list contains, in order, the name of the tag as a string, a list of the children, and a dictionary of the attributes. And yeah, it’s interesting you mentioned the closure one, I haven’t seen that before, but basically almost every functional language has a version of this. So Haskell has one, OCaml has one, and there are similar things in Python as well, although they’re not quite as functional as FastHTML or those OCaml and Haskell ones are, it looks like the closure one. And it’s how I wrote FastML, that was written in Perl, but to me, apart from anything else, I don’t like switching between files.

[00:32:02]

So templates were created back in the day because web design was very difficult, because you had to write lots of HTML in order to deal with the quirks of each browser. And a lot of the visual representation was contained in the HTML, that before CMS, CSS, all of it was. So because we had web designers, like that was a, you know, a very specific kind of job, which involved to a large degree knowing about quirks of browsers and stuff, web designers needed to be able to write HTML and look at the HTML, and they were not generally coders, so we came up with this idea as a community being like, oh, let’s give them templates. And so, you know, the place where the name will go be like curly bracket name, and the place for the email will be curly bracket email. They don’t have to worry about that, they can design the whole thing, give it back to the coder, and then that’s now a template they run.

[00:33:02]

This doesn’t seem useful anymore, because, you know, we just do semantic HTML anyway. So to me, the idea of having a separate file that contains two separate languages, the first separate language is HTML, the second separate language is like Jinja, or whatever, I’m not very fond of. So I want to be able to do it all in one file. And yeah, this functional approach, it was actually Austin who, and also Bradcourt Daniel, who’s one of the authors of one of my favorite books, which is called Two Streets of Django. They both told me about some of these functional libraries. Okay, so yeah, so I’ll show you guys more about this in a moment.

[00:34:01]

FastHTML Components

But I got one more question.

[Alexis]

Sorry, you said I had a license to ask questions. So here’s question number two. You talked about how this design, one of the goals you had in mind was for it to offer a kind of smooth path onto a more full featured website that, you would do for a real app. And part of that is cleaving to the underlying abstractions of the browser, so you can work with them rather than hit the impedance mismatch problem. But another part of it is using components that other people have made. Yes, like this one, for example. I was going to ask about calendar pickers and React components and stuff like that.

[Jeremy]

This one, which I made. So we will see that in a moment. And they are written in Python and distributed as pip installable things. And so, yeah, we’ll see that. So let’s look more at how to implement this particular form.

[00:35:02]

HTML Construction in Python

Okay, so I’m going to assume you know HTML. So you know this thing called a form. You know there’s a thing called a button. And as you see, we’ve got like, you know, it’s all autocompletes all here. So if I start typing, it’s all here. ID, inert, input mode. So I’ve got all of the attributes there as autocomputable things, but they’re all standard stuff. You’ll see I don’t return an HTML object with a body. But instead I return a tuple with two things. One is what’s going to be inside the body, which, as we saw, is a main.

[00:36:00]

So the body contains a main. The other things that are there have been added by my extensions. So you could ignore those. So this is the thing that goes into the body. And then this is the thing that goes into the head. So that’s, you can return an HTML if you want to. Like I could have gone return HTML head body. But you don’t have to. And we’ll see why this has got some benefits later. So for now, I’ll just say like, this is both more convenient, and as it turns out, it’s got some benefits. All right. So the child of a, so when you put in a tag name, you’ll see that the first thing is star C.

[00:37:00]

This is where you put children. So you can write as many children as you like. So title, hello, there, whatever you like. Put a link there. All right. So that’s fine. And then, yeah, any attributes, just attributes to the tag. Class is special. You can’t write class equals, because class is a keyword in Python. So you have to write C or S equals. So you’ll see that we have a, the main has a class.

[00:38:00]

Okay. So like, that’s the basic idea of constructing HTML in Python.

FastHTML Decorator

You have to have it respond to this. And so we, FastHTML uses a pretty standard approach of having a decorator. So you start out by creating a FastHTML app. And then you, the decorator, this can be any HTTP verb. So get, we’ll do a get. And this is what it’s going to listen on. So this is, if you’ve used Flask, this is pretty standard. Or FastAPI. FastHTML is loosely based on Fast, or quite strongly based on FastAPI. I went through the FastAPI tutorial and tried to make each section of the tutorial work with the same syntax. So when you create your FastHTML app, you could, you know, since we optionally make it, you don’t have to manually create your HTML header.

[00:39:04]

You have to have some way to say what headers are going to be there. So this is how you do that. And in this case, there’s going to be, so FastHTML comes with a link ready to go for Pico CSS. This is, again, something you can give people. It’s just these things predefined. And I added some additional stuff into a style sheet. So if you look in the FastHTML repo, you’ll find an examples. And yeah, I just changed some of the sizing a little bit. I found their defaults a bit too big for my liking. But you don’t have to use any CSS if you don’t want to. OK, so how on earth does it go ul star todos?

[00:40:07]

Todos Table

What’s going on there? Well, todos, you hopefully won’t be too surprised to learn, comes from Fastlight. So I create a database using SQLite utils. And then I ask for the todos table. And you’ll be pleased to see, Alexis, I used capital T for my todos data class. So as we’ve seen, if you type todos parentheses, it calls a select statement. And by default, there’ll be no limit and nowhere. And therefore, this is all of your todos. They get passed as children to a ul. So how on earth does an unordered list render a data class? And the answer is that if your class contains a special dunder xt, these things are structures.

[00:41:01]

I call them xt structures, standing for XML tags. If you have this special thing, this is what’s used to render it in HTML or actually as an xt. So I patch this into my todo. There’s lots of ways you could have done this, right? If you don’t like this, you could instead simply have a function called xt. You could get rid of patch. And you could have just gone map, like that, right? Like, you could certainly do it that way as well. Either one’s fine.

[Jono]

Todos Table Existence

A very minor thing, Jamie, but the todos table, if it doesn’t exist, might, I think, cause issues. So I’ve done some…

[Jeremy]

I… So there’s a few ways you can handle this, right? So in my case, I just have a little notebook where I’ve been fiddling around with things, and I created it there.

[00:42:23]

And I added a few todos to it. So in fact, you can see if I run these cells, I’ve got recreate equals true. So that’s actually deleted and recreated it. So if I now go back to here… Wait, is that using a different database? todos.tb. That’s strange.

[00:43:16]

Database Refreshing

Now, why did refreshing… Something was caching that. Interesting. Anyway, that’s what I need to think about. Yeah, so if I change the, you know, rerun the database, I get back the, you know, different set of rows. So yeah, it’s like Simon Willison’s, in his examples, tends to kind of have stuff in his insert that has additional things to describe how to create the table if it’s not already there. Kind of neat, but I prefer to use something called migrations, which we probably won’t talk about today, to do more stuff in SQL or outside of the main application to kind of get the initial structure in place.

[00:44:08]

But yeah, you know, or, you know, some people have like a, if, you know, if dunder name equals main at the bottom, and that way you can like run Python db app.py and it would create things. Yeah, you could have like something at the top that’s like, oh, if it doesn’t exist, then create it. So yeah, there’s lots of ways you can do that. But it’s a good point.

To-Do Item Links

Now, the each todo has two links. And so here are those two links. Now, rather than using an a tag, I’m using an ax tag. Sometimes I add an extended version of a tag. And when I extend a tag, I add an x to the end of it. And it’s not very extended. It’s just like, partly it’s like some of the defaults are a bit different.

[00:45:03]

So it starts out with like the text you want is first. And so you can, I don’t need any keywords here or whatever, but it’s basically.

[Alexis]

Ax Tag

So a doesn’t mean anchor. A is a general purpose constructor for a tag like structure.

[Jeremy]

No, a means anchor. This is an anchor tag. Yep. These are all, you see their links, their a links. Okay. Yep.

[Alexis]

So ax is a anchor tag.

[Jeremy]

An extended version of an anchor tag or extended version of a hyperlink. Yeah.

[Alexis]

Okay. So it’s not like block or some kind of super class that represents all block like tags. Got it.

[Jeremy]

Otherwise it wouldn’t know what tag to use. I didn’t say. Yeah. So each to do item is a list, you know, that contains that first link and maybe it’s done. And then that second link. And also it’s got an ID. So it knows which to do it is. So it’s got this little tiny function here, which just returns to do dash ID.

[00:46:03]

So if I look at one of these list items, you can see here li to do one. I don’t know where all this data default font size is coming from. Curious. Probably some Pico thing or I don’t know. Okay.

Delete Functionality

Yeah. So when I click delete, that calls, that sends a, so that sends a delete HTTP method to that endpoint. And I can just call todos.delete because that’s how SQLite utils slash last light works. So the next thing to talk about is how come we have what appears to be a SPA, you know, there’s no full screen refreshes or anything.

[00:47:13]

HTMX Introduction

But my, you know, it looks like I’ve written a kind of web 1.0 style app here. And the trick to make this work is something called HTMX. And HTMX basically adds what I think of as the obvious missing features to your browser and HTML. And so it adds four features, basically, and once you’ve got HTMX, which is a JavaScript library that’s auto installed if you use fast HTML, your browser behaves as if the following things are true. Normally a browser or kind of without JavaScript and stuff, a web 1.0 browser could do two things.

[00:48:04]

It could provide a hyperlink, which when clicked on sent a get request. Or it could have a form, which when submitted sends normally a post request. There are lots of other HTTP methods. So HTMX lets you, adds all of those other methods as things that a browser link button, et cetera, can do. The second issue with web 1.0 HTML was only two things could cause any action to happen. Clicking a hyperlink could cause a get request to be called and the entire page replaced with the result. Or it could cause a submit to be done with the form data. And again, the page to be replaced with the result. HTMX makes it so that any object can cause behavior. The third is that with web 1.0, the result from the server can only do one thing, which is replace the whole page.

[00:49:12]

HTMX makes it so that you can either replace the page or replace a single DOM element or delete a DOM element or insert after a DOM element or insert before a DOM element. So it lets you, yeah, change where the result from the server goes. Damn it, what’s the fourth one? So we’ve got any HTTP method. You’ve got what you change. You’ve got what can trigger it. I’m sure the fourth one will come up in a moment. So the way this works is that you add additional attributes that’s, let’s right click on this and choose inspect, that have an hx at the start.

[00:50:08]

So hx-get means this will cause a get method to be sent to this. Which obviously is the same as what an a tag does anyway. You send a get method, but this is an HTMX get method. Okay, so I mentioned that the result doesn’t necessarily replace the entire page. How do you decide what it does do? Whatever target is, is what’s replaced. So there’s a thing called current to do, which is actually a empty div down here. Right, so when we click on this, it’ll call to do slash one and it’ll insert the result into current to do. So we could see what’s going to happen by manually going to slash to dos slash one.

[00:51:02]

And you can see there it is, right? There’s that little snippet. So the HTML and head and body has got added by my browser. But actually, I guess if we look at the notebook, the network tab, you can see actually this is the response. Okay, so you get this little HTML partial and it will be inserted into this div. So let’s try it. If I click, and you can now see, yep, there it is. It’s been inserted into this div.

HTMX Target

That make sense? So that’s what HTMX is. And Alexis is talking earlier about, like, kind of looking into the flexible foundations of the web, rather than creating some single purpose abstraction.

[00:52:05]

So HTMX basically endeavors to make that kind of web foundation dramatically, dramatically more flexible and powerful. By just taking what’s already there and kind of doing it almost like in hindsight, the obvious thing, which is like just to remove the constraints.

[Jono]

HTMX and FastHTML

And maybe just to clarify something, like you don’t have to use HTMX with fast HTML. These are two orthogonal things. Like I built basically the same app, but anytime you want to do something, you can just get the full page and the full page is rebuilt now with an extra to-do item in the list.

[Jeremy]

Yeah, I mean, maybe either now or in the future, you could show us that if you’ve got the web 1.0 version of this. But yeah, absolutely. So the things that fast HTML does to make HTMX more convenient is, for example, by default, it includes the JavaScript for HTMX in the header.

[00:53:06]

And in the autocomplete, all of the HX things are there for you. It’s just minor little things. But yes, you certainly don’t have to use HTMX.

[Alexis]

HTMX Interactability

So here’s a question about HTMX. You say that it allows you to make any element interactable. That makes sense. Thinking about native development, this is like in Cocoa Touch having a gesture recognizer. It’s an object that you can attach to any UI element and then will allow gesture recognition to be associated with it, taps or swipes, other things. And then you say that it lets you choose which kind of HTTP method is going to be executed. OK, so that’s good because beyond get and post, there’s things like patch and whatnot that enable partial updates that aren’t as well-known but have relevant semantics.

[00:54:06]

[Jeremy]

Or here we’ve got, sorry, you can see them here. We’ve got delete to delete a to-do, post to create a to-do, and put to update a to-do.

[Alexis]

Right, but so my question is, while those HTTP methods have a lot of relevant semantics, there’s all sorts of actions one wants that aren’t expressed by an HTTP method that aren’t just kind of, yeah.

[Jeremy]

HTMX Actions

So in that case, you then you would use paths. So for example, show an edit form. There’s a different path for that. So you can, yeah, have a. So like, and in fact, you could use only post or you could use only get. Like there’s no particular reason you have to use any other HTTP verb. You can only use paths for everything. OK. And some people do. So you could have slash to-do, slash delete, slash ID.

[Alexis]

OK, so I can use HTMX to make an arbitrary element interactable.

[00:55:01]

But then I have a choice about what the action means. It could mean in a simple way, hey, update this component that’s in the DOM. Or it could mean actually, hey, send a thing to my server, you know, a get request, and that thing is now going to trigger.

[Jeremy]

HTMX HTTP Requests

Well, I mean, it always means send an HTTP request to the server. You choose what request method it is. And you choose what path it goes to. And you choose what data is in it. And you choose what headers are there. It’s the normal things you have to choose when you create any HTTP request. And then on the server, this is what you write. You know, you then decide what happens. What happens when this path gets this HTTP method? Oh, it runs this function.

[Alexis]

OK, so it’s single page and very dynamic because we don’t see a whole page reload. But it’s still doing a round trip to the server, even for incremental local modifications in the page.

[Jeremy]

HTMX Round Trip

Exactly.

[00:56:01]

[Alexis]

OK.

[Jeremy]

For example, one of the things you are likely to want at some point is something where, like, maybe it’s a create new user. And as you start typing the username, a little thing pops up saying username is available. So that’s an example. So you can absolutely attach, you know, hx-keyup or whatever. Sorry, like you can attach, I think it’d be hx-trigger equals keyup. So now we’re attaching a, to the, you know, as you type, it’s sending stuff to the server. And then the server sends back something saying, you know, put user is available into the user available empty div or whatever. OK. And so it’s, it’s, it’s surprisingly OK. Like that, you know, at least from, even from Australia, we’re at 70 milliseconds each way. To the US, and most servers are in the US.

[00:57:03]

It doesn’t feel at all slow. Like you wouldn’t, you wouldn’t write a computer game in this because every tiny click of key it has to do a full round trip, you know. But anything that can happen in about 150 milliseconds is going to be fine. And for stuff you don’t want, you can still write JavaScript, of course. If you did want stuff that happens in less than 150 milliseconds.

[Alexis]

HTMX Attributes

So I noticed that the htmx magic is managed through these hx attributes. And part of interoperating with the web is not just being able to use HTML and JS, but using the enormous suite of components that other people have, you know, made. So I know that in terms of like heavyweight reusable components, like if I want a complicated, like, let’s say I want to have a location picker, which is going to present a kind of browsable, zoomable little view into a map and allow me to like put a pin and then show me the address and then confirm it.

[00:58:09]

HTMX and Reusable Components

Yeah, that’s a very plausible thing.

[Jeremy]

Htmx is great for that.

[Alexis]

Yeah. OK, because I don’t want to make that myself, but I also don’t want to have to like become a, you know, full time ReactJS person and already be able to use it in this thing.

[Jeremy]

So somebody else, you know, somebody else, hopefully somebody who’s not me or who likes, who likes Python more than I do, sorry, who likes JavaScript more than I do, hopefully will create the pip installable thing. So, for example, there’s a sortable JavaScript library. OK. And here’s an htmx demo of using the sortable JavaScript library. You can see these things being dragged around. And I’ve got to get it between the two. And you can see underneath these, these are the requests that were coming in.

[00:59:03]

Right. So this is actually using htmx that now, because sortables has been attached to htmx, these are triggering htmx HTTP events. Right. And you can see the response coming back is, again, a little HTML partial. So the first person to make htmx and sortables work together is going to have rights in JavaScript. And so here’s the JavaScript they wrote. But once somebody’s written that once, they can stick that on PyPy and now everybody can use it. By just writing, you know, sortable equals true or something in their form, whatever. Yeah. And it’s not a lot of, it’s not like, it’s not, A, it’s not a lot of JavaScript and B, the JavaScript’s not complicated because htmx is using very standard JavaScript stuff.

[Alexis]

HTMX and React

So I don’t know much about React.

[01:00:01]

I know that it tends to be integrated. It provides a reusable component model, which is why it’s been successful. But I also know that component model is integrated with a certain data flow model. Do we know if anyone has used htmx to…

[Jeremy]

This is not for React. Okay. Right. So this is for stuff more like…

[Alexis]

HTMX and Web Components

What about like web components? Sure, like components, for example.

[Jeremy]

So, and this is the way the web’s going, right? Is these kinds of, these components can go in React or Vue or Angular. Hopefully at some point, somebody’s going to add the first HTML version in here, you know, and…

[Alexis]

But React, I’m not familiar with that domain, but it seems like it’s like the first generation of an idea that has been refined with some of these later kind of component models anyway. So maybe some of those component models are easier to htmx-ify, and then the kind of promised land that you might get to is that there are pip installable components once you’re there.

[01:01:07]

[Jeremy]

FastHTML Components

Yeah, that’s not some distant promised land. I mean, hopefully within a few weeks, there’ll be quite a few, you know. And as I say, it’s going to, it comes with some already super basic stuff like group, you know, and card. So yeah, great. So let’s have a look behind the scenes a little bit, and then we can maybe come back to here to look at some of the details if that’s of interest.

Fastcore.xml

So the kind of basic thing, I guess, this is all built on, is fastcore.xml. And originally it was just a list, xt was just a list.

[01:02:04]

Now it derives from list, and has three very handy properties, tag, children, and attributes, which are just in case you forget the order of what, zero, one, and a half. That’s all they are. And it also has an init that you can only pass in exactly three things, but it’s just a list. And so that’s what an xt object is, it’s a list. And you can just use a list, there’s no need to use an xt object, this is just meant for your convenience. So if you’re going to use the xt function, you pass in a tag, you pass in the children, and you pass in the keywords. And so xt, got my fancy tag, and then that contains a child, and another child, and an attribute.

[01:03:09]

[Alexis]

Xt Function

So why are you doing xt as a function rather than patching it in as a method onto the list type? Well, I guess because list isn’t a type of such, okay.

[Jeremy]

You know, I mean, now that I’ve added this, so I originally, I wrote this. Now that I’ve got this, I could have replaced it as having done the init, could have done these tiny little things. So it’s just history. Anyway, it’s not much code, so it’s fine.

[Alexis]

I’m just thinking about the analogy with the anchor type, you had anchor, and then you used patch to retroactively conform to this de facto protocol of underscore underscore xt.

[Jeremy]

Yeah, no, it’s a bit different. But you’re not doing that here with list. And I don’t want to change the constructor of all lists. Okay, all right. I very much doubt you even could, because it’s a built-in.

[01:04:02]

HTML Tag Functions

Okay, so then I just grabbed all of the main HTML tags, and added them over the module. So now, as a result, if you call html, head, title, body, div, p, input, image, instead of using cl, so instead of using class, you can use html class, or class, or class. These are all kind of standard things. Oh, actually, I’ve also seen some people use underscore class, let’s use that as well. Underscore class, which means we should also allow underscore for. Okay, so there’s various different ways you can spell it. So yeah, so as you can see, it’s just returned one, two, three element list. Or these are three element lists.

[01:05:00]

So that’s it. And it doesn’t have to be, like, you can put text there as well. Maybe that’d be better to demonstrate. So okay. Mm-hmm.

Xt Object Properties

So as I mentioned, they’ve got property names. What else is there to show you? Okay, so then the thing that converts that structure into xml slash html is this.

Xt to XML/HTML Conversion

So you can see here, here it is. Okay, so that’s the basic foundation. So there’s hardly any code, as usual. I don’t like writing code. I don’t like having to make people read lots of code. So then on top of that, there’s fasthtml.core. Now fasthtml.core is the thing which does this stuff.

[01:06:02]

Fasthtml.core

And this is basically a pretty thin wrapper around Starlet.

Starlet Framework

Starlet is a ASGI, I think you say ASGI framework. This ASGI thing is a standard way to create Python web applications. And basically almost nobody understands it or knows what it is. Because when I started asking around to try to learn about it, everybody was like, I don’t know what it is. I just use other people’s things, right. So now that I do know what it is, at some point I’ll do a little course in that. So now I’m just going to say, don’t worry about it too much. It’s a thing which, like, Starlet provides that thing for you.

[01:07:03]

And Starlet lets you, you know, create routes that send you off to functions and have responses and have things in requests or whatever. It’s, I think, you know, it’s not necessarily designed to be something that most people write things indirectly. I suspect it’s mainly designed for people to write stuff like fasthtml in. Or indeed, fastAPI, which is a super popular library, is written on top of Starlet as well. So fastAPI is designed to create APIs. So all of the, so you go to the tutorial.

FastAPI Tutorial

Wait, how am I supposed to use the tutorial?

[01:08:00]

This is awkward. Oh, I see, I had to, that’s interesting. When you zoom in, it makes everything disappear. Yeah, so basically in the tutorial, it’s a really good tutorial. And a lot of the things you learn here will also apply to fastHTML. It always returns JSON, but returns dictionaries which become JSON. So at the end of this tutorial, you don’t actually end up with an application at all. You end up with an API that you could write an application on top of, and that’s going to require JavaScript. So fastHTML is designed to be something where the thing that gets returned here are HTMX partials or HTMX pages. So that’s basically what this is. So here’s a list of all of the HTMX headers, for example. So we haven’t even talked about this, but when HTMX calls your, you know, your HTTP verb endpoint, it always includes, for example, an hx-request header.

[01:09:10]

So you can always tell if something is sent by HTMX or not.

HTMX Headers

What I might do is jump to the tests, just to give you a sense of what fastHTML.core does.

FastHTML Tests

So fastHTML is something that I can create an app. And then this is quite neat. Starlet has a thing called test client, which I can now call cli.get on, for example. So that may be the first one we should do this manually. cli.get, it handles any HTTP verb, can go here.

[01:10:01]

And I can say, okay, what URL? And you can also pass in headers, cookies, etc. So we’re going to go to slash hi, because we’ve just defined a listener there. And you can see that’s returned a response, which has various things in it, including text.

[Jono]

Starlet Requests and Responses

It’s somewhat helpful here to know a little bit about the Starlet requests and responses and things like that. But maybe that’s a good separate cutting point for a different video or course or something like that.

[Jeremy]

Because here, I just returned text. So I’m trying to make it so you don’t have to know about any of those things. But you can use them if you want to. But in fact, in our tests, none of them use a request or a response, except for the very first one, just to prove to you that you can.

[01:11:02]

[Jono]

So if I didn’t know the fast HTML way to do things, but I did know, well, if you give me a Starlet request, I know what I’m doing with that. Then that’s great for the people who have that. But if you don’t have that, then you don’t need to worry about it.

[Jeremy]

Starlet Usage

If you’re fine with that, just use Starlet, in a sense, maybe. Yeah, so basically…

Starlet Request Object

So I mean, one thing you do need to know from Starlet and from Flask and from just about everything is that the thing we say, oh, this is the endpoint I want you to listen on, you can whack a thing in curly brackets. And that means anything that gets put in there will work. Yeah, maybe the first few we should do manually. So yeah, as you say, Starlet has this concept of requests.

[01:12:02]

And if you add a parameter to your function, to your route function, that’s called request or rec or r or any sub scene of request, it will automatically be passed this special request object. Oh, you know what? It would be better to look at index, because that actually shows you, I think. Yeah, here we go. So this one here… Oh, there’s a lot of stuff in request. I mean, we could just print it out. So let’s… Actually, no, it probably won’t work. Never mind. So yeah, the request object is something that you could go to Starlets and go to requests.

Request Object Properties

And you can see here, request. And there’s various things in a request object. The idea is, though, with fastHTML is that you never have to worry about any of that.

[01:13:03]

But it is there if you want it. So one of the things in there is a dictionary of headers. So the test client uses this. It’s there.

[Alexis]

Authentication and Headers

So authentication is often the domain where one starts to care about HTTP headers, because cookies and things like that are managed through headers.

[Jeremy]

Well, you’re not going to have to worry about that either. It’s all going to be automatic. But let me get there so you can see.

Cookies

We won’t do authentication today, but we will do cookies. Okay. So if there’s a curly bracket thing here, that’s going to get passed to your function, if your function has a parameter with the same name.

Path Parameters

And give it a type, right? So it can…

[01:14:01]

There you go. Okay. So this is a bit of magic, although it’s pretty standard magic for these kinds of libraries. Is to work this way. And it will endeavor to cast things appropriately. So in this case, obviously there’s no such thing as ints or whatever in paths. But we’ve asked it to become an int, and it will turn it into one, like so.

[01:15:07]

Data Class Support

Okay. Which actually, that’s exactly what this one already does. So it’s pretty interesting. So if you return an xt, it will be automatically 2xml’d. So when we call slash html slash 1, we get back all this html. Because that’s what this gets. You can… This is quite a nice neat little thing. You can register regular expression parameter types. So here I’ve registered something called image extensions as a regex. And then if you stick it in here, it will match anything that matches this regular expression.

[01:16:05]

The type can be other more interesting things.

Parameter Types

In this case, model name is an enum. So slash models slash alex net returns alex net.

[Jono]

Oh, you overwrote slash name.

[Jeremy]

I broke it. That’s what I did. I’ll come back to that. So if I look for slash models slash gpt5.

[01:17:01]

Sorry. Yes, yes. slash models slash gpt5. Then I’ll get back a value error. gpt5 is not a valid model name. At least not at the point we’re recording this video.

Query Parameters

OK. So you can also put stuff as query parameters. All right. So that, again, it just comes through exactly the same way. You can also add stuff as headers.

Headers

So dash, they’ll be lowercased. And dash is turned into underscores. If there are htmx special headers, and you have a htmx headers type, they will be passed to there. Or you can just use the special name, htmx.

[01:18:04]

[Alexis]

Endpoint Values

So in those examples is what we would see if we hit those endpoints, that those values are echoed back. Like it would read the user agent from the request, and then return that back in the response?

[Jeremy]

Yeah, so it’s returning a string. Right. Right. So the string is the number one. Yep. So it just returns the number one. You can see here, this is the expected value. So it’s returning the number one as a string.

[Austin]

OK.

[Jeremy]

Just like up here, we returned the string. Hi there. This time we’re returning the string.

[Alexis]

Return Mechanism

Yeah, I understand the return mechanism. But what I’m looking at is the parameters in the, let me see, the def of UA.

User Agent Parameter

Right. OK, so UA user agent colon. User agent’s a special keyword that means I’m going to take my input parameter from the HTTP header.

[01:19:00]

Is that right?

[Jeremy]

No. No, no.

[Alexis]

Oh, OK.

[Jeremy]

Nothing special at all. There’s only two special ones, which is htmx and request. Everything else is not special. Everything else looks in order to see if it’s a path parameter. If not, is it a query string? If not, is it a header? So it just keeps looking to try and, it does everything it can to find something. And so it found a header for user agent. OK. Yep. Got it. And if not, is it a cookie?

Cookies

So here we set a cookie. So this is where we do for now at least require, have to create a starlet response object. And we set the cookie. And so here, that cookie was called now. So here we put now.

[01:20:00]

It will grab the cookie because it’s not a header and it’s not a query string and it’s not a path object. So it gets the cookie.

POST Requests

Also, you can pass things into a POST request. So in this case, the data we’re going to pass in is this dictionary.

Data Class in POST Requests

And so if it’s, this is quite fun, I think. If you have data in a POST request and you have a data class, it will automatically put the, put it into that data class. So here, data is now a Bode object because you asked it to be. And if we try to put in something that you can’t fill into a Bode object, then you will, of course, get an exception. It’s like, oh, you can’t put a C in there.

[01:21:03]

You don’t have to use a data class. You can also use a dictionary. Now, if you use a dictionary, right, then it won’t be A and the number one anymore. It’ll be A and the string one because it has no idea what data type it would have to be. So this is a good reason that data classes work nicely with FastHTML. And that’s why I added Fastlight’s data class support.

[Jono]

Pydantic Validation

And you could do, like, if you wanted to be real fancy, you could do Pydantic and then have custom validation on that. Oh, it has to be a string. And also, it needs to be a string that starts with these letters.

[Jeremy]

And you can do all that. I don’t love it myself. I would rather do that in the function, the handler. To me, that’s a better place for that. Unless, like, yeah, I don’t see the point of using Pydantic for that because you can put it in the function and then you can respond to those validation issues in a more nuanced handler-specific way.

[01:22:05]

But yeah, you could.

Named Tuples

You can also create a named tuple. So that works fine. Again, named tuples are untyped. So it won’t know what that is.

Custom Classes

Or you can just create any old class. And as long as it has annotations, it will know what types things have to be. So Bodhi2 is a number because it’s locked inside here. And then, like, oh, it’s a number or none. So we’ll try and make it a number.

Parameter Handling

Yeah, so it tries. Like, basically, FastHTML does everything it can to give you the parameters that you’ve requested is basically the way to think of it.

[Alexis]

Design Patterns

So here are two things that occurred to me. Well, three. One is I’m having all these flashbacks from the last time I built a significant web app endpoint.

[01:23:00]

Like, there’s so many of the design patterns are similar. I’m not sure if this is because how everyone builds endpoints.

[Jeremy]

It’s an attempt to be as normal as possible.

[Alexis]

OK, that explains it.

[Jeremy]

I guess I wasn’t doing it in a weird way. If we go to the FastAPI tutorial, I literally went through every line of code, copied and pasted it, and tried to make it work exactly the same way, except the return is not JavaScript anymore. So if you’re a FastAPI user, it should be extremely normal.

[Alexis]

Parameter Search

So the second thing that occurs to me is that the behavior of, you know, search with fall through when you get a key, essentially, is the key something that was given to me in the path? No. OK, well, is it something that’s there in a query parameter? No. OK, is it in the header?

[Jeremy]

Parameter Order

And here it is, by the way. And when I sent this code to Jono, he was happy. He was like, oh, OK, it’s nice to see.

[Jono]

Well, and I had to see that, right? Before I saw that, I didn’t trust it.

[01:24:01]

And what I was writing was, just always give me the request. Thank you very much. And I’ll check if request.cookies or request.useragent or whatever, like, I’ll go get the thing myself because I didn’t know what magic you were doing. And so I was like, OK, fine, I can see it now. OK, now I’m happy to trust.

[Alexis]

Magic Spidey Sense

It’s slightly magic, and I’m not saying that’s bad. I can see it’s dead handy. It also triggers my magic Spidey sense. The one possible failure mode I could imagine here is if someone naively chose as their name for a query parameter a key that’s already conventionally used as a header name or a cookie name.

[Jeremy]

Parameter Collision

Well, that would be OK because headers go last. So the order of these was carefully chosen. But if you have a query parameter and a path parameter that are the same, or if you have a query parameter that’s the same as a cookie, you won’t get your cookie anymore.

[Alexis]

Header Namespace

Well, also, fortunately, the header field namespace is not polluted with, like, 8 million things that are out of our control. It’s a relatively orderly part of the universe.

[01:25:03]

URL Path and Function Namespaces

The last observation I have, and this may be just that I’m not used to doing a ton of web development, is when I look at how the code looks and how the logic of it looks, there’s these two namespaces that are side by side because we’re trying to understand their interaction, but they’re still different. And that’s the namespace of the URL path. We have to choose the string that represents the path. And then separate from that is the namespace of the function names and the parameters within the functions. So one is necessarily…

[Jeremy]

Function Naming

Yeah, the name of the function, you could call them all underscore, I think. Yeah. And it bothers me a little bit that there’s just like… So the reason I haven’t done anything about that is because this is how FastAPI and Flask and everything works.

FastAPI and Flask Behavior

Yeah.

[Alexis]

When we’re building the web from scratch, one wouldn’t want to have this weird world where you have the path, you have this, like, effectively a mini language, which is what’s expressed in the URL path.

[01:26:12]

URL Path and Function Languages

Yes. And then another language, which expresses the action that’s actually performed.

[Austin]

Yeah.

[Alexis]

The symbol that represents the action that’s performed in the server program. And then you have a whole mapping thing in between and you have to like, well, is this the right name? Am I changing the underlines? Is this exposing too much? Is this exposing too little? It all feels a bit jury-rigged together, but I guess that’s the web for you.

[Jono]

Multiple Methods

I quite like it, especially because you can have multiple methods, right? So you can have, like, the get on slash is generate homepage might be, or, you know, or home might be my function, right? Because it’s going to respond back with a full web page full of all the content that I want. But the post on that might just be like you’re submitting the form to like sign up for my email newsletter. And so then I can have a separate function that’s listening for the post option.

[01:27:03]

[Jeremy]

Function Renaming

I just renamed everything to underscore and it still works. Nice. Maybe that’s not a bad thing because like, yeah, why are you spending times naming this function that never gets used?

[Alexis]

Testing with Renamed Functions

How would you call it with testing then? If you can’t have, you can’t use the function name anymore, right?

[Jeremy]

It’s fine. I’m just, I’m client.get. Like, okay. So I do have this, all of my tests work that way.

[Alexis]

I kind of like that, but I don’t spend a lot of time doing web development.

Web Development Experience

So I don’t know if my tastes are informed by my deep judgment and experience or informed by my vast ignorance of the domain. I guess a little impeach. I’m not sure which of those I should listen to.

[Jeremy]

Conclusion

Anyway.

[Alexis]

Okay.

[Jeremy]

I got to go. This was very cool. Thanks very much. I had a feeling that was going to take a while. We’ve got a lot more. Well, not a lot more, but anyway.

[Jono]

Future Content

But this is something that we will be showing people a lot more from, I suspect. Different ways to write the same apps, different extensions of it, how to make it play nicely with the database stuff that you showed, which we didn’t really get a chance to go into.

[01:28:06]

Like why those data classes play so nicely with it.

[Jeremy]

Yeah.

[Jono]

Yeah. So there’s a few cool parts too.

[Jeremy]

All right.

[Jono]

See you.

[Jeremy]

Bye.