spotifyovercastrssapple-podcasts

elm-pages v3

We discuss the new elm-pages v3 release and the new suite of features for full-stack server-side rendered Elm.
July 17, 2023
#86

Transcript

[00:00:00]
Hello Jeroen.
[00:00:02]
Hello Dillon.
[00:00:04]
Well it's finally time to turn the page and start a new chapter of Elm.
[00:00:09]
In fact, this might be more than a chapter, this might even be a whole new volume of Elm Pages.
[00:00:15]
There are two volumes before, but by adding a third volume we've got a full stack of books about Elm Pages now.
[00:00:23]
We are going to talk about Elm Pages once more.
[00:00:31]
Yep, and we are not going to talk about Elm Pages Alpha, we are not going to talk about Elm Pages Beta,
[00:00:39]
we are not going to talk about a soon to be released Elm Pages, we are talking about a stable release of Elm Pages V3, finally!
[00:00:48]
Wooo!
[00:00:50]
We've covered Elm Pages in a bunch of episodes, like our very first episode was about Elm Pages, what is Elm Pages?
[00:00:58]
And that was for V1, V2 was released in between, we've done a few other episodes, including recently one about Elm Pages scripts.
[00:01:07]
But that was still yet to be released, but now everything is released.
[00:01:13]
Dillon is now finally done with his work, he can now retire.
[00:01:22]
At the very least I can think about other things, because it really felt like there are so many pieces to Elm Pages,
[00:01:30]
and I sympathize with users for the number of concepts that are available in the framework to grok.
[00:01:39]
But yeah, it was quite a journey to just juggle all those concepts,
[00:01:45]
and constantly try to think of better ways to simplify things, and how those pieces fit together,
[00:01:51]
and then keep doing that over and over until all the pieces fit together really elegantly.
[00:01:56]
Like I came up with a lot of very new techniques that I haven't seen in an Elm framework yet,
[00:02:04]
so it was quite an experience.
[00:02:09]
Alright, so for the few people who have not heard about Elm Pages yet,
[00:02:16]
or who have not...
[00:02:17]
For our one listener who doesn't know what Elm Pages is.
[00:02:21]
Or who hasn't re-listened to all the episodes since the beginning,
[00:02:26]
maybe tell us what Elm Pages is, and then we can afterwards talk about what is new in v3.
[00:02:34]
So yeah, what is Elm Pages?
[00:02:36]
Yeah, so the way I think about it, Elm Pages, number one, Elm Pages is Elm.
[00:02:43]
It's an extension of Elm.
[00:02:45]
So everything you understand about Elm, you can apply that understanding to Elm Pages.
[00:02:50]
So it's just Elm?
[00:02:52]
It's just Elm.
[00:02:53]
It's just Elm.
[00:02:54]
It's a single-page app in Elm.
[00:02:57]
So Elm Pages is a way to build a single-page app.
[00:03:01]
But it's a single-page app that extends the traditional Elm architecture.
[00:03:06]
So whereas the Elm architecture gives you an initUpdateView, and it renders client-side.
[00:03:12]
So you load usually sort of an empty HTML skeleton that if you turn off JavaScript, it's a blank page.
[00:03:20]
If you turn on JavaScript, it's a blank page until it hydrates the client-side rendering.
[00:03:27]
Hydrate isn't actually the appropriate term there.
[00:03:29]
Until it executes the JavaScript.
[00:03:32]
Exactly. It parses and executes the JavaScript.
[00:03:35]
Elm Pages, for one thing, it pre-renders out the HTML.
[00:03:41]
And it does that with a special addition to the lifecycle of the traditional Elm architecture.
[00:03:49]
So the traditional Elm architecture, you have init, it can kick off some commands, and then it renders a view with your init.
[00:04:00]
And it does that all on the client-side.
[00:04:02]
In Elm Pages, it runs init on the server.
[00:04:07]
So it actually takes the Elm app you write, and it runs it in two places.
[00:04:12]
It runs it on the server, and it runs it on the client in the browser.
[00:04:17]
And so, yes, it runs it twice and it creates two applications.
[00:04:22]
Because the server one is running without a browser.
[00:04:27]
It's running a headless application, but it runs your init, and it renders your view with your init, without running any of the commands from init.
[00:04:37]
It just takes your data from init, and it renders that to an HTML string.
[00:04:42]
And then that is the initial response that's sent to the client.
[00:04:46]
So the client has a fully rendered HTML view before the JavaScript takes over the page.
[00:04:52]
So that's one thing Elm Pages adds.
[00:04:55]
The other thing that's a really big piece of what Elm Pages adds is your routes have something called data.
[00:05:03]
That is an extension to the traditional Elm architecture's init update view.
[00:05:08]
So data is something where you define, in Elm Pages v3, it's called a back-end task. In v2, it was called a data source.
[00:05:18]
But that's a declarative way to define data, very similar to an Elm core task.
[00:05:24]
So you can have an HTTP request as a task, and you can transform that all into some data type.
[00:05:33]
So when that task resolves, you end up with some data.
[00:05:38]
And that data is resolved fully on the server. It's resolved on the back-end, as the name back-end task implies.
[00:05:45]
And then that data is available before init is called, before your view is ever called.
[00:05:51]
And so, whereas with a traditional Elm app, there's no data before init, unless you pass in flags.
[00:06:01]
And sometimes people do pass in flags with data from the server, to sort of bootstrap the page.
[00:06:09]
Maybe it's like a Rails app or whatever server rendering framework.
[00:06:13]
So actually, I think a really good way to think about the Elm Pages architecture is a sort of abstraction over that pattern.
[00:06:23]
It's a way to make that pattern really easy for you to manage, where you do all of that in pure Elm,
[00:06:30]
and you don't have to write all the glue code to do that.
[00:06:33]
But that's essentially what it's doing.
[00:06:35]
It's saying, hey, before we load this page, since we're serving up this request from a server,
[00:06:42]
we may as well just resolve some data and put that in the page, basically as a flag.
[00:06:47]
It actually is doing that under the hood.
[00:06:50]
So you have some data that is available as flags, and it's there before init is called.
[00:06:55]
That's exactly the mental model for an Elm Pages app for the route modules data.
[00:07:02]
Your data is available before view is ever called, unlike the commands that you return in your init function,
[00:07:12]
which are available after view is called.
[00:07:15]
So you have loading spinners and intermediary states in your model to deal with.
[00:07:20]
So that's really at the heart of Elm Pages.
[00:07:23]
It's about getting that data before your page loads and everything that comes from that.
[00:07:29]
So, for example, one thing that comes out of that pattern is you have fewer intermediary states to deal with,
[00:07:37]
fewer loading spinners.
[00:07:39]
The user doesn't have flashes of loading spinners coming in.
[00:07:43]
Does it also resolve errors?
[00:07:45]
Like if you ask for some kind of data, then you're sure to get that data, otherwise the user will get a 400 or 500 page error.
[00:07:55]
Yes.
[00:07:56]
Okay, so you also don't have to handle those error states. That's pretty nice.
[00:08:00]
It takes those out of your happy path.
[00:08:02]
So the way this works in Elm Pages v2, there was only one mode, which was rendering your pages at build time.
[00:08:12]
It was a static site generator.
[00:08:14]
Elm Pages v3 is a hybrid framework that supports both static site generation with something that's called pre-rendered routes,
[00:08:24]
and then there are server-rendered routes.
[00:08:26]
So in the pre-rendered routes, if your backend task has an error, then that results in a build failure.
[00:08:35]
And you can, you know, so, oh, this API is down.
[00:08:38]
Well, okay, then don't push that site live.
[00:08:42]
Or if you want to, you can handle an HTTP failure with whatever logic you want, retrying the HTTP request,
[00:08:52]
falling back to some data, falling back to a cached response.
[00:08:56]
You can use all of these types of strategies.
[00:08:58]
You can write general purpose Elm code to handle these errors.
[00:09:01]
And that's actually been a big part of the v3 release of Elm Pages,
[00:09:05]
partially because if you're doing server rendering, you want more fine-grained control over your error handling,
[00:09:11]
because if it's just a static site, something goes wrong, fail the build, and let me play around with it and fix it.
[00:09:19]
If it's server rendered, if something goes wrong, you might want more nuanced control.
[00:09:24]
You know, maybe it's just that you show an error page, but you want to give really good information in that error page
[00:09:31]
instead of just giving a 500 with no information.
[00:09:34]
You know, maybe you want to respond like maybe an API gives you strange error codes or fails in strange ways,
[00:09:43]
but it actually is just because it's a 404, this page doesn't exist for this user, or you're not authorized,
[00:09:49]
or whatever it is, and you want to handle that in a more sophisticated way.
[00:09:53]
But so with Elm Pages v3, there's something called a fatal error.
[00:09:56]
We talked about that a little bit in our Elm Pages scripts episode.
[00:10:01]
As I mentioned in that episode, I think that Elm Pages scripts are a very good way to get acquainted with these concepts
[00:10:07]
around back-end tasks and fatal errors.
[00:10:09]
But to summarize briefly, a back-end task has an error type, just like the Elm core task type has an error type variable
[00:10:17]
and a data type variable.
[00:10:19]
And in Elm Pages, the places that accept a back-end task, like your route module's data function, you return a back-end task.
[00:10:27]
The error type it accepts for that back-end task is something called fatal error.
[00:10:32]
And fatal error is just something that represents an error message that you can present if there's a build failure
[00:10:40]
for a pre-rendered route, or if there's a 500 error.
[00:10:44]
That's really all it is.
[00:10:45]
But Elm Pages is able to accept something of the fatal error type.
[00:10:51]
You can build your own fatal errors if you want and just say,
[00:10:53]
I want to bail out, I want to just fail the build with an error message if I encounter this case.
[00:11:01]
You can create your own fatal error type as well.
[00:11:03]
All right.
[00:11:04]
So is it, of course, there's going to be a simplification, but basically Elm Pages is a static-side generator,
[00:11:11]
which in v3 now has also server-side rendered routes and something called Elm Pages scripts,
[00:11:22]
which allows you to run arbitrary scripts written in Elm slash Elm Pages.
[00:11:27]
Right.
[00:11:28]
So that might sound like a lot of disparate concepts.
[00:11:32]
The way I would frame it that makes sense to me is Elm Pages is a tool centered around this concept called a back-end task.
[00:11:42]
A back-end task.
[00:11:43]
Oh, so it's Elm back-end task.
[00:11:45]
It kind of is.
[00:11:46]
It kind of is.
[00:11:47]
So that is the heart of Elm Pages, and it always has been in my heart.
[00:11:52]
Deep in my heart, it's always been the heart of Elm Pages.
[00:11:55]
Maybe my heart is the heart of Elm Pages.
[00:11:56]
I don't know.
[00:11:57]
But what it's all about is it's a way to declaratively resolve data.
[00:12:03]
So the back-end task API gives you a way of resolving data on a back-end.
[00:12:10]
Right.
[00:12:11]
So it's a regular Elm core task.
[00:12:17]
You can say scroll to this position as a task, or there are a handful of web APIs that are exposed as tasks.
[00:12:26]
Those don't exist in back-end tasks because they're tasks that run on the back-end.
[00:12:30]
On the other hand, an Elm core task cannot run things in Node.js.
[00:12:35]
It cannot read a file.
[00:12:36]
It cannot perform a glob to list out files matching a certain pattern.
[00:12:42]
It cannot read environment variables.
[00:12:44]
And those are often handy things to have if you're also it's not executing in a secure, trusted environment.
[00:12:53]
So even if you had a way to pull in environment variables in an Elm core task, which executes in the browser,
[00:13:02]
you wouldn't want to pull in secrets because suddenly you've revealed your secrets for doing admin database queries
[00:13:12]
or using your open AI API token.
[00:13:16]
And now you're getting slammed with requests from people who stole your token and are using that for their requests or whatever.
[00:13:24]
Right.
[00:13:24]
So you can't do that.
[00:13:26]
With back-end tasks, you can't because it executes in a trusted, secure environment.
[00:13:30]
So that is the difference between a task and a back-end task.
[00:13:34]
And it has a whole suite of APIs designed for abstracting over that.
[00:13:40]
For example, the glob API, back-end-task.glob, gives you a way to...
[00:13:45]
I'm really pleased with the way you express this.
[00:13:48]
You sort of are able to parse out parts of the file path.
[00:13:53]
So you can say things matching star.md.
[00:13:57]
But you don't do it as a string, so you can capture just that star part.
[00:14:01]
You can capture that into an Elm type, just like if you're writing up a JSON decoder using a similar pattern.
[00:14:09]
So these APIs are built in a way where you're doing that.
[00:14:12]
So it's not just like a very light wrapper that lets you execute things in a back-end context,
[00:14:17]
but it's like a really thought-out API for doing these types of tasks nicely in Elm and parsing them into nice Elm types.
[00:14:26]
So that's the heart of it. It's this back-end-task concept.
[00:14:30]
The back-end-task... yeah.
[00:14:32]
So just to finish that thought, it allows you to execute things in a back-end context.
[00:14:38]
It's secure, so you can use secrets, you can read files, you can pull in environment variables.
[00:14:45]
You can do expensive data processing and things like that before something ever reaches the page.
[00:14:54]
Whether that's at build time, where you do a lot of expensive computation, reading in a bunch of files,
[00:15:02]
parsing that in some format, organizing that in some dictionary and sorting it and counting things
[00:15:10]
and counting the number of occurrences of the most frequent words or phrases or whatever expensive thing.
[00:15:17]
You can execute those things and then have the result of those and then that's just, if it's a static page,
[00:15:28]
that's just part of your static page that's pre-computed.
[00:15:31]
Or if it's server-rendered, you don't have to pay that cost on the client.
[00:15:36]
And you don't even need to pull in the code in your bundle for things that only run on the back-end.
[00:15:43]
So at the heart of Elm Pages is a back-end-task.
[00:15:46]
Elm Pages is a framework that's centered around different ways to use a back-end task.
[00:15:52]
A static route, pre-rendered routes, can use a back-end task to have the back-end task available before the view is rendered.
[00:16:01]
You can render the head tags for SEO purposes that give you things like the nice social media previews
[00:16:07]
for Twitter and Slack and things like that.
[00:16:10]
That is there in the rendered HTML, which a lot of these tools for social previews don't execute JavaScript.
[00:16:18]
So you need pre-rendered HTML in order to display those.
[00:16:22]
So Elm Pages provides that, right?
[00:16:24]
But how does it provide that if you don't have access to your initial page data before you do that?
[00:16:31]
So back-end task is kind of a core thing around that.
[00:16:34]
Back-end task is a core thing around server-rendered routes because you resolve this data,
[00:16:39]
and you can run it in a secure environment in a back-end task.
[00:16:42]
If you want to respond to a user clicking a Pay Now button by running something in Stripe,
[00:16:49]
or if you want to respond to a Stripe webhook or whatever you want to do,
[00:16:53]
back-end task is the core of how that is done.
[00:16:56]
And back-end task is the core of how an Elm Pages script is done.
[00:16:59]
An Elm Pages script is just a little helper that lets you easily execute a back-end task.
[00:17:06]
It also lets you parse command line flags that you can use in that context of your back-end task.
[00:17:11]
But again, it's all just centered around this concept of a back-end task.
[00:17:15]
Yeah, that makes a lot of sense. I haven't seen it that way.
[00:17:19]
But it does make things a lot more... but it does make a lot of sense.
[00:17:24]
Yeah, it's more coherent when you think about it that way, right?
[00:17:27]
Yeah, exactly.
[00:17:28]
So basically what you did is you made this concept of back-end task,
[00:17:33]
and you added a lot of primitives to get the contents of a file,
[00:17:39]
to make HTTP requests ahead of time, ahead of render time, let's say,
[00:17:46]
or JavaScript execution time on the client.
[00:17:50]
And then you asked yourself in a way, where should this apply?
[00:17:56]
And for Elm Pages v1 and v2, that was, well, this is for the front-end.
[00:18:02]
Like, we're executing something at build time,
[00:18:05]
and then you have some data that is sent to the front-end as plain HTML,
[00:18:11]
and then there's some hydration or re-execution of the Elm code.
[00:18:16]
And now with Elm Pages, you've made this apply for more things.
[00:18:21]
So you have Elm Pages scripts, so it's like, well, we have all these nice things
[00:18:26]
that are to get the contents of files, to write to files, to do things like that.
[00:18:32]
Would be nice to just be able to run them to see, like, what is,
[00:18:37]
if I run this code, do I get what I expect
[00:18:42]
without having to run this whole server-side rendering machine?
[00:18:47]
And then you also have this server-side rendering,
[00:18:50]
because that's something that apparently the web community expects or wants
[00:18:56]
and has a lot of value, which let's talk about that in a bit.
[00:19:03]
But yeah, this makes a lot of sense.
[00:19:06]
And I'm kind of wondering, like, if you knew that this was the core part
[00:19:12]
of the package or the tool, because it's more than just a package,
[00:19:17]
would you have called it differently?
[00:19:20]
Because it's Elm Pages, but the Elm Pages scripts has nothing to do with Pages,
[00:19:25]
and it doesn't have to be anymore.
[00:19:27]
I'm guessing if you had renamed it to something else when it was still in V2,
[00:19:32]
you would have called it Elm Data Source,
[00:19:35]
which you would be unhappy with today, I'm guessing.
[00:19:38]
Yeah, and I'm not sure Elm Backend Task really captures what you do with it either.
[00:19:45]
But I don't know, maybe like...
[00:19:48]
Yeah, maybe as an underlying package of Elm Pages.
[00:19:52]
Maybe like Elm Backend, I don't know.
[00:19:54]
But then it's like, well, Elm Backend, it's like, well, can you do things with a front end?
[00:19:59]
It's like, yeah, it's about interopting between a front end and a back end.
[00:20:04]
So yeah, it's hard to name.
[00:20:06]
If anybody has a genius name, definitely let me know.
[00:20:12]
But also, I think I mentioned this in the scripts episode we did,
[00:20:16]
but the origin of Elm Pages scripts was that I was actually wanting to build more customizable scaffolding.
[00:20:25]
Elm Pages V2 had an add route command, or Elm Pages add command that let you add new routes
[00:20:33]
that would scaffold out the starting template for a new route.
[00:20:36]
And I wanted to make that more customizable.
[00:20:39]
If you want to use Elm UI or Elm CSS, Elm Tailwind modules,
[00:20:47]
if you have a specific pattern that you use in all of your routes that you want to bake into it,
[00:20:55]
whatever that might be.
[00:20:56]
So when you say scaffolding, you mean like the tool creates a blank file or a few blank files
[00:21:04]
with some predetermined data in there.
[00:21:08]
Like maybe if you say, I'm just imagining something,
[00:21:12]
like if I do Elm Pages add slash users slash ID, or however that's,
[00:21:20]
is users dot ID or something?
[00:21:22]
Yeah, you do it like an Elm module name.
[00:21:25]
Okay, so if I do Elm Pages add users dot...
[00:21:30]
ID underscore is a dynamic one.
[00:21:34]
So ID is a dynamic thing.
[00:21:37]
Then I would get a Elm module at the right position.
[00:21:42]
And it would contain like, oh, a command saying this renders at slash users slash colon ID,
[00:21:50]
something like that.
[00:21:51]
And it actually would because it uses file-based routing.
[00:21:54]
So it would create a file in the appropriate place for that route module.
[00:21:59]
And then...
[00:22:00]
I'm guessing an init view updates.
[00:22:02]
Right, and the view would be view dot placeholder because your view might be Elm CSS or Elm HTML or Elm UI.
[00:22:12]
So having a user-maintained module called view dot Elm with a type alias view being whatever type their view is,
[00:22:21]
and a user-maintained function called placeholder, which renders out the placeholder.
[00:22:26]
Let me scaffold out something with the view using the same pattern that Ryan came up with for Elm SPA.
[00:22:35]
And that was great, but it was like, well, I want to make it more customizable than that.
[00:22:40]
And I also like if you can...
[00:22:43]
If you want to have...
[00:22:45]
If you want to be able to pass in CLI arguments to customize how you're generating it.
[00:22:50]
If you want to generate it with some GraphQL query or some HTTP request or something like...
[00:22:57]
The sky's the limit.
[00:22:58]
There's so many things you could do.
[00:22:59]
Or maybe you want to scaffold out modules that aren't even route modules.
[00:23:03]
Maybe they're helper modules for defining some backend tasks or whatever it may be.
[00:23:08]
So I just wanted to make that more customizable in general.
[00:23:12]
And so I knew I wanted to basically say, well, I want to have a way to make scaffolding more customizable.
[00:23:20]
And I would love to do that using Elm CodeGen.
[00:23:22]
So you can write Elm code to generate your Elm code for these modules.
[00:23:26]
But I also want to be able to pull in HTTP data, pull in environment variables, read files if I want to read a config file, things like that.
[00:23:36]
So it's like, well, OK, I want to be able to execute backend tasks in this context when I'm running the scaffolding script to generate a file.
[00:23:44]
And then I'm like, you know what?
[00:23:45]
Like if I just make...
[00:23:46]
Like there's very little difference between a custom scaffolding script and just a script.
[00:23:53]
So why don't I just make a script feature and the scaffolding script is just a special case of that.
[00:23:58]
So that's what I ended up with.
[00:24:00]
Nice.
[00:24:01]
So for instance, I know it's like not the best use of it, but if I want to customize my scaffolding, I could create a backend task that gets the current time and then inject a comment saying this was generated on this date.
[00:24:21]
Absolutely.
[00:24:22]
That's pretty cool.
[00:24:23]
Yeah.
[00:24:24]
Terrible use, but I mean, why not?
[00:24:28]
Yeah, I mean, you can read configuration from some JSON configuration file and use that to generate something and the sky's the limit.
[00:24:39]
Yeah.
[00:24:40]
Yeah.
[00:24:41]
I'm really curious to see what people come up with or what they used it for.
[00:24:45]
Me too.
[00:24:46]
Yeah.
[00:24:47]
I'm not going to go down a rabbit hole, but there's a new feature where you can execute an Elm pages script pointing to a GitHub gist or a link to a raw GitHub file, including a branch or just pointing to a GitHub repo and then you provide a path to a script.
[00:25:06]
So I'm really excited to see what people do with scripts now that it's like much more shareable, you can just have a gist that has your scaffolding and your, you know, whatever logic for a little helper script you have.
[00:25:21]
So scripts are a lot more shareable now and a lot easier to play around with.
[00:25:24]
Has that last feature you talked about been released?
[00:25:27]
It's released.
[00:25:28]
Yeah.
[00:25:29]
Okay.
[00:25:30]
I haven't done an announcement post, but it's released and it's stable.
[00:25:33]
Yeah.
[00:25:34]
Okay. I was thinking it wasn't released yet.
[00:25:37]
I'm like, we should finally talk about things that have been released.
[00:25:43]
This is a bad habit.
[00:25:45]
This is an intervention.
[00:25:48]
Let's talk about things that have been released, but okay.
[00:25:51]
It has been released.
[00:25:52]
I have nothing to say.
[00:25:53]
Turned over a new leaf.
[00:25:54]
It's released.
[00:25:55]
So we should probably talk about the big headline of ElmPages v3, which is server rendered routes.
[00:26:04]
So first of all, maybe we should define server rendering and what that's in contrast to.
[00:26:12]
And also you say server rendered routes.
[00:26:16]
And the term that I hear from the JavaScript ecosystem is SSR, server-side rendering.
[00:26:24]
Is that the same thing or the difference?
[00:26:27]
It is.
[00:26:28]
Yeah, it's exactly the same thing.
[00:26:29]
But your name is better for some reason that you're going to explain it now.
[00:26:35]
Well, in the context of an ElmPages app, you can have a pre-rendered route or a server rendered route.
[00:26:41]
Pre-rendered route, the backend task is resolved at build time.
[00:26:45]
It results in a pre-rendered HTML file on disk, which your hosting provider can serve up or through a CDN or however you want to do that.
[00:26:56]
Like a static HTML file?
[00:26:58]
Yep, you get a static file.
[00:27:00]
You also get a static file called content.dat for that URL.
[00:27:06]
And that contains the serialized data for your route.
[00:27:11]
Now, the reason for that, so remember before I mentioned that conceptually you can use the mental model of, you know, like a Rails app that bootstraps some server data and passes them through ElmFlags.
[00:27:26]
So that is actually exactly what happens if you load an initial page in your browser with HTML.
[00:27:36]
But then if you click a link, it's not loading an HTML page.
[00:27:40]
So if you click a link, it's actually loading just a single file.
[00:27:45]
It doesn't load the HTML because it doesn't need that because it is now in single page app mode.
[00:27:50]
So it's hydrated into an Elm single page app.
[00:27:53]
Now it only needs that data for the page.
[00:27:56]
So that's what the content.dat is for.
[00:27:58]
So if you have a blog slash introducing v3 post, that has some the markdown data, the parsed markdown data and the publish date and whatever other data is resolved for that, that routes data is serialized as bytes in a byte format there.
[00:28:20]
That gets downloaded by the browser.
[00:28:23]
Exactly.
[00:28:24]
That's in the metadata or the head tag.
[00:28:27]
Exactly.
[00:28:28]
So the Elm pages framework itself, when it's running in the browser, it knows when you click on a link to go fetch the corresponding data.
[00:28:37]
So it can, you know, it's a single page app, but it knows before I change routes to this page, I'm going to need this data.
[00:28:45]
And is that JSON in practice?
[00:28:48]
In v2 it was JSON.
[00:28:50]
That is then sent to the init function as flags.
[00:28:54]
In, it's all binary and it uses the Lambdara wire encoders to encode that data to binary.
[00:29:04]
In v3. Okay.
[00:29:06]
Yeah. So Lambdara, so, and I think this is worth being explicit about.
[00:29:11]
So there are two meanings of Lambdara.
[00:29:15]
One is a compiler and one is a hosting platform.
[00:29:20]
Elm pages v3 has nothing to do with Lambdara, the hosting platform.
[00:29:24]
Lambdara, the hosting platform is awesome.
[00:29:26]
You should definitely use that.
[00:29:27]
If you want a Lambdara app, an app that is using WebSockets to have multiple connected clients, sending real time data and doing evergreen migrations.
[00:29:37]
Awesome platform.
[00:29:39]
Nothing to do with Elm pages v3.
[00:29:41]
Elm pages v3 uses the Lambda compiler, which is it's exactly like the Elm compiler, but it adds a couple of things, including the ability to automatically serialize or deserialize an Elm type.
[00:29:55]
And that is how in v3 Elm pages manages taking that data, which is resolved on the server and then hydrating that on the client.
[00:30:05]
And this is actually a huge improvement in Elm pages v3, because in Elm pages v2, there was something called optimized decoders, which I think we talked about at length in the Elm pages v2 episode.
[00:30:18]
So we're going to talk about it at length again now?
[00:30:23]
Well, fortunately, you can forget about all of those concepts that you learned.
[00:30:29]
And I'm sorry if you spent time learning and mastering those concepts, but fortunately, you get all of those same benefits with Elm pages v3 without having to learn any of that or without having to import a drop in replacement for JSON decoders, which is what optimized decoder was.
[00:30:46]
You no longer have to worry about your data being sensitive data being used.
[00:30:51]
So like Elm pages v2, there was like this design when you when you do an HTTP request, you could you had to wrap it in this secrets thing, which basically would obfuscate any secrets in the JSON data, because what it needed to do is it needed to basically mark every bit of data you depended on as you resolved your data sources and pull in the corresponding JSON.
[00:31:18]
And it did like basically key value pairs with the definition of the request it was pulling from to the data for that request.
[00:31:27]
So it had to scrub out sensitive data in the request in the keys of the JSON values.
[00:31:32]
And it had to go and mark all the things that's what optimized decoders did is they would mark the things that were needed and only and strip out all of the data that wasn't used.
[00:31:42]
It was very complicated to build, but it it allowed you to like make it an API request in the data source that pulled in some JSON data, but only used a few keys, but it wouldn't pull in all of the JSON data.
[00:31:57]
But with with v3, it just resolves all of the data in your back end task that resolves to your whatever your route modules data type is, and then it it just serializes that data type.
[00:32:12]
So you don't have to worry about what data you had that went into resolving that data. It's just what is the final data type that gets serialized in a binary format, and then passed in when your page is hydrated, or when you navigate to a new page, it gets downloaded.
[00:32:27]
So much simpler, and much more performance.
[00:32:30]
Yes, and better for security as well, which was one of the main motivations too. So I was like, if you're doing server side stuff, I don't want you to have to think about like, am I pulling in sensitive data?
[00:32:42]
So yeah, because it was still possible to mess it up and to expose secrets.
[00:32:48]
Yeah, even though there was a whole affordance for for working with that it, it's something that you could get wrong. So you no longer have to think about that at all. And it's more compact binary data. So it's a win win.
[00:33:02]
You do need to ensure that your route modules data type, there's a type called data, usually it's a record type alias data, you do need to ensure that it does not contain any non serializable data, namely functions, functions are not serializable.
[00:33:18]
And data structures that contain functions are not serializable. Some of them are surprising, like HTML, I don't know if it actually contains functions in Elm's implementation.
[00:33:27]
Yeah, because you have HTML to map.
[00:33:30]
Exactly. Yeah, if you have a map, right, exactly. That's right.
[00:33:35]
Okay, so server side rendering, and or server rendered routes, SRR.
[00:33:42]
SSR. Yeah. Should we talk about what you can do with server rendered routes?
[00:33:48]
Yeah. What is it for? What can you do? Why is it better? Why is it worse? Sometimes? Let's let's first talk about what it is. Yeah.
[00:33:57]
And you're right to ask, when is it better? When is it worse? Because so someone asked about this in the Elm pages, like channel recently. And my answer was, if you if you don't need fresh data, like a blog, you know, like if you if you publish, if you're running a news site, and somebody edits content, there's a typo or a correction or some emergency report with an update or something like that.
[00:34:26]
And you want it to instantly be reflected when somebody loads the page, then then server side rendering is going to be a good approach. So a server rendered route would would be a good fit for that.
[00:34:38]
But if it's a blog, and you're like, okay, I published something, and within within a few minutes, it's live, then I would go with a pre rendered route. And the reason is, because we've discussed this before, with a pre rendered route, you have the ability to essentially parse don't validate your initial route data with any errors being parsed out of the happy path to a build error.
[00:35:05]
Whereas with a server rendered route, it gets parsed out to a 500 error. So that's one thing you have to deal with. Now, if you're building a CRUD application, or like things happen, and you can't guarantee that every service will always be up, right. So you do have to deal with that possibility.
[00:35:26]
But if it's just like a little blog, or a portfolio site, or something like that, it's just like, you don't want to have to put that much thought into how you respond to those types of errors, you're just like, well, if something's wrong, then I'll just build it later or try to fix it.
[00:35:41]
And you can mix and match them. For instance, if you have a news website, then you can use server rendered routes for the content. But the about page can be a pre rendered route.
[00:35:56]
Yeah, exactly. Exactly.
[00:35:58]
And can you also have like, plain elm pages, like the way that we currently do it without elm pages, just a blank HTML.
[00:36:07]
Right. In other words, a client side rendered page, you cannot, in theory, it would be definitely possible to do that. But at the moment, elm pages does not have that. So you know, you can have a page with an empty back end task with a back end task that's just back end task to succeed, empty record or whatever. But, but yeah, there's no feature that it will still fetch that empty data before it loads your page.
[00:36:34]
Also, something I've been wondering, like, ever since we started recording is like, can you have, can you mix the pre rendered routes, and the server rendered routes into one in the sense that can you have a back end task that resolves a build time, but that is then used for a server rendered routes.
[00:36:59]
So like, part of the things that I need for this page are computed the build time and other things are computed at render time. Is it possible to have both?
[00:37:10]
That is not possible. But I mean, you could always like have an elm pages script that pre compute some data and use a codec to deserialize it or but yeah, so the options are, there's one we haven't talked about actually pre rendered route, which is the back end task data is resolved at build time.
[00:37:32]
And then of course you, you have a standard elm architecture. So you can also use your init update to perform HTTP requests and that sort of thing.
[00:37:43]
There's also server rendered routes, which we've talked about. And then there is something called pre render with fallback. So pre render with fallback will pre render a set of pages for the route. But then it will dynamically execute the back end task for that route for anything that wasn't a pre computed page in there.
[00:38:09]
So you basically give it a list of pages, it's actually a back end task of a list of of pages. And then it's going to go so like for example, your most popular or recent articles could be pre read pre rendered.
[00:38:25]
And then your archive, which you know, maybe your archive from 1932 is not requested very frequently. So you don't want your entire build to have to build your entire archive. But if somebody requests it, then you build it.
[00:38:42]
And once it's built, it will cache that. So pre render with fallback is interesting because it, it really depends on your hosting provider how it's going to be actually implemented.
[00:38:56]
So with with Netlify, they have something called DPR, distributed persistent rendering, which is like a weird kind of marketing buzzword, I guess. But they basically have this idea of, of exactly that pattern. And if you use a pre rendered, if you use pre render with fallback with the Netlify adapter, then that's that's what you get.
[00:39:17]
And you can define different ways to handle those types of routes with different hosting platforms through Elm pages adapters. Adapters are the way that you take you take an Elm pages app, when you run Elm pages build, it will run your adapter, which you define in the Elm pages config file, Elm pages dot config dot mjs to JavaScript file and you you can import an adapter.
[00:39:46]
The default adapter in the starter is a Netlify adapter that will execute the the server rendered routes through Netlify functions. But there's a GitHub issue that I'll link to our GitHub discussion where people can share different kind of community adapters they've written.
[00:40:03]
And an adapter is basically you're, you're given like a JS file that is all of the compiled Elm code for running the server rendering function. But then you generate all the surrounding files for running it in your specific framework and hosting provider.
[00:40:22]
So like there's an express adapter that someone has been writing in the community and it has a way to wire it up through an express middleware. So it's just like a little bit of glue code that helps set things up for a specific provider and framework.
[00:40:38]
Gotcha. And provider is a hosting platform.
[00:40:42]
Right, exactly. And, and which they, you know, they have specific file structures that they expect. And, you know, where, where do serverless functions live? Or, you know, if it's express, if it's, you know, maybe you're running it in some Kubernetes context that you need a particular thing, whatever it might be, you can you can build that into your adapter scripts.
[00:41:05]
But basically the contract is that you're given this bundled file that has the, the server rendering thing that handles all of the Elm pages server rendering. You give it like, here's the, here's the route. How am I responding?
[00:41:20]
And it gives you the response back. And then you have to do something with that response. So if it's in express, how do you take a response and turn that into the right data that express expects for a response?
[00:41:33]
And how do you take the incoming HTTP requests and turn that into the format that Elm pages expects for an HTTP request with the HTTP method and the incoming URL? So that that's what an adapter is.
[00:41:46]
Okay, are these fairly simple? Or are they pretty complicated?
[00:41:50]
They're pretty simple. I mean, the the the contract is pretty simple, because it's just saying, I need the incoming HTTP request in a specific format, I give you a function that you call with that specific format of data for the HTTP request.
[00:42:05]
So you might have to adapt your frameworks, you know, express or Node.js or Coa or whatever it might be, you might have to turn that into a different sort of JSON object for what Elm pages expects. And then you have to turn it into a response object.
[00:42:20]
And then you have to put some files in a specific place. But the part that Elm pages gives you is, here's a function to, to call the rendering and get a response back, right? And then you just need to wire that up.
[00:42:30]
Yeah, the hosting provider is now a big part of web development as well, right? And this is something that we now have to face if you at least you want to do server side rendering and serverless.
[00:42:43]
Yes, maybe we should talk a bit about the use cases.
[00:42:47]
Yeah, I was thinking like, we still haven't said like, what is it for? What is it good for? We said where it was complicated. But that's it.
[00:42:55]
Exactly. Yeah, right. So yeah, like, what would you actually do? So for example, if you have a CRUD applicant, like if you want to have an admin panel where you can manage users, manage user permissions, you know, send some forms to make a database request.
[00:43:12]
And, you know, or if you have an e commerce site, and you want to manage back office things, orders, inventory, forms that do that, right, v2 would not have supported those kinds of use cases, at least it wouldn't have given you much help there, it wouldn't have been a great fit for that, right?
[00:43:30]
Because if you're resolving your data, at build time, when your data is mostly dynamic, and it depends on the user session, the logged in user, it doesn't really help you there.
[00:43:42]
Yeah, so you would either need to rebuild like, continuously, or you would need to do a lot of the dynamics things in on the client anyway.
[00:43:53]
Right? Yeah, it just wouldn't make sense. Also, if you have a conceptually infinite number of routes, you can't even pre render all of those routes. So it just wasn't really possible. So now with v3, when you define when you define a pre rendered route, you do not have access to the request.
[00:44:12]
But when you define a server rendered route, you do have access to the request. And the request allows you to get the method of the request, the URL, the query params. So you know, in a in a pre rendered route, somebody can load that page with query params, but you don't have them until the page hydrates, because the page is rendered before a request is made. So conceptually, they don't exist at that point. But for a server rendered route, they do exist.
[00:44:39]
Yeah, you also have access to cookies. Right?
[00:44:42]
That's right. You have access to all of the headers, including cookies. And there are some high level abstractions for accessing and setting cookies. And that's a huge part of v3 is that design and that philosophy of using the platform because I believe it really makes things easier. Like I believe that cookie based session management is way easier than JIT token, JWT token based authentication.
[00:45:08]
Oh, okay. But could you use JWT tokens? If you wanted to?
[00:45:13]
You could, but I mean, I don't, I don't think you'd want to. But so yeah, for security reasons to like there are some advantages to cookie based authentication. So for example, in the cookie API in on pages v3, the default strategy for cookies is HTTP only, because that's like a common best practice. And so you can opt into cookies that are visible to JS, but by default, they will only be available through HTTP.
[00:45:41]
So if you have a server rendered route, and you manage the user through a session, then that cookie, you can't, if somebody injects some cross site scripting attack script in your in your page, which of course, you'd prefer not to happen, but that's one possible event attack vector, they would not have access to the HTTP only cookies. That's what an HTTP only cookie means.
[00:46:07]
When you do whatever it is document cookies, it show up there. So but when you make a request to to a server at the same origin, it is accessible. Whereas if you make a third party request, the default security policy for for on pages cookies and for for regular cookies, if you don't explicitly set one, is that those cookies will not be sent to external origins only to the original origin.
[00:46:36]
So that means that, you know, it's just less to think about for for for the attack vectors for people trying to take your cookies. So because if someone gets your session cookie, then they are you right, they've successfully hijacked your cookie, if they if they get that cookie, then then they're you. So you don't want that to happen. But, uh, but yeah, so with on pages v3, you have access to cookies, there's a there's a session API that even manages key value pairs of cookies.
[00:47:05]
So it will serialize those key value pairs of strings for you in in the cookie. And you can give it a list of secrets. It's actually a back end task of a list of secrets because that back end task, you can do environment variables, because you might want to, you probably want to pull in an environment variable and environment variable is accessible through a back end task.
[00:47:30]
So, so you can list out session secrets through these back end tasks, and you give it a list a back end task of a list of strings, and it will use those to rotate the signing. So what that means is, so first of all, signing, signing the session, what that means is it's not encrypted, it's signed, which means if you tamper with it, so there's like the raw key value data, you can you can look at the cookie and see the data that was encrypted.
[00:47:59]
So you can see the data that was encoded there directly, I think it's like base 64 encoded maybe but you can basically 64 decode it and just see the what the values are.
[00:48:10]
But if you change the values, this, the signature, the hash that signed it will not match. So you need the signing secret in order. So it's tamper proof. So, you know, if it says like user ID is 123 and you say, Ah ha ha user ID is 124, then the signing will will fail, it will not unsigned that session, and it will rotate through those.
[00:48:35]
So if you give a list of secrets, it will use the first one in the list to sign new requests, but it will, if it fails to unsign it with the first one in the list, it will go through the rest of them to try unsigning so you can rotate through your signing secrets.
[00:48:51]
So before you said that an admin page, for instance, was a good use case for the server side rendered, right? ssr. I don't know. I think that's what it is. Some combination of SS and yeah, I mean, it's better than our own pages review code gen naming. So far, so admin pages, for instance, are good for this.
[00:49:15]
But is it better than a plain Elm client app? And if so, how?
[00:49:21]
Right. Yeah, it's a great question. So it's a different architecture. And actually, we kind of we kind of talked about this in our writing great docs episode a little bit of being being clear and straightforward to help users make decisions about what tools to use in your documentation. And, and I talked a little bit about how I put a lot of thought with for Elm pages v3 into how do I help users make that decision between Elm land and Elm client?
[00:49:50]
Elm land, which is a new version of Elm SPA, or Elm pages on pages v3. Since since now there's a lot of overlap in the kinds of use cases they can handle, right, you could do an admin panel in Elm land, you could do an admin panel and in Elm pages v3. And to me that the answer is like, it depends on what kind of architecture you want to use.
[00:50:13]
And of course, like, you know, do you want to use back end tasks to get that initial data? Do you want to do you want to use the platform and use cookie based authentication and use the URL as much as possible? Because that's like a key philosophy around Elm pages. It has this philosophy of use the platform. It has an API for submitting forms.
[00:50:39]
Yeah, which we did a whole episode about like two actually.
[00:50:44]
Yeah, that's right. That's right. Exactly. So see those episodes for more about the form design. But basically, that's like a core philosophy of Elm pages is the web has forms. And so let's use forms because if we use things that the web has built in opinions on, we can piggyback on those to make things simpler for the developer to write and rely on these web standards to reduce the amount of boilerplate and, you know,
[00:51:13]
things just line up nicely. But, you know, if you buy into that philosophy of use the platform, and you want to work that way, that's great. If you want to kind of deviate from the platform, then you might not have a great experience with it, because it sort of is like designed to work really nicely, if you buy into this architecture and this philosophy. So that's what I realized. It's about buying into an approach.
[00:51:42]
Yeah, if you want to do the right things, then use Elm pages. If you want to do the wrong things, use something else. Gotcha.
[00:51:48]
I mean, you know, there are, there are different, you can do very bespoke things, right. And, you know, if you're doing like a Google Docs thing with, you know, multiplayer editing, where people can see the cursors of other people and stuff, it's like, well, okay, like, use the platform is great. But what's the platform for that? Like, it doesn't really get me anything. Right. So,
[00:52:14]
Yeah.
[00:52:15]
Elm pages might not be a good, good choice there, right. But if it's like a, if it's an admin panel thing, so like, if you're trying to do a post, you know, hit the submit button on a form and create a new entry in an admin panel, then with Elm pages v3, like, it, there's a certain resilience because it works before the JavaScript hydrates, which might seem like nitpicking, but it's like a real thing.
[00:52:44]
That, that, that can happen. So it's like one, one less thing to think about, like, you know, do I need to have the button disabled before the page is hydrated and stuff. And it's also a philosophy of, you know, it's a certain performance philosophy where it's, you know, about having your data result, you know, your initial data resolved to reduce loading spinners.
[00:53:07]
But if you're not heavily optimizing, you know, for example, having your, your data center co-located with where your Elm pages app is hosted, you might not get those performance benefits. But if you do, then you, you don't have to incur the cost of making multiple round trips and incurring the latency costs between the client and your data center.
[00:53:29]
Instead, you pay that very small latency cost that hopefully approaching zero latency cost of resolving data from the Elm pages backend to your data center. You do all the little round trip requests, which are very short to resolve all of your initial page data that you need.
[00:53:49]
And then you serve a fully hydrated page and you do your processing of that data on the backend and you don't need to ship that code that was used to resolve that data in the backend. So like that's, but if like not everybody's going to want to architect their app that way.
[00:54:05]
And, you know, and also like Elm pages gives you an architecture for reaching through and sort of directly grabbing your data. That doesn't mean that you can't hit a GraphQL API or something like that, but you can, you can make a database request from a server rendered route and directly resolve data, you know, often using like a backend test dot custom, which allows you to define a custom node JS async function to your data center.
[00:54:34]
You can use a custom node JS async function to, you know, receive JSON data and return JSON data that will be resolved in that backend task. But under the hood Elm pages uses ports, but it, it wires that all up for you.
[00:54:49]
You define a file called custom backend task dot TS or dot JS, and you export async functions, and then they take one argument, which is the JSON encoded value that, that you call that you pass in from Elm.
[00:55:06]
And then they return a JSON value and you decode, you give a decoder for that in Elm. So yeah, you can, so again, that's like a philosophical architectural choice. Like, is that an appealing way to work? Because if you're not getting benefit from that, this architecture might, you know, it might be trying to fit a square peg in a round hole.
[00:55:30]
Yeah. For the performance, just so that I get it. So the server that is, so the server that the client hits and asks, like, Hey, I want this page at this URL that will communicate that server will communicate with some database or some things, hopefully on the same server or very close to, and then it will give back a page that is filled with a lot more data that it would have fetched otherwise on the client.
[00:55:59]
So the time to first paint might be slower because the server is doing more computations and more requests. So it will take longer to resolve. So you will have a blank blank page for tiny bit longer in the good use case, I guess.
[00:56:17]
But once you get the data, it's all there, or almost all there. Whereas with a regular application and full front end clients, you would have time to first paint that is very short, because you just ask for a static file.
[00:56:37]
But then it goes to fetch a lot of JavaScript, a lot of files, and in the init it makes an HTTP request to get a lot of data. And then once all those things are done, then you have a page that has all the data. Is that correct?
[00:56:54]
Well, kind of. The time to first paint being slower is not necessarily the case, though, because consider the time to first paint for an Elm application. So when is it painting your Elm application? Basically, one question is, what's faster to paint?
[00:57:13]
Some HTML that you return or some JavaScript, right? With the JavaScript, it needs to probably make a follow up request for a JavaScript file, right? So that's one more step in the waterfall that it needs to do.
[00:57:30]
Which you don't in the server?
[00:57:32]
The HTML is just there right away. You need to parse the JavaScript, which takes some time. And while you're parsing the JavaScript, that's a blocking operation, so it's not doing anything else. And then you can render the page.
[00:57:47]
But then, of course, now that you've rendered the page and initialized the JavaScript application, you're going to need to go and trigger your HTTP requests to get your initial data, right? So yes, now you've painted it, but now you need to go ask for your initial data.
[00:58:09]
You don't even have that yet. Maybe you even need to do a JIT authentication handshake or whatever first. And so all of these things are going to need to happen after the paint, right? That's not even before the paint.
[00:58:24]
But getting to the first paint, it's a question of what's going to be faster, serving and rendering that HTML or serving and rendering that JavaScript, right? And it's actually not an obvious answer, which it's going to depend on a case by case basis.
[00:58:41]
But there's a lot that needs to happen to initialize that JavaScript as well. So that's part of that philosophy of that architecture, right? If you can get your data resolution on your backend down to 50, 100 milliseconds or whatever, then you can have a pretty compelling story where, like,
[00:59:07]
all right, I resolved the data, rendered out this HTML. Now you get a first paint. And so you can actually get a faster first paint. Of course, if it's a pre-rendered route, then you don't even need to resolve the data and you can serve it through a CDN, you could serve it at the edge.
[00:59:24]
But you can also serve it at the edge, you know, with a server rendered route as well. And you can use caching, you know, you can use a Redis cache to optimize your data loading and all sorts of so it really depends on your architecture, right?
[00:59:42]
But what did you mean with rendering at the edge? Like I can get for the static rendered routes or pre-rendered routes, but for server-side rendered routes?
[00:59:55]
Right. So edge functions are sort of an emerging approach to serverless functions.
[01:00:03]
So that's one specific technique in serverless?
[01:00:09]
That's right. Yeah, that is something that some hosting providers are starting to really build out a lot of features around. But yeah, so like edge functions, as opposed to serverless functions. So a serverless function might be US East, you know, it might be an AWS Lambda in US East. So if you're
[01:00:32]
You mean specifically in one location, and that's it?
[01:00:36]
That's right. So there's some JavaScript code that some US East Lambda will be able to pick up and execute and somebody in Tokyo hits your server rendered route, and they go all the way to US East and they request that data.
[01:00:55]
And they have to go to US East? Like there's only one copy, one server that can do this?
[01:01:02]
Yeah, and maybe there are a couple or, you know, something like that. But the edge functions are this idea that let's put them at more places, you know, more, you know, serverless locations where we can do the same thing as serverless functions where you call a function as a service or whatever, but it's distributed across more locations physically.
[01:01:28]
Then of course, you do need to think about your data center story, right? Because if you're calling, you know, if the server is serving it up from a closer location, but it's farther from your data center, that's not good either.
[01:01:44]
And there are places that are exploring ways to do more sort of distributed edge data, you know, whether it's key value or, you know, NoSQL or SQL style databases or whatever. A lot of things are being explored there. It's a big, big world.
[01:02:05]
Yeah, it sounds a little bit complicated in practice.
[01:02:09]
It is. It's hard to navigate.
[01:02:11]
Yeah, it sounds pretty simple, but also like how to optimize all that.
[01:02:16]
Totally. Totally. But on the other hand, like you can, you know, have if you've got your data center, and you've got an express server, you can serve up some Elm pages routes too, right? So that's...
[01:02:30]
And so if you want to use Elm pages with an edge architecture, would you, how would you do that? Would you just define it in Netlify or whatever your hosting platform might be? Or do you have to write an adapter for that?
[01:02:47]
Yeah, you would need an adapter. The Netlify one specifically, I'm not sure that Elm pages works with Netlify edge functions right now, because they use Deno. And right now Elm pages is node based. It doesn't have like a D, it's really hard to decouple between different JS frameworks.
[01:03:09]
But so that's definitely one thing on my radar. But yeah, at the moment, they use Deno and it is not, I don't think it's compatible with it.
[01:03:17]
Okay, interesting.
[01:03:19]
But yeah, so server rendered, it's a big world, but I think that it's easy to get lost in those details. But I would say for people thinking about building something with this, the bottom line is, you know, again, it's this pattern of bootstrapping your page with data from the server.
[01:03:38]
And Elm pages v3 is an abstraction for, for being able to basically automate the glue code for that pattern. Right. So, and again, like, you, you might not want to use that pattern. And if you don't want to use that pattern, then then Elmland is probably a better fit, because it doesn't, it's not bound to that pattern.
[01:03:59]
But Elm pages v3 allows you to use that pattern in a really seamless way. That's sort of its core design and philosophy. And, and it has a lot of things around that, like the session API and, and the request API for looking at request data, you can even define API routes, where you can write pure Elm functions that respond to an API route.
[01:04:26]
So if you want to have an API route that doesn't have a view, it's not a page, and you want to respond to a webhook with a Stripe, you know, some sort of header that has some Stripe signing secret.
[01:04:41]
So you know, it's coming from Stripe, and you can respond, you know, respond to a payment being received by Stripe through a webhook, you can you can build that in pure Elm and resolve it with back end tasks and even directly make database requests to manage things. So
[01:05:00]
Did you just casually say, Oh, by the way, we can do back end in Elm pages.
[01:05:06]
That's, that's the headline. Exactly. Yeah.
[01:05:09]
Okay, well, that was not clear enough.
[01:05:12]
Yeah, maybe we should have done a
[01:05:15]
That was not clear to me.
[01:05:17]
From the start. Yeah, you can, you can write back ends, you can write APIs, you can write a JSON API endpoint. I mean, it's pure, you know, it is, you can write pure Elm code that can that can respond to server requests with the incoming HTTP request payload, whether that is a route module, or an API route, which doesn't have a view, you can do that. You can do that with Elm pages v3.
[01:05:46]
I didn't understand that you could do non view things or non HTML things with it. That's very cool.
[01:05:53]
Yeah. And you can you can pre render your API routes to also known as files. But, but yeah, it's the you can, you don't have access to the HTTP requests. But if you you know, for example, the Elm radio podcast feed is, it's an RSS file, that is an API route.
[01:06:15]
But if we wanted to make that a something that you know if we really wanted to be able to just instantly go live with an Elm radio episode at a very specific time.
[01:06:30]
Which we don't really have that problem because we have, you know, like a specific time that an episode releases and we can just do that with a build step in and have it through a cron job. But if for something where you wanted to be able to like drop something at a specific time and have that update in real time, we could have it be a server rendered API route.
[01:06:52]
And then the RSS feed and we could take that same route that is now pre rendered and say, okay, make it server rendered. And now I have access to the incoming request. And now, okay, here's a, here's an Elm radio pro feed, and you're going to need authentication.
[01:07:09]
So you give some query parameter or some cookie or whatever it might be to get your pro Elm radio feed for your user authentication. We could do that. Pretty interesting.
[01:07:23]
So I know you've taken a lot of inspiration from other frameworks that deal in the same space, mostly in the JavaScript land. Tools like Remix and Astro and, and I can't come up with any other ones. And like, so now you've opened up Elm to that space to things that are SEO performance as well.
[01:07:49]
So how does Elm pages compared to those now? And also, like, are there any parts of the fact that it's written in Elm that makes it much nicer or much more performance secure? Like, why would someone who is familiar with both JavaScript and Elm choose Elm pages over the other ones?
[01:08:11]
Right? Yeah. So Elm pages v3 is heavily inspired by Remix JS. So shout out to Remix and the Remix team for, I think, paving a path that is really exciting for sort of a web standards based approach to full stack front end frameworks, you know, with this idea of progressively enhancing forms and, you know, heavily relying on, you know, basically this idea of, you know,
[01:08:40]
basically this idea of let's, let's make a lot of our docs just linking to MDN and saying, well, here's how cookies work, which, you know, if you're, if you've been a front end developer for the last 5-10 years, maybe you've forgotten or don't know how cookies work, or, you know, maybe you've forgotten or don't know how, how form, how forms work. Like, because you, you know,
[01:09:06]
it's very lazy on their part.
[01:09:13]
But you don't actually use the built in platform features for these things. And they're saying, well, maybe let's enhance what the platform gives us, but use that as a starting point. So we have this baked in set of opinions that actually works really well with the browser, and we can leverage existing functionality there. So yeah, big shout out to Remix. One thing that obviously is different between, you know, Remix and Elm pages is Elm pages is Elm.
[01:09:42]
Oh, really?
[01:09:45]
Remix, they use a lot of TypeScript. And there are some cool features for bridging the types between the front end and the back end, using some of these TypeScript features. But of course, you know, Elm, you get more confidence in that you get custom types and all these things we like about Elm. So that's, you know, that's, that's one obvious thing that is a difference.
[01:10:09]
In addition to that, there's just a different philosophy between JavaScript and Elm. And that really shows up in these APIs. So for example, there's a lot of, I mean, if you're using Remix, one thing I noticed is you end up doing like a lot of casting. For example, you receive some form data, and you say, okay, well, I expect this to be a string, I expect this to be an int, I expect to be able to parse this, and I expect this to be a string.
[01:10:38]
And I expect to parse this as an int or whatever, right. But you're still, you know, you can use things like Zod, which definitely improves things and gives you more of an Elm decoder style approach to that. But you know, just like you would choose between Elm or JavaScript in any context, it's a similar set of trade offs with TypeScript and Elm there where, yeah, you can sort of get some of the safety of Elm if you use things like Zod and stuff.
[01:11:06]
But it's never like all the way there. And for, you know, for people like us, like, that's really important to us and would be a reason to use Elm in itself.
[01:11:17]
So when you communicate from the client to the back end, do you get a specific type or do you just get JSON, which you then need to encode and decode in Elm pages?
[01:11:28]
I mean, you get a specific type.
[01:11:30]
Oh, that's nice.
[01:11:31]
Yeah, yeah, yeah. So that's the whole, that's the whole game is you. So you define in your route module, you define a data function where you define a back end task.
[01:11:39]
If it's a server rendered route, then you have access to the HTTP request and you can respond with a back end task based on that incoming HTTP request.
[01:11:51]
And if it's pre-rendered, you don't have access to that HTTP request. And then that back end task resolves to your route modules data type.
[01:12:01]
So type alias data equals record with user name, ID, product, product name, whatever, whatever your route is showing.
[01:12:12]
You know, if it's a showing a product and it has inventory and it has whatever you get that, that that's your type alias data.
[01:12:21]
And then you just have that in you have that in a knit. You have that in view.
[01:12:25]
So you just do that. You have an app argument in your view function and you just do app dot data dot inventory count app dot data dot products name.
[01:12:36]
And it's not a it's not a maybe it's not a remote data. It's not a result. You just have have that data. It's just there.
[01:12:43]
Yeah. So that's the data that you get from the server when you're on the client. Right.
[01:12:48]
But if the client wants to send data to the back end.
[01:12:53]
Gotcha. OK. So, yes. And this is kind of a bit of a can of worms.
[01:12:57]
But, yes, Elm pages V3 has something called actions in server rendered route modules as well. And that is that's heavily inspired by Remix JS.
[01:13:08]
That name comes from Remix JS. I think they got the name from like a form has an action attribute in a web form.
[01:13:18]
So the action function is much like the data function. The data function is a back end task that resolves on initial page load.
[01:13:28]
That's your initial route data and the action. So the action is there for non get requests.
[01:13:36]
So if you make a non get request such as a form submission through a post method, then that would that would come into your action function on the server.
[01:13:48]
It's going to resolve your action function instead of your data function for that. And that will that will update your route.
[01:13:56]
So this is like one of the core things of Elm pages V3 is there's a whole form API for for working with this where you can define a form declaratively.
[01:14:10]
And also this is one of the big differences between Elm pages and Remix.
[01:14:15]
At least for me, this was like an important design space was like creating a very Elm way to sort of parse form data that gives you the real time validations and all these things.
[01:14:26]
So you just you know, with Elm pages V3, you declare your form, which has the parser and the view for it.
[01:14:33]
And then Elm pages takes care of of showing the real time client side validation errors. And when you hit submit, it will submit the form to your action function.
[01:14:46]
So you can in your action function, you can create a new item when somebody clicks submit on, you know, or you update the inventory.
[01:14:56]
And then you you hit the submit button. And now you receive that in your action, you get the parsed form, which is either successfully parsed or has errors, it parses into the same value.
[01:15:09]
So you can even access the value that it parsed into on the client side. And you can use that to do optimistic or pending UI, optimistic UI being assuming that it was successful and showing the UI as if it succeeded before it does while it's pending.
[01:15:25]
And pending UI being less optimistic version of that where you show a spinner that something is in progress or something.
[01:15:33]
So I have a demo app that people can try out a live version of or they can look at the source code of it's a modified version of Evans to do MVC Elm app.
[01:15:47]
But it adds database persistence using an NPM package called Prisma with with Postgres. And it directly manages the database request through Elm pages, custom back end tasks.
[01:16:02]
And, and it gives you so it gives you full database persistence uses magic link authentication.
[01:16:08]
So you so Elm pages itself will send you an email with a magic link that you click on. When you click on the link, it sets a session cookie that signs you in, because you click the link.
[01:16:21]
So it you've confirmed that it's you and now now you're signed in by clicking that link. That's what magic link authentication is.
[01:16:27]
So it that's implemented in Elm with a couple couple of very small bindings through custom back end tasks to do to use the crypto API to do encryption and decryption.
[01:16:38]
And, and then doing Prisma for the database persistence. And the rest is all in Elm pages directly and manages the cookies and all that gets the logged in users to do items.
[01:16:51]
And it shows some pending UI. So as you're so if you enter a new to do item, hit hit create or whatever the button says, you will see the new item show up in your to do list with a little loading spinner next to it.
[01:17:06]
And that's all done declaratively. So the same logic for parsing that form that is used to parse that that form action on the on the server to parse a an action, you know, of creating an item deleting an item editing an item, checking an item,
[01:17:27]
yeah, checking an item off checking all items off. Those are these actions uses the same form parsers on the back end to respond in the action to actually handle performing those tasks on the database.
[01:17:42]
But it also uses those same parsers to say what are the in flight actions that are happening. And it uses that to derive all of the pending state of all of the of your to do items. So if you delete something, you can instantly show it as deleted, because it knows there is an in flight form that is deleting this item.
[01:18:05]
So I'm just going to show it as deleted and and elm pages has this is one of the really big features of on pages v3 is it has all of the in flight form submissions are managed for you. So you just run your form parsers on it, but you don't have to wire anything up to your model.
[01:18:23]
So with no model or managing anything in your update, you just say, what are the currently submitting forms? Here's my form parser, what are the currently submitting form actions, and then you derive your pending UI from that state.
[01:18:38]
So I recommend taking a look at that to do example, the live demo and the code. It's like, I think it's one of the coolest features of on pages v3. Like it's it's what I'm the most excited about.
[01:18:48]
That does sound very, very good. Yeah, I'm very curious. I'm gonna look at it. When I can.
[01:18:53]
I love that declarative approach. You're not imperatively, you know, if they click Delete on this, then add it to this list of deleting things. Or it's just like, no, there's a list of all the actions. And you kind of just have that and say, Okay, well, if I have these pending actions, then what is my UI state, and you derive it from that.
[01:19:15]
So that's one less. That's one less thing that could go wrong. Like this. For me, this is like one of my big opinions about coding, I guess, is when things are more declarative, there are fewer things that you can get wrong. So on pages is very declarative. I hope I hope people will agree.
[01:19:33]
Kind of has right it is Elm. And that's the way we do things. Exactly, exactly. That's right. Maybe one last thing to mention is on pages v3 has built in V integration as well. So the dev server, the dev server still has the same hot data reloading that v2 had you you know, if you if you have a page that reads from a file, and you modify that file, it automatically knows that the page you're looking at is the one that's going to be in the page that you're looking at.
[01:20:02]
So it knows that the page you're looking at, dependent on the file you touched, and it will hot reload the the back end task for that for that route. In addition to that, so try out the dev server. It was a lot of work. I hope you enjoy it.
[01:20:16]
It's a girl. It has a built in integration with VJS. So now you can you can read about that more in the announcement blog post. But But yeah, you can define your own custom V configuration, and the dev server will use it and the production build will will use it as well. Yeah. So that's on pages v3.
[01:20:36]
Yeah, that's a lot of information. Yes. The docs are are big, because the API for the tool is big, because I mean, it's doing a lot of things in practice, like, like, oh, well, let's just add these primitives to read files. Oh, well, now we can render things to
[01:20:57]
now we can make API's now we can do a lot of things. Oh, now we need cookies. Oh, now we need sessions. Now we need forms. Now we are like, it's a lot. I know it's been a very big journey for you. Like, yeah, you've been working on this for like a year and a half. Yeah. So I'm really glad that you can now focus on something else. Even though I know you're still gonna work on the own pages like that's that's you.
[01:21:26]
Right? Yeah, definitely. I hope people like it. I hope people also like the all the cool stuff you you you made possible with this, including just the tiny thing you mentioned at some point saying like, oh, we can do back end. Right? Yeah, I hope people enjoy it. Working people will go if they want to learn more like you have an announcement blog post, we will add that to the show notes elm dash pages.com I'm guessing.
[01:21:55]
That's exactly right. Elm dash pages.com. We've got a doc site there and a and a blog. I might might write another blog post or two in the coming weeks and join the Elm pages slack channel. I'm very active there. Happy to answer questions. Check out the discussions in the Elm pages GitHub. Yeah, like let let me know what you build with it. Let me know what questions you have. Let me know what you want to see.
[01:22:24]
Let me know what docs you think could be improved. I would love to hear from people and see what they end up building with it. All right. And you're in until next time. Until next time.
[01:22:54]
Transcribed by https://otter.ai