Getting Started with Deno and Deno Deploy

Rate this content
Bookmark

Learn how to build full stack apps with Deno and Deno Deploy. We'll build a simple app using the fresh (fresh.deno.dev) framework, and host it on Deno Deploy.

158 min
04 Jul, 2022

AI Generated Video Summary

Today's workshop is about building a full stack application using Deno and Deno deploy. We will create a blog with personalized blog entry pages and explore Deno's features such as TypeScript support and integrated tool chain. Deno deploy allows for easy deployment of Deno applications, and Fresh is a full stack web framework optimized for fast and scalable applications. The workshop covers topics such as setting up Deno, creating projects with Fresh, handling static pages and blog posts, testing, client interactivity, and deployment using Deno Deploy and GitHub Actions.

1. Introduction to Full Stack Development with Deno

Short description:

Today's workshop is about building a full stack application using Deno and Deno deploy. We will create a blog with an index page and personalized blog entry pages. Users can adjust their preferences, and the blog articles will be managed as files in a git repository.

Okay, cool. So, the talk or the workshop I'm going to be giving today is full stack with Deno and Deno deploy. And what that means is we're going to be building an application, full stack application, using Deno, deploying it to Deno deploy and checking out all the cool features that Deno gives us and how it makes it really easy to do that.

So what are we specifically building? We're going to be building a blog. This blog is going to have an index page, which lists all the blog entries. It's going to have a page for each of the blog entries. We're going to be using some dynamic server side rendering to personalize the page for each user. Specifically, we're going to render the dates on the page users prefer locale. So if we have a user coming from Italy, for example, it'll render the dates in the Italian style, if they're from the UK, it'll render in English and the UK style. In the US, it'll put the month first, stuff like that. We're also going to have a page to let the user adjust these preferences. So that's going to be like some interactivity that's going to happen there. I'll show you this in action in just a second.

We're not going to actually have an editor for the blog articles or anything like that. Blog articles are going to be marked down as files that are checked into the git repository. Like many of you, if you have a blog, that's probably how you manage your blog articles. So let me quickly demonstrate this. This is what we're going to be building today. So here we have this blog where you can see all the blog articles. If you click on them, you go to the blog page.

2. Building an Interactive Application with Deno

Short description:

We will be building an application with interactivity where users can adjust settings such as language preferences. The application will display information based on the selected language. We will be using Deno, a modern JavaScript runtime, to build the application.

You have your date, your title, content of the blog. You can have multiple articles. This is all like nice markdown rendering. can see here that this is rendered in the UK format. Date, month, year.

Same here. This is in English. If I go to my chrome settings, language, and I set my language to U.S., it'll switch these around here. If I set it to German, for example, it'll use a completely different formatting and it'll start speaking to German in here.

We also have the settings page where you can see what you have selected. So currently we're using the one that the browser sent us, which is German. I can force it to be Italian, for example. You can see while I'm typing here, it'll tell me some information. So this is some interactivity that we're going to be building into our application. So I can type in Japan, for example, it'll give me, it'll tell me what the name of the language is, and what a sample of time in that language would look like. If I hit save, it persists these settings. And if I go look at the date now, it'll say this in Japanese.

Okay, so that's what we're going to be building. How are we going to be building it. So we're going to be using Deno for those who aren't familiar with Deno, the like five second thing is it's a modern JavaScript, JavaScript runtime.

3. Introduction to Deno and Deno Deploy

Short description:

Deno is a runtime similar to Node.js but with TypeScript support and a fully integrated tool chain. It uses modern JavaScript features like promises and sticks to Web APIs. Deno also has a second product called Deno deploy, which is an edge runtime for Deno applications. DenoDeploy powers various services and we will be using Fresh, a full stack web framework optimized for fast and scalable applications.

So like Node.js from the same people, except it supports TypeScript, which just isn't from the same people that had originally built Node.js, so Ryan Dalbert builder, those folks. And it contains a fully integrated tool chain, which is different to Node, it has built in formatter, built in linter, built in testing framework. And it uses a lot more modern JavaScript features like promises everywhere. And it tries to stick to Web APIs where possible.

So for example, there's no custom HTTP server or HTTP client. It is using fetch like a browser would. I work on Deno full-time. I am one of the engineers working on it. We have a team of, I think, 15 folks working on Deno right now, working at the Deno company, building Deno. But the Deno company also has a second product, which is Deno deploy, which is an edge runtime for Deno applications.

So for those who aren't familiar with what that means, An edge runtime is, I don't know if you're familiar with Cloud for Workers, a way to run some code all over the world, really close to users and in a bunch of different locations. So whenever a user makes a request and you need to do servers at rendering or anything like that, it happens really close to the user, which minimizes the latency required. For example, if you have a user in Germany making a request to your service, that could be served by a server in Germany, rather than having to travel under the ocean to a server in the US somewhere. DenoDeploy powers a bunch of services. For example, deno.land, our website, all of our web properties, actually. It powers Netlify's edge function offering, Superbase's edge functions as well. There's a bunch more, you can find them on our website. And lastly, we're going to be using Fresh, which is a full stack web framework for Deno that uses Preact, is optimized for fast, robust and scalable applications. It has no build step, and ships no JavaScript for the client by default. So Fresh is, you can think of it like nextJS or Remix but built for Deno and trying to really make use of all of the features of Deno, like all the tool chain features as best as you can.

4. Introduction to Build Step and Workshop Logistics

Short description:

We will be discussing the build step and providing a demo. The source code is available in the repository LucaCastinato/Fresh Workshop. Each commit in the commit log represents a step in the workshop. If you have any questions or get stuck, feel free to ask and refer to the commits. You can re-watch the workshop at your own pace later.

Part of that is having a build step. Yeah, so this is what it's going to look like. I gave you a demo of that a second ago. You can check it out here again if you want to.

Now about following along at home. Please do. The source code we're working towards is available at this repository here, LucaCastinato slash Fresh Workshop. You can see here in the commit log, there's a bunch of commits here. Each of these is like one little step that we're going to be taking in the workshop today. If you ever get stuck, you can just checkout at any one of these commits and continue working from there. For example, let's say we're currently working on this and you just cannot get it to work. You can checkout this commit and then continue on from there rather than being stuck for the next two hours, for example.

Another thing is if you have any questions, if you see anything that's unclear, please unmute yourself and shout it out into the room. Like, interrupt me and I'll try to explain it in real time. I think that's much easier than if you ask them in Discord or in chat. Then I don't see them and it takes a while for me to answer. And don't forget that you can always re-watch the workshop at your own rate later. If there's anything that's that you miss, it's going too fast for you feel free to just drop out with following along by writing source code. And instead just watch and listen and you can always try this later in your own time. And then let's head to the prerequisites.

5. Setting Up Deno and Creating a New Project

Short description:

To get started, make sure you have Deno installed and the Deno integration for your editor set up. If you want to deploy your code, you'll need a GitHub account and repo. Now, let's create a new Deno project. There's no boilerplate, just a settings JSON file. Run 'deno run -A' and see how it works without a package JSON file or node modules folder.

So you need to have Deno installed. I had put this on the JS Nation website earlier. So I hope you have Deno installed. And have the Deno integration for your editor set up. There's a manual page for how to do this. It's also linked on the JS Nation website. And if you want to deploy your code to your deploy, this is not mandatory. If you don't, if you just want to play around with it locally, that's totally cool. But if you wanted to pull your code, you need to have a GitHub account and a GitHub repo to be able to push your code too. So yeah, that's important.

Okay, so that's all of the like boilerplate out of the way, I think we can get started with coding. So first thing first is need to create a new project. So I have a Git repo here, which has nothing in it. It only has this settings dot JSON file in it, which set which tells VS Code that this is a Deeno workspace, and it should use the Deeno extension. Nothing else in here. And I'm just going to create a new Deeno project. And you will see that there's really nothing to this. So let me just log out hello world. And then Deeno run dash A, you know, PS. And you can see this Just runs there's Deeno has no package JSON file or node modules folder or anything like that.

6. Creating a Project with Fresh

Short description:

To create a project with fresh, use the init script on fresh.deno.dev. Answer a few questions and it will generate a basic folder structure. Run 'denotask start' to start the project and access it on localhost 8000. The main OTS file is the entry point of the project.

You just create your file, start typing code in it. So that's how you create a project. But fresh, fresh is like an opinionated framework, which gives you a nice set of defaults to work from. And for that to work best, it gives you a little init script.

Let me go to the website, a little init script that you can run that scaffolds out your project. So what you can do is you can go to fresh.deno.dev, copy this command here, and let's remove this file. And then it'll just initialize a project for you. And it might ask you a few questions, just answer yes to all of them. And then you'll see it generates this folder structure. And this folder structure is the most basic fresh app you can have.

And I'll walk you through what all these files mean in just a moment. But first thing, let's what we'll do is run denotask start to start the project. Oh, I apparently already have a server running. No, this is my reference project. Okay, let's do that again. And now it starts up the server. And then I can listen, I can go to localhost 8000 and see this page that is being hosted from my freshly initialized project.

So let's go through all of the files here. First thing is this main OTS file, this is the entry point to your project. I'm not going to explain all of the details of everything that is happening here because I don't think it's super interesting.

7. Exploring File Structure and Components

Short description:

If you want to learn more about this, you can go to the fresh documentation and run through this getting started tutorial later. The important thing is this routes folder, which contains one file per route of your application. The index.tsx file is what is rendered when you go to localhost 8000 without any path. You can edit things in here and see the automatic updates. This project uses preact with JSX and Tailwind for styling.

If you want to learn more about this, you can go to the fresh documentation and run through this getting started tutorial later. And part of this is it will tell you what all these files do and what is in them. I'll just give you the important details here.

The important thing, this is all boilerplate. You don't really need to care about it, is this routes folder, which might look very familiar if you've used Next.js before. But it is essentially one file per route of your application, so there's this index.tsx file. Sorry, let me, Okay, there we go, get rid of all the errors. Yeah, so this index.tsx file, this is what is rendered when you go to localhost 8000 without any path. So the index page, you can edit things in here. Let's edit this and you will see that it refreshes automatically and updates and I have a counter here as well. I'll show you what that is in just a second as well. But yeah, I can change things here and it'll update on the page automatically.

I have this actually I'm not gonna explain what these two are. We will just delete them and get to them later. And the islands, we're also gonna get to those later. So what we're gonna do for now is just get rid of everything that's in here and put in a random Hello World. Okay, so this project is using a preact with JSX. So these are JSX components which is like a templating language for JavaScript if you've used react before, you'll know what this is. And we're using Tailwind for styling. And if you've never used Tailwind before, let's, tailwind-css.

8. CSS Utility Framework for Styling

Short description:

The CSS utility framework allows you to style through CSS classes without writing CSS. It generates only the necessary CSS for the page, resulting in small site sizes.

It is a CSS utility framework which allows you to do styling through purely CSS classes without having to write any CSS itself. And the way this works is through this template that will here that you put on your JSX classes. It will automatically generate just the CSS that is necessary to render this page. So if I look at the page source, you can see that, so all of this is just a CSS reset, you don't need to care about any of this. But you can see there is three CSS classes here. And it has generated just these three classes, so MXAuto, P4, and Max with ScreenMD. So this is really, really size efficient. It allows you to make super small sites.

9. Exploring Static Page and Adding Blog Posts

Short description:

No JavaScript is loaded on this purely static page, which eliminates the need for unnecessary client-side JavaScript. Unlike Next.js, which sends a 70KB JavaScript bundle to the browser for even a simple Hello World page, this workshop will demonstrate that little to no JavaScript is required for a great user experience. Next, we'll add blog posts to the project, including front matter and content written in markdown.

One other thing that you can see is if I open the Web Inspector, go to the network panel, and hit refresh, is that no JavaScript is loaded. Actually there's, not quite true, a little bit of JavaScript is loaded just to make the hot refresh work, but there's no application JavaScript loaded. This is a purely static page, because there's no interactivity here, right? Because there's no interactivity, we don't need to send any JavaScript to the client, so we don't slow down the client by giving a JavaScript that doesn't need, and this is different to if you're using next JS for example.

It will send the entire client renderer to the browser by default always, so you'll get a 70 kilobyte JavaScript bundle that's sent to the browser, even for a Hello World page like this without any interactivity. And you'll see during the course of this workshop that you really don't need much JavaScript on the client, you can have a really great experience with little to no JavaScript.

Okay, so this is our scaffold. Let me commit this scaffold and push it to my repo and go back to my slides to see what we're doing next. So first thing we want to do is add some blog posts that are actually going to be shown on that we actually want to show later. So I'll create a data folder and a post folder and create some blog posts. So the first one is going to be hello, it's going to have some front matter at the top to give it things like the title, the publish date. Publish, sorry, publish at, and this needs to be a ISO date. I sort of going to say it was published right now, and we're going to need to snip it as well. So this is what is shown in the meta description. So what Google would show, for example, if you search for this page, or what is shown on the index page, as the summary of the blog post. So, this is the first post. And now after this, I can just have, sorry, let me disable Codepilot actually, this is going to be really annoying otherwise. Yeah, and then this is just regular markdown, so you can have your content here. So this is my first blog post on my new blog, and this is markdown syntax here to make things italic. And you can actually see what, I don't know if you saw that, if you caught that. But if I press Ctrl S, this saves and formats things automatically.

10. Using Deno FMT and Adding Blog Posts

Short description:

Deno has a built-in formatter called Deno FMT that can format files in the directory. It supports markdown, JavaScript, TypeScript, JSON, and more. Let's add a second blog post with subtitles and a table. We will also define a TypeScript type for our blog posts.

And this formatter that it's using is actually you know this formatter. So Deno has a built in formatter. It can run Deno FMT and it will format all the files in my directory. So I can do this for example. Right. Yeah, that's like some weird markdown syntax with two tabs. But yeah, it will format all of the files in my directory. This is prettier, put markdown JavaScript, TypeScript, Jason and a couple others.

Okay, so first post, let's add a second post. Just so we have a little bit more variety. Second post. Let's have this one be published tomorrow. And let's have for this one add some subtitles. And maybe let's add a table. So this is markdown table. And you'll see why I'm doing this in just a minute. Okay, so there's our table. We have our two blog posts. One other thing we're going to do is we're going to define a TypeScript type, which represents each of our blog posts. We're going to be using this in a couple of places later.

11. Creating TypeScript Type for Blog Posts

Short description:

This TypeScript type will have an ID that corresponds to the URL bar. Each blog post will have a unique ID, title, publish date, snippet, and content. The content will be the markdown content after the front matter.

It's easier if we do it upfront. So this TypeScript type is going to have the ID. I thought I just able to co-compile it. Okay. Oh. AI, terrible. So yeah, it has an ID. The ID is the thing that you'll see in the URL bar. So for this hello blog posts, it's going to be, you go to slash blog slash hello. So the ID is going to be hello for that one. For the second one it's going to be, second is the ID. As a title is the string that, is it a string up here in the front matter? Then we have our publish at date, which is just a JavaScript date object. Then what else do we have? We have the snippet, which is a string and we have the content, which is also a string. And the content is just like everything after the front matter. So the markdown content itself.

12. Adding Page for Individual Blog Posts

Short description:

Let's add a page for showing individual blog posts. We'll create a file called ID.tsx in the blog folder. The square brackets in the file name indicate a dynamic variable that can match any value. We'll start by hard coding the blog post data and handle loading later. We should import the post type.

Okay, so we have our posts. We have a type for our post. Next thing, let's add a page for showing the individual blog posts. And let me commit this. So what we're going to do is we're going to add a blog folder and in there we're going to add a file called ID.ts with square brackets around it. And what these, oh sorry.tsx actually. So what these square brackets mean is that that means this is a dynamic variable. So this will not just match blog slash ID as a path but it will match blog slash anything really. So let me just copy this in here so we have something to look at. And then you'll see if I type in slash blog this doesn't match anything because there is no blog slash index.tsx. But a blog slash hello adds to something. Blog slash hello to. Blog slash second this all works because this is a dynamic parameter. For now let's just hard code the blog post data. We'll get to loading in a second but let's take things one step at a time. So for now we're just gonna hard code things. Copilot manage to enable it itself again. This is, I'm just gonna uninstall the extension, bye. Okay there we go. All right so we should probably import this post type.

13. Adding Date and Title to Post

Short description:

We'll have a published update with the current date and some content. This is the first post. We'll start by adding the date and styling it. Then we'll add the title and apply styling to it as well.

So we'll have a published update. Let's just do new date so that is right now. We need some content. This is all just placeholder for creating the actual page right now. This is my first post.

Okay so we have our post data here that is currently static. We're going to make that dynamic in a bit but let's start with just adding the page. So let's go back to what this is meant to look like. So we have the date, then we have a title, then we have the blog content.

Let's start with the date. Let's do post dot publish at and what is it? Call date string. So this formats it into a string. Let's give it some styling. Let's make this text gray 600. So make it not be black but make it slightly brighter gray to make it stand out less. Then let's add the title of the post. Let's style this one as well. So this one's going to be a text, I think five excel and let's do a, oh that is the wrong one. Okay yeah that looks cool. Let's add a margin top to this one as well.

14. Adding Content and Dynamic Rendering

Short description:

Let's add the content without markdown rendering for now. We'll keep it simple and add top margin. Our post data is hard-coded, so let's change that. We need a loader function for asynchronous data loading.

Let's do 12 maybe. That looks good and let's make this bold as well. Cool, okay that looks pretty close. Yeah.

Then next we're gonna add the content. We're not gonna do with markdown rendering right now we're gonna get to that later. Let's try to keep it as simple as possible for now. And let's add some top margin to this one as well. So let's do eight top margin. Ah actually let's do 12 as well. Cool, okay.

So we have our blog post rendering, pretty simple. Right now our post data is still hard-coded though. Let's change that. So, it's rendering of blog. Okay, so how do we do dynamic rendering? So as you can see this function here which is actually called wrong. Let's just rename it, is synchronous. And data loading is usually asynchronous. So to do asynchronous loading, we need some sort of loader function that actually lets us, yeah, load some data asynchronous. We can't just make this function asynchronous.

15. Creating Utilities and Loading Text from Disk

Short description:

We need to create utilities for turning the ID into the blog post struct. The load post function takes the ID as a parameter and returns a promise of post or null. We'll then load the text from disk using Deno's readTextFile function.

That doesn't work. The page, like everything's gonna completely freak out. You can see, it just turns white there. So we have to have some sort of loader hook somewhere. We're gonna get to that in just a moment.

Before we do that, we need to figure out, let's create some utilities for actually turning the ID. So this thing up here into this blog post struct. So let's start by creating a new function, async function. And yeah, so yeah, exactly, load post. So this is gonna take the ID as a parameter and it's going to return a promise of post or null. So this is going to return the post structure or if the ID was not found, it'll return null because it's obviously possible that you don't find a blog post to render.

Yeah, actually before I'm gonna do that, are there any questions right now? Does anyone have questions so far? I'm sort of racing ahead, I'm very sorry. Is this pretty great? Okay, cool. So that's it, thank you. Okay, so then what we're gonna do is just continue on here. So, first thing we're gonna do is load the text from disk. We're gonna do this in a try-catch. I'll explain why in just a moment. So, what we're gonna do is we're gonna say text is equal to await deno.readTextFile. This is the Deno function for reading, as it says, a text file from disk.

16. Loading File Content and Parsing Front Matter

Short description:

We load the file content and check if it exists. If not, we return null. Next, we parse the front matter using the Denoland STD library, which provides utility functions for handling various tasks. The library is maintained by the Deno Core team and follows the same standards as the Deno Project itself. We import the FrontMatter parser from the library and use it to extract the front matter from the file. If a version is not cached, we can cache it by clicking on Cache in the editor.

It takes as the first argument, the path, where you wanna load from, in our case, it's gonna be dataPostsId.empty. And what we're gonna do is, if this throws an error, and if the error is an instance of the Deno errors not found, then we return null. So, this just means if we're loading a post and the post doesn't exist, we'll return null. And in all other case, we'll rethrow the error, and then we can handle that later.

So, at this point, we have the content of this file, and if you remember, it starts out with a bit of front matter, and then it contains the contents. The first thing we're gonna need to do is parse out the front matter from the top of this file. And to do that, we're going to use a front matter parsing library called, actually, it doesn't have a name actually, because it's part of Denoland STD. What is Denoland STD? It is the Denoland Standard Library. So, the Standard Library is a bunch of utility functions, little, yeah, like UID, for example, like UID generation, dealing with streams, some more advanced file system operations,.env. This front matter parsing is one of them. It's encoding front matter. So, this is just a TypeScript library, essentially, that is maintained by the Deno Core team next to the Deno Project itself. And, yeah, it gives you a bunch of utilities, which you would usually have to import some third party libraries from in other ecosystems. And this is like audited and tested by the Deno Team to the same standards as Deno Project itself.

So, what we're gonna do is we're going to import the FrontMatter parser from the FrontMatter library here. So I extract, import extract from FrontMatter. Well, actually, so I'm kind of cheating because I've done this earlier this morning, and so I already have everything cached. Usually the way this would look, let me actually just showcase what that would look like. So if I import a version which I don't have cached, so I don't know, the 1.44 release, it'll be in red and it'll say that this is an uncached or missing remote URL. What I can do is I can press Control-dot and then click on Cache and it'll automatically cache this, or it'll just pull it down automatically while I'm running.

17. Dependency Management and Parsing Front Matter

Short description:

Deno imports dependencies from remote modules using URLs. Deno supports import maps, which allow remapping specifiers to URLs. This allows customization of module resolution. The front matter module is imported, and the extract function is used to extract attributes and body from the text. The attribute type is currently unknown, and input validation is out of scope for this workshop. The parameters include title, publish at, and snippet, with the publish at being parsed as a date.

So this is like the way it works in a browser. The first time it encounters a dependency that it hasn't downloaded yet, it'll go download it, and then it'll cache it forever in the cache directory. For Deno, this is called the DenoInfo, or sorry, the denoDir, which is like the Deno cache directory. If you type DenoInfo, you'll find the location of this cache directory, and you can see all of the dependencies that are downloaded are stored in here forever. It's not meant to be navigated by users, which is why these are all terrible names.

So one thing you'll see is that Deno imports dependencies from remote modules, so from URLs, just like the browser does. This is different to Node. In Node, you import modules from the Node modules directory, and you put stuff into the Node modules directory using NPM, and pulling it from the NPM registry, and you have to use a package JSON to do that. In Deno, you just encode the dependencies directly into your source code, but sometimes this is not super useful. Sometimes you want to import the same module in a bunch of different places, for example, and if you have to retype this dependent of this whole long specifier every time, that can get quite annoying.

So Deno has support for a browser standard called import maps, which allow you to remap specifiers like bare specifiers, so things like, I don't know, Preact, or this, Tailwind, to URLs, and browser support, though Chrome supports this, it's being implemented to Firefox right now, and this essentially allows you to customize how the system does module resolution. So what we can do, we can add a new rule here that says if something starts with dollar sign STD slash, then replace that with this HTTPS, Deno land slash STD version 145. And now I can do this instead in my imports, and this is much cleaner now if I need to change the version there's just a single place where I change the version, rather than having to go through all my files and change it in a bunch of different places.

So, with all that said, I'm now importing this front matter module, and I have this extract function that I'm importing from there, and I can call this extract function with a text and it returns a object with attributes and body and there's attributes, this is the front matter itself. Pars is YAML, and the body is everything after the front matter, so the content. What we are going to do is we will, so this attribute currently has an unknown type, let's cast it to a different type. We're not gonna do an input validation right now because that's out of scope for this workshop, but if you were to want to do import validation here, you could check, is this actually an object? Does it only have string keys, and does it only have string values? Did a user put an array into the front matter instead of an object, for example? But yeah, we're not gonna do that right now. The parameters, this is going to have, the title, sorry, let me open one of these posts so you can see what it actually looks like. This is going to have the title, the publish at, and the snippet. So let's, the first thing we're gonna do is parse this publish at as a date, because right now it is just a string, we want it to be a date.

18. Loading and Parsing Blog Post Data

Short description:

We'll parse the publishAt date and return an object with the ID, title, publishAt, snippet, and content. The loadPost function loads a post by its ID and parses the data. We can test it using the Deno VDbub Print loop. Adding tests ensures the function continues working in the future.

So we'll do params.publishAt, and pass that to the new DateConstructor, and that'll just parse it. And then for the rest we'll return an object which has the ID, which is just the thing that's passed into this function. The title is params.title, the publishAt is the date, the snippet is also passed in by the params, snippet, and the content is the body.

Okay, so now we have this loadPost function which loads a post by its ID and parses out all the data. So let's see if this works. We can do this using just the Deno VDbub Print loop, so without integrating this into the application right now. So what I'll do is I will import, I'll startup Deno, and then let me copy the relative path here, and then do import loadPost from Deno. dot slash utils post.ts, and that will import this file. And now if I call loadPost, I need to specify the ID so that's hello. You will see that this loads the file correctly. It has the date, it has the publishAt, the snippet, the content. I can also do this with loadPost second. And you see this works just as well. Here's the unprinted markdown.

Okay, cool, so this function works. We should probably make sure that it continues working into the future, though. Let's get this real quick, add the loadPost. So how do you make sure something continues working into the future? Well, you add a test. And usually people skip writing tests because it's like too complicated to set up or it's like too much maintenance to keep them running. Deno tries to make this so super simple that like you have no excuses for not writing tests.

19. Writing Tests for Deno Applications

Short description:

To create a new test, create a file with the appropriate extension and call deno.test. Import assertions from the standard library to check if the post is not null and if the post ID is correct. Running the tests will show if they pass or fail. Writing tests is easy and important.

So to create a new test, the only thing you do is you create a file and you end it with underscore test.ts or.js or.tsx or.jsx or any number of other supported file extensions for Deno. And what you then do is you call deno.test in here, you give it a name. So we're gonna be testing load post, we're gonna give it a function to test. So this is the function we wanted to test, and we're just gonna call await load, oh, sorry, what is it called load post. And this will, so far only test that it's actually voting something which is not there.

I can just press this little green button here and it integrates nicely into VS code. It'll like run the test. I can also run dino test dash A, and it'll run the test this way. But I probably also wanna check that this post is like actually not null. So let me import some assertions. We can do this from the standard library against std, testing, asserts.ts, and let's do a assert and assert equals, so let's assert that post does not null and assert equals that post.id is hello. I run this test again, it still passes. Let's make the test fail just to see that it's actually running the test. So it fails now because it does not equal hello too. It equals just hello.

And yeah, you could have a second test here which tests for load post non-existent where I load a post that does not exist. This post does not exist, and now it's going to assert that post is equal to null. And I can run this one as well and it also passes, and I can run them from the command line and they both pass. Okay, so that's how to write tests. And I hope this shows that it's so easy to write tests that you'd have no excuses to not write tests, and yeah, if you still think there are excuses, then please reach out to me and we will make sure that the excuses will be gone in the next release because writing tests is important, people.

20. Using Libraries for Testing in Deno

Short description:

Is it possible to use libraries like Jest to write tests in Deno? While Jest specifically cannot be used, Deno provides a similar syntax through its behavior-driven tests (BDD) in the STD library. You can use describe and it statements to structure your tests, and it integrates seamlessly with DenoTest. Additionally, if you prefer the expect syntax, you can import expect from the try module. However, the compatibility of the try syntax with Deno is uncertain.

Okay, so. Luca, I have a question. Is it possible to use libraries like Jest to write tests or is it the only option? Yeah, so you can't use Jest specifically, but you probably don't care specifically to use Jest, but you probably want to have the syntax like Jest, right, where you have the describe and its and stuff like that, and that is possible. So there is a, in the STD library, there is something called BDD, which is behavior driven tests, and this essentially looks like Jest's test system. So if I wanted to rewrite my test using this, I could totally do that. Let me show you how that would work. Describe it, so I could describe load posts, and I could, it loads successfully, and this, da da da, and I can do that. And this just integrates right into DenoTest. So if I do DenoTest A, it'll still run this test, and it'll like, show it correctly here. So this is possible, and the other thing is that, you might not want to use assertions like this, but you're maybe more familiar with expect. So that would be, I think try is the one with expect, right? Is it? Yeah, it is. So you can, like import try, as well. And then you can do, let's see here. Import expect. So we can do like, expect post, let's get the types for this. Expect post hello. Okay, well, expose. I don't know that try syntax works. But to be no, I don't know. Maybe.

21. Snapshot Testing and Dynamically Loading Post Data

Short description:

Snapshot testing is a recently added feature with its own framework. Other features include running tests in parallel and locking down permissions. Now, let's focus on dynamically loading post data on the blog post page.

Does that work? It's okay, yeah, I get it, thanks. Yeah, it's possible. Okay. Yeah, so.

Okay, any other questions on testing? Or can we move on? So actually snapshot testing is something we added really recently. It is supported now. I've never used it myself, but there's this snapshot testing framework. Yeah, there's some documentation on it somewhere. But I'm not the right person to use it. But it does exist now, which is cool. And there's some other features which I haven't demonstrated. Like you can run tests in parallel with the jobs flag. And yes, you can lock down, or yeah, I haven't covered permissions at all, but if you know what those are, you can lock down permissions for each test as well. The testing framework is pretty advanced, but it has a really nice simple interface to get started with.

Okay, cool. So moving on from testing, right testing there already. Let's dynamically load the post data on our blog post page. So right now we have the posts hard-coded here. Instead of hard-coding them, let's get them dynamically. So to do this, we're gonna need, as I alluded to earlier, some sort of loader function because this cannot be asynchronous.

22. Using Handlers in Fresh

Short description:

Fresh supports handlers, which allow you to intercept the request and response handling completely. By default, Fresh renders the default exported component for each route. However, for API routes or specific requests, you can use handlers to handle different types of requests or parse JSON from the request body.

So we need to do the asynchronous work sometime upfront before actually doing the rendering. And to do this Fresh supports something called handlers. And handlers are, they, yeah, how do I explain handlers best? Like handlers are essentially a way for you to intercept the request and response handling completely. So by default, if I don't specify any handlers, Fresh for each route will just render the default exported component. But API routes, for example, may not have a defaulted exporter component, right. They may want to handle like post requests there instead of get requests, or you may want to handle, I don't know, get requests you want to handle with rendering the page. But a post request, you want to like parse the JSON out of the request body or something like that. So to do that you use handlers.

23. Creating a Handler for Rendering and Loading Posts

Short description:

We'll create a handler for the 'get' request that takes a request and context as parameters and returns a response. We can intercept rendering and return a static response, but we actually want to render the page. To do this, we call context.render, which renders the component and returns a response. To load the right post, we get the ID from the URL using the parameters field in the context.

So let's export const handler. And let's import the types for this because I get really lost if my types don't complete properly. So fresh server.ts. And we're going to import the handlers. Type, and we can now create a handler for get. This can be asynchronous. It is going to take a request and a context. And it returns a response. So this is just the same response that you get from fetch, for example.

And what you can actually do here is, let's say you don't want to render this page at all, let's not render the page and instead just return ASD ASD for all of our pages. It's kind of stupid, but whatever, it's possible. So I've now completely intercepted rendering and instead of rendering, I'm now returning this static response here. Not quite what we want to do. We actually want to render the page. So we're going to call context.render, which renders out the component and this returns a response. So yeah, this doesn't work right now because post is not defined, we'll get there in a second.

Then, to be able to load the right component or load the right post, we need to get the ID out of the URL. We can also get this from the context. The context has this parameters field, which contains all of the URL parameters.

24. Loading Post Data and Ensuring Type Safety

Short description:

The context has a parameters field containing all the URL parameters. We can log out the ID for now. By using the utility function, we can load the post and pass it to the render function. The props are of type page props, and the data field is set to the argument passed into the render function. We need to ensure the correct type is passed and returned for type safety.

The context has this parameters field, which contains all of the URL parameters. In this case, there will be an ID parameter, which we can, for the time being, maybe we'll just log out the ID. And if I scroll up to above where the error is, I'm currently loading the second page and it prints out second. So this is definitely there.

I can now load my post with the utility function we wrote just a minute ago, pass in the ID and we now have a post. And I can pass this post to my render function. And what this is gonna do is it's going to pass all the arguments that I passed my render function to the data of my render function into my component here, via my component properties. So yeah, the props here are type page props. And page props has this field called data. And this is set to whatever you pass into the render function. So if you pass the post into the render function, it'll return, that'll be set on the data. You can see right now, it says data as any. And you can also see here that render is, that the data is any here as well. Right here. This is not very nice for type safety.

So what we're gonna do is we can pass. We can tell the handler and the page props that we want to ensure that the right type is passed in to the render function and its right type is returned from the data here. You will see that this immediately errors out because post is of type post or null. And render has to be of type post or undefined. The undefined, yeah, that's part of how FRESH works.

25. Handling Missing Posts and Listing Blog Posts

Short description:

To handle the case where there is no post, we return a 'post not found' response. The rendered pages are server-side rendered, so having more code in the render functions or handlers doesn't affect performance. Next, we need to list all the blog posts on the homepage. To do this, we'll create a function called 'list posts' that loads all the posts, retrieves their metadata, and sorts them by date.

You don't have to care about that right now. But yeah, the important part is that this null cannot fit in there because null is neither undefined or it's the post. So what we're gonna do is we're gonna say if there's no post, we're gonna just return a new response page not found, status 404 or actually post not found. And if we can now go to our page, you will see this actually works. So if I go to my hello blog posts, it'll show the hello data. If I go to my second blog post, it shows the second post data. If I go to a page, which doesn't exist, it'll show post not found.

You probably won't have a nicer error page than this, you can do this by, for example, passing a argument to your render function, which is like error true or something like that, instead of your posts. And then you can do, I don't know, if error is true, render out a different page. And because this is all server-side rendered, it doesn't, like, it's not bad for your performance or anything like that to have more code in your render functions or handlers. But, yeah, are there any questions regarding handlers or passing data to the render function, anything like that? Nope, okay, cool. Well, that's nice to hear.

So yeah, next thing we need to do is if you go to the homepage right now, the GIMP's page, you'll see it still just says hello world, there's no listing of blog posts here yet. If I go back to the reference, you'll see there's this nice list of all the blog posts sorted by the date when they were published. So to do that, first thing we're gonna need is we're gonna need another load function here that actually loads all of the posts. So let's create another function, export async function, list posts. This is not gonna take any arguments because this returns all posts and it's gonna return a promise of post array. So it's gonna return an array of posts. So how do you actually, so what we wanna do is we wanna list out all of the files inside of the post directory. Then we want to open those files to get out all the metadata and then sort it by the date timestamp because we want the oldest one to be last, like the newest, we want the newest post at the top.

26. Listing Files and Extracting ID

Short description:

We want to list all the files in the directory and get the ID from the name by removing the '.md' extension. We can use deno.readdir to iterate through the directory entries and console log them. Then, we add a test to verify the functionality. The output shows that both entries, 'hello.md' and 'second.md', were found. To extract the ID from the name, we use entry.name.slice(-3) to remove the last three characters, which is the '.md' extension.

And you can see here that's not necessarily the order they are in the directory. So we wanna do that and yeah, that's pretty much, yeah. So that's what we wanna do.

So first thing we wanna do is open all of, or list all the files in the directory. We can do that with a for-await loop entry of deno.readdir. So deno.readdir is a function that returns an async iterator or an async iterable actually of all of the directory entries in a folder. So we're gonna go through the posts folder and for each of those entries, we are going to, let's start by just console logging out the entries.

And for simplicity sake, let's immediately add a test for this. So it will be easy to show what's going on. So list, posts, async, posts. So we're gonna do await list posts and then let's just console log out to posts. And if I, okay, can I please see the output? Where is it? Here, yeah, okay. So here's the output. So you can see that each one of those entries, it found both of the entries, the hello dot mark down, the second dot markdown. So that's working. So we want to get the ID from the name now. So the name contains the dot MD extension. The ID does not. And so we need to get rid of the MD extension. Easiest way of doing that is to just say the ID is the entry dot name, and then sorry, entry.name.slice off the last three characters.

27. Loading and Sorting Blog Posts

Short description:

We start by extracting the ID from the file name without the '.md' extension. Then we load the post for that ID and add it to an array called posts. After loading all the posts, we sort them by date using the built-in sort method in JavaScript.

So we want to start from the start to the end minus three. So this will like start here, and then end, go to the back, and then go three characters back. So that would be without the dot MD. And if we console log that out now, console log out the ID, run the test again, you can see this gets the IDs correctly.

Then we can just run, let's see here. So we can get the posts or load the post for that ID. And let's create a array called posts. And then I'm going to go with, push post and return the posts. And if you remember this load post can return post or null. We don't care about the, or null because we just checked that this file exists like the system just told us the file exists. So it's probably not gonna return null. Probably safe to cast this. So you can see now we have all the posts loaded.

Before we go onto the problem with this code, let's actually sort the posts by date. So we're gonna use the sort method on an on the race, this is built into JavaScript. This is going to return the, let's see here. I'm gonna do this. Yeah, so this is going to return the first two posts, and then we have to compare these posts. So if post A is older than post B, we wanna return a negative number. If it is newer than post B, we wanna return a positive number.

28. Sorting Algorithm for Blog Posts

Short description:

And if they are the same age, we wanna return zero. And using just that information, the sorting algorithm can figure out what order they need to go for them to all be in order. So we can do that with a dot published at, that's a JavaScript object, we can get time. This is the Unix timestamp. So the number of seconds or the number of milliseconds since January 1st, 1970, an ever increasing number essentially. And we can subtract this from b.publish at get time. And what this will do is if a is larger than b, if let me think, if a is larger than b, then this will return a positive number. And if b is larger than a, it'll return a negative number. If they're the same, it'll return zero. So this is exactly what the sort function wants. Let's run this and see if it sorts correctly.

And if they are the same age, we wanna return zero. And using just that information, the sorting algorithm can figure out what order they need to go for them to all be in order. So we can do that with a dot published at, that's a JavaScript object, we can get time. This is the Unix timestamp. So the number of seconds or the number of milliseconds since January 1st, 1970, an ever increasing number essentially. And we can subtract this from b.publish at get time. And what this will do is if a is larger than b, if let me think, if a is larger than b, then this will return a positive number. And if b is larger than a, it'll return a negative number. If they're the same, it'll return zero. So this is exactly what the sort function wants. Let's run this and see if it sorts correctly.

29. Sorting Posts and Parallel Loading

Short description:

Sorting posts can be complicated, and even with prior memorization, mistakes can still happen. It is recommended to refer to resources like MDN for sorting algorithms. Loading posts in series can be inefficient, especially with a large number of posts. Instead, loading posts in parallel using Promises can significantly improve performance.

Oh, it actually did not sort correctly, which means we probably need to switch around a and b. Let's try that again. And it is now sorted correctly. So the second post comes first, and the first post comes last because it was published before the first one. Yeah, I think that makes sense. So yeah, sorting, always very complicated. I memorized this before the posts, or before this talk and I still got it wrong. I suggest you just go on MDN and look it up there every time.

Yeah, so this is correct. This returns the correct data now, but for the observant of you, you may realize that this is not the most efficient way of doing this because we are loading all of the posts after each other. So what await does is I load the, sorry, I go to the first... The first article or the first post. I load that, then I go back to the top of the array. Then I look for the second one. I load that, then I do the next one, on and on. If I have like a thousand posts, this is going to take, if load post takes one millisecond to complete, then this is going to take a total of a thousand milliseconds to complete because they all happen one after the other. You really don't wanna be doing this in series like this. You wanna do all of, you wanna load all the posts in parallel. What we can do instead is we can do, create a new array called Promises and not await here. But instead we Promises.push with the Promise that is returned from this load post.

30. Loading and Parallel Processing of Posts

Short description:

This load post returns a Promise of Post or a null. It starts loading all the posts at once and runs them in parallel. Once they're all complete, Promise.all returns the array of posts. Deno has a built-in benchmarking framework that can be used to measure performance. Let's improve the test by asserting the length of posts and the ID of the last post.

This load post returns a Promise of Post or a null. Then we can do Posts is equal to await Promise.all, promises as a Post Array. So what this will do is it will start loading them all at once and then Promise.all waits for all of them to complete. And then they can all be running in the background in parallel. And once they're all complete, then Promise.all returns and we get the array of all of them and this is probably not too noticeable with if you just have two posts, if you have a hundred posts or a thousand posts, this is gonna become really, really noticeable because this is going to run much, much faster than doing it all synchronously.

Oh yeah, in series, sorry, not synchronously. Do you know actually has a built-in benchmarking framework that we could use to benchmark this? If we have time at the end, we can play around with that but it works pretty similar to Dino tests. It's like you do Dino Bench, Dino Bench, you know, there's no bench modules in this directory, but yeah, it works really similar to Dino tests and it allows you to do benchmarks to like measure this and make sure it doesn't get worse in the future, for example. For something like this, probably not as relevant as if you're writing like a library to do image compression or something like that, you want that to happen as quickly as possible.

Okay. So let's improve our test a little bit here by instead of just logging it out, let's assert that posts.length is bigger than one or bigger or equal than one, we could say. And let's assert or let's get the last item, let's assume there's never going to be a blog post before this hello one. So to get the last one, we'll do post.at negative one. This is relatively new JavaScript feature, it allows you to, this is very similar to like this, but it allows negative numbers as well. So negative numbers count from the back. So I want the last item. So the end minus one, which is the last item. We will assert that this item exists and we will then assert equals that last.id is equal to hello. And let's test this. And the test completes.

31. Creating PostEntry Component and Loading Posts

Short description:

We have a working function to list all the posts. Now, let's move on to the next page that shows the list of all blog posts. We start with a big title, the name of the blog, and each blog post has an entry with the date, title, and summary. Let's start with the title, 'Luka's Blog.' Add a margin and then create a new component called PostEntry that takes the post as an argument and returns a list item. We need to load the post first before proceeding.

So cool, we have a working function to list all the posts. Are there any questions? No. Cool, no questions. That means we can go on to the next page that shows the list of all blog posts. So if we go back to the reference page, you'll see we start with a big title here, which is the name of the blog. And then each blog post has an entry which has the name or the, sorry, the date and then the title and the summary. So let's do that. Let's start out with the title. Let's just copy the title from the blog post so I don't have to type this again. Luka's Blog. And if I go to Localhost here, you'll see that it's there. Let's add a margin to this, just slightly bigger than two, 12, that looks good. And then let's add the list. So what we're gonna do is we're gonna create a new component which is called the PostEntry. This takes the post of an argument. Yes, it takes the post of an argument and it returns a list item. We will install that in just a moment. So we're going to do, oh right, we need to actually load the post first. Sorry, I forgot about that. So we're gonna add a new handler first.

32. Creating Handler for Rendering and Loading Posts

Short description:

Handlers import from fresh server and return post array. On GetRequests, load posts using context.render and return the result. Get props with posted array and render post entry component for each post.

Handlers, let's import that from fresh server and have that return the post array. And we want something to happen on GetRequests, we don't need to care about the request or the context right now because we don't need to give any parameters from there because it returns the saved data every time. Let's load the posts, await, list posts, and then, oh actually we do need the context, I forgot. We need to call context.render with the posts and we return the result of that. Then we can call, we can get the props in this component which is going to be the posted array again, so const posts is equal to props.datum. And then we can go over each of these posts using the map function and get the post and render a post entry component for each post.

33. Troubleshooting Handler and Styling Improvements

Short description:

This part covers the troubleshooting process for a handler that was not working due to a missing export statement. The issue was resolved by exporting the handler. The postTitle was then displayed to confirm that it is working. Styling improvements were made, including adding a border, padding, and a link component to redirect to the blog page with the corresponding ID.

And let's see, this did not work. Why did this not work? I must have done something wrong. Oh, I did not export the handler. Right, if you don't export the handler, it obviously does not get a vote.

Cool, okay, so this works. They're currently not actually showing anything. Let's just show the postTitle to see that it's working. Post.title, oh. Const post is equal to props.post. And it shows the second post and the available post.

Cool, let's add some top margin to this and improve the styling. So we're gonna have a border at the top, border top. We're gonna have some y padding, and we're going to, let's see here. I need to quickly look at my reference to what we're actually doing. One second. We are, yes, right, okay. We're going to create a link component here. An A component, which is going to redirect us to the blog page for this component, when I click on it. We're just going to go to slash blog slash the ID. Inside of here, we're going to have the date.

34. Styling and Formatting Blog Post Entries

Short description:

The date is going to be post.publishAt get. We want to display the title, post.title, and the snippet. Let's do styling by making it a flex box and adding a gap between the components. We'll make the title bold.

The date is going to be post.publishAt get, let's find it. Oh, call a date string, that's the one. We want to display the title, post.title. We want to display the snippet. And this is currently not styling yet, but you can see it vendors all the data. If I click on it, it sends me to the right page, which is exactly what we want.

Okay, let's do styling. So first thing we're gonna do is this should be a flex box. Let's actually move this py2 from here to here. So the click area gets larger. We want this to have a, actually let's do that in a second. Yeah, so this is a flex component. You can see it now vendors them next to each other already. If let's add a gap between the two things, that looks much better.

We can now, let's see. So this, we wanna make this larger. So this is going to be bold. It's going to, can I? Oh, it's not actually larger here. Okay, well let's not make it larger then. Actually no, let's make it larger.

35. Styling and Underlining Title

Short description:

We made the snippet slightly grayed out and added an underline to the title when hovering over the A tag. Clicking on the title will redirect to the corresponding page.

It's nicer. Yeah okay, so we have that. And we're going to make the snippet v slightly grayed out. So we're going to do text gray 600 here. And that looks good. Something else we wanna do is, currently if you hover over this, you don't really see that you actually have one of these posts selected. So what we're gonna do is, we're gonna underline the title when you hover over this A tag. And to do that we're going to mark this as a group, and then say, if you hover over the group, then underline. So if I now hover anywhere over this group, it underlines the title. And if I click on this, it sends me to the right page. I can go back. This all works, and it's sorted, or new is told us.

36. Deploying the Project to DenoDeploy

Short description:

Let's take a break from coding and deploy the project. I'll push up my index page to my repository and then go to the DenoDeploy dashboard to create a new project. I'll select my user account and add the 'Freshworkshop.js Nation' repository. It needs access to my GitHub repository.

Okay. That was a lot of talking. I'm sorry. Are there any questions about this?

Okay. Let's take a break from coding. So we've done a lot of coding. I've done a lot of talking. Let's actually deploy this. I'm going to push up my index page here. Look at my repository for this. So this is the repository I'm pushing to. That's not so important. What I'll then do is I'll go to the DenoDeploy dashboard, dash.deno.com. I'm already signed in. I already have a bunch of projects here. And then what I'll do is I'll click on new project. And I will select my user account. And actually, I'm gonna need to add another repository. So let's do, what did I call this? Freshworkshop.js Nation, yes. Let me give it access to this repository on GitHub.

37. Deploying the Project to DenoDeploy

Short description:

I select the branch and file to deploy, package it up, and deploy it everywhere. I have analytics, logs, and can customize the deployment. Each commit on GitHub gets a unique preview URL.

So I select my organization and I do Freshworkshop.js Nation. Select the branch I want to deploy, select the file I want to deploy. I want to deploy the main Notes file, that's the entry point. And I want to give it a name. Freshworkshop.js Nation, that sounds like a great name. And then I click on link. It's going to then download all my code clone my repository, package it all up and it is not deployed.

So if I go to the Freshworkshop.js Nation document or DevLink, you can see my page is deployed. And it is not just deployed here and where I am in the Netherlands, but it is deployed everywhere. If I open my Effect Console here, let's see Network Panel and look at the response headers, I'm currently getting a response from Europe West 4, which is in Amsterdam. But if you all request this page and look at the response headers, it is going to be somewhere else, maybe in Asia or in the U.S. Yeah, so that was literally it. This page is now deployed everywhere. I have analytics for it like requestCounts, I have logs. My application is not actually generating any logs. All the deployments, I can add environment variables, add a custom domain. I can change the name if I want to. And the best part is that on GitHub, it deploys my page for each commit now. So every time I push up into commit, it'll give it a unique preview URL. So this one is a fresh workshop destination with this long string.

38. Using Preview URLs and Full Page Transition

Short description:

This specific commit of this project will be available to this URL. You can go through all of the old commits using the preview URLs to easily figure out when something broke. The link we built in the list reloads the full page. The navigation is quick, and client-side rendering is not necessary. Fresh does not have client-side routing but has client interactive components.

And forever into eternity, this specific commit of this project will be available to this URL. So even in three months time, if something drastically changes, but you realize that, I don't know, something broke in the last month and you're trying to figure out which commit did it break. You can go through all of the old commits this way and really easily figure out when something broke by using the preview URLs.

Any questions on do you know deploy? Or did it go too fast and people didn't see what happened? Luca.

I would have one question, but regarding the page still, the link we built in on the list, does this reload the full page now, I assume? Yes, yeah, it does. This is a full page transition. So if I go to the Network Panel and I refresh, I click on the link, this does a full page transition. So I get the response back here, and you can see that this is pretty much, you don't know this is on localhost. Let me go to the deployed one and show you the timing there. This pretty much happens so quickly that you can't even tell. This happens in, is this 132 milliseconds? If I reload this page, this page renders in 36 milliseconds. So this is like anything under 100 milliseconds, humans perceive as being instant. So this navigation is so quick. Yeah, client-side rendering would just, Why would you client-side render this? This is much faster server-side rendered.

Okay. Yeah, I'm sorry, go ahead. Will we have in the workshop will you cover routing or if not, is there a router? Yeah, so there's client-side route. Fresh does not have client-side routing. Fresh does have interact, like client interactive components. And we will get to that later on in probably around half an hour or so.

39. Server-side Rendering and Performance Benefits

Short description:

Everything we've seen so far is completely server-side rendered and there's no JavaScript being served at all. It has a 100% performance score on Lighthouse, indicating excellent performance. Server-side rendering is much faster than client-side rendering.

Alright, cool, cool. Thanks. Yeah, but so just to clear this up, everything we've seen so far is completely server-side rendered and there's no JavaScript being served at all. Like, you can see this page, it's the HTML page and the CSS is embedded into the HTML and there's no client-side JavaScript. And this means that if I run this through web page speed insights here, oh, why is this in German? Oh, right, I set my language to German, I remember now. Give it a moment. Has 100% performance score and this is like as good as it can get on Lighthouse. You can actually see this, not just with my page, but also other pages that are built with thresh. This is real user data here. Everything is completely green. You pass all page, all of Core Web Vitals things by default because server-side rendering is just really, really fast compared to client-side rendering.

40. Rendering Markdown as HTML and Applying Styling

Short description:

To render Markdown as HTML, we can use the Dino land X/GFM library. By importing it and using the GFM.render function, we can convert the Markdown content into HTML. To embed HTML in Preact, we need to use the dangerouslySetInnerHTML property. GFM.render also sanitizes the HTML to prevent XSS attacks. Additionally, GFM provides a default CSS stylesheet for styling Markdown, which can be injected using the dangerouslySetInnerHTML property. With the CSS applied, the Markdown content is now nicely styled.

Okay. Yeah, so let's now that we've deployed this to deploy, let's get back to coding and let's start out with something slightly less complicated to get back into it which is right now you can see that we're just rendering this as text here, as plain text not as marked down yet, which means that this looks kind of trash, right? Like this is not how I want my markdown thing to be rendered. So to be able to render this as markdown, we first need to find some sort of tool which can help convert the markdown text to HTML that we can embed in our page. And there's actually a few libraries for Dino to do this, one of them is Dino land X slash GFM, which stands for GitHub flavored markdown, and I can just import it, let's just do that and I will show you what it does. This is a post page, I can put this into my import map, I'm not gonna do that right now for time's sake, but yeah. So what I can do is, I can do const HTML is equal to GFM dot render page or sorry, post dot content, and this is going to render my markdown into HTML, and then instead of embedding the plain text string into my page, I can embed HTML instead. And to embed HTML inside of Preact, you need to do like some slight gymnastics here, you have to do this, which just tells Preact, yes I'm very sure I want to inject HTML in here, because this might like inject, like if this is untrusted HTML that you're rendering, this might result in XSS and things like that. If this is like user supplied HTML, you probably don't want to be doing this. But this HTML is provided by the developer by us, so it's probably safe, and GFM about render actually sanitizes the HTML as well. So it'll strip out any script tags or things like that, that are in your markdown. So if we look at this post now, you will see that this definitely looks more rendered, but it still looks pretty terrible. And like this is technically a table, but without the right styling, this does not look like a very nice table. And same goes for the site h2 here, not a very nice table. So what we want to do here is we want to add some styling as well, and GFM actually ships with a default style sheet for the Markdown as well, which we can also do this dangerously, set HTML with very confusing, because we're not actually gonna be injecting HTML here but CSS. But yeah, so you can do this, and this will create a style tag on the page with the CSS in it. And then we also need to create, give this element here a Markdown body CSS tag. And you can now see it's nicely styled. And if I look at this, there's the style tag here, which has all of the CSS for styling Markdown in it. So this works on my hello world post and on my hello world post, there's the thing that was previously with the underscores on either side is now italic. This has the nice title and the table.

41. Time Formatting and Locale Selection

Short description:

The only thing that we're still missing is the time formatting. Users can switch the locale by going to the settings page and updating the locale. The first step is to render the dates with a given locale. Currently, we are just calling two locale date string without specifying the locale, so it renders with the default user agent locale. Let's change that.

And the title has this nice little button where when I click on it, it'll give me an anchor URL to this page or a specific part of the article. So that's Markdown rendering. Any questions on that or anything covered previously? Nope, okay.

So we're now at the point where all of our application is pretty much completely built now. The only thing that we're still missing is the time formatting. So if you remember, if you go back to the reference page here, you will see that this renders the dates in the user-selected locale. And users can switch the locale by going to the settings page and updating the locale here. So let's see. I wanna go back to my default locale, which is apparently German. Or if you go to Dutch, and it'll render things in Dutch. The first step here is to even, oh, there we go. That's our code. To even render our dates with a given locale. So right now we are just calling two locale date string. And two locale date string does, yeah, it takes the locale argument, but we're not specifying it, one. So it'll just render with the default user agent locale, which on my system is English. Which is, or English US. Which is why this all renders in, or is it English UK? I don't know. It renders in some sort of English time format. So let's change that.

42. Using datetimeformat for Advanced Date Formatting

Short description:

Let's use the built-in datetimeformat in JavaScript to create a date formatter. We can get creative with formatting options, such as displaying dates as numeric or two digits. We'll hard code the locale to English UK and use the 'full' date style. This will give us the longest date format. By using the formatter and the format method, we can easily format the date with the desired style.

Let's use the intel.dateformat built-in. So we're gonna create a date formatter with intel or new intel.datetimeformat. And datetimeformat is a built-in in Javascript. It's actually not that new, but it allows you to do like super advanced date type formatting. Like you can get really, really creative with how you format the dates, and get very precise on how exactly you want them to be formatted.

For now, let's hard code the locale to be English UK. And specify an options bag. And we're just gonna say we want this date style to be full. And this date style full is just a preset that essentially like, you have all these options on datetimeformat. For example, do you wanna display dates? If so, do you want them to display as numeric or two digits? So like if your day six of the month, do you wanna display it as six or as oh six. Or month, do you wanna display month as the two digit date, as the numeric date, as like Jan for January? Or do you want it to like read out January in its full stuff like that? It's not full. It's like the longest date style you can have.

And this is on the posts page. So on top of the, that is this, this time up here. It's fine, if we have this one be really long because this, yeah. And this is a whole post, right? You're only showing one date. So we can have that be really long. And what we can do is we can take this formatter and say date time format dot format with the date instead of calling to local date string. And if I refresh now, it will format this with the long date. So that's really easy.

43. Specifying Custom Locale and Committing Changes

Short description:

I can specify a different locale, such as German, and even multiple locales. If a specified locale is not supported, it will fall back to English. Let's apply the same date formatting to the next page using a short date format. The output remains the same, but it uses the new date formatter. We can now specify a custom locale. After committing the changes, the deployment is quick, taking only a few seconds.

If I want to use a different locale now, I can specify that here. So I want this to be in German. It's gonna be in German. And I can actually also specify multiple locales. So I could say like, I want English or I want German. And then it's going to pick whichever one in this list it supports. So if I specify one in the front here, which it doesn't support, like, I don't know, XX.YY, like this definitely does not support this because this is not a thing. It'll just fall back to English. I mean, if I switch around the order here, it will fall back to German now cause that's the second one.

For now, let's hardcode distinguish though. Let's do the same date format thing for the next page. I'm also just playing a date here. Let's once again do the date format and instead of doing a full date this time, let's do a short date I think. And we're gonna do the same thing when we do date format dot format post.publish. And you can see here that actually nothing has changed because the output happens to be the same but yeah, it's formatting using this other date formatter. This is a refactor we have done now so we can specify a custom locale in a moment. Let's go to this and commit this. And actually, just a quick note, I'm committing this and you will see that if I go to my page here, JS Nation, I'm very slow at typing so by the time, yeah, this is already deployed. So this deployed really, really quickly here. If I push it, takes like five or six seconds for it to deploy.

44. Parsing Accept Languages Header in Middleware

Short description:

Let's figure out the user's preferred page display locale by examining the accept languages header sent by the browser. The header contains a list of languages the user understands or supports, ranked by preference. We can use a middleware in Fresh to intercept all requests and parse the accept languages header. This allows us to write the code once and share it between all pages.

And not just deployed close to me but deployed globally and all of our regions. Okay, so next thing, let's figure out what locale the user actually wants their page to be displayed in. And we can start out by just looking at the accept languages header that the browser sends when it makes a request. So if we go to the network panel again and we look at this request that the browser sends, you can see that as part of the request headers, where's it here, the request headers, it sends this accept language header, which contains a bunch of languages that the user understands or supports and which one of them they prefer.

So for example, I have currently set up that I prefer German and after that US and then British, and then just plain English. And this Q here, that's like, how much do I prefer? So if there's no Q, I prefer it the most and the lower the number, the less I prefer it. And if I go into my Chrome settings here, Chrome settings, got it, language settings again. And change the order. Let's move this to the top for example, and where's my local, oh, that's the wrong one. And if I refresh, you can see this is now a different order. And now German is the one I care at least about.

Okay, so how do we do this? So what we could do is we have this handler function where we get the request. We could, in each handler function, do like, request.headers.get accept language and parse that out, but that's kind of annoying because we have multiple pages and ideally we want to only write this code once and share it between all of our pages. So what we can do instead is we can use a middleware. And to use a middleware in Fresh, you create this underscore middleware file anywhere in your route structure. And this middleware is at the root of your route structure, which means it is going to intercept all requests. So any request that comes into your application is going to be intercepted by this middleware. And this middleware is actually very similar to the handlers in that it exports a function called handler. And this takes a request, which is a request, and it takes in a context. And this time the context is a middleware, sorry, middleware.

45. Using Middleware to Intercept Requests

Short description:

We can use middleware to intercept requests and call the next middleware or route handler. We can validate the interception by logging the request URL. To parse the accept languages header, we'll use the accept-language-parser library from NPM via ESM.sh. The parse function is called with the accept languages header.

No, it's working tight. Middleware handler context. Context, there we go. And we can import this from fresh server. And then what we can do here is for now we'll do nothing. We'll just call context.next. And what this will just do is it will call the function and then it'll immediately hand it off to the next middleware. And because there is no next middleware, it'll hand it off to the route handler. So if we look at this, local host, we'll see nothing has changed, right?

But we can validate that this is actually being called. We could do like console.log request.url, for example. And you can now see that every time I refresh here, it'll intercept all of the requests that the browser is making. Yeah, so let's see what can we do here. We can, we now want to parse out this accept languages header. To do that, we're going to use a library called accept language parser. It's on NPM. But you know, we can still import things from NPM by importing them from ESM.sh. So you can do ESM.sh accepts language parser, oh parser, and we're gonna do 1.50. That's the latest version. So we can call this parse function with the request header called accept languages. Accept language.

46. Passing Locale Information to Handlers

Short description:

The parse function can take a string or undefined and return a string or null. We use context states or middleware states to pass information from the middleware into our handlers. By creating a context state locales array, we can iterate over the locales and create a new locale with the language code and region if available. If the user has not specified an accept languages header, we default to English. Let's address the type error.

And this parse function can take either a string or undefined, get return string or null. So we're gonna turn the null into undefined. And then for now, let's just block out the lines. Console block lines. So refresh this, you'll see it parses out the accept language header and it gives me the list of all of the supported locales that the user has.

We now somehow need to pass this information from the middleware into our handlers and then ultimately into our intel.date, time format, date time format objects. To do that we use context states or middleware states. So you might've seen this during the tab completions earlier I think, there's something called the context.state that I can set information on and I will then be able to use this data inside of my handlers by also calling context.state here.

So what we're gonna do is we're gonna create a context state locales array that's gonna have all the locales in it and then we're gonna, we're gonna iterate over all of the locales in the languages array here for langs, create a new locale, which is lang.code. So lang.code is this first part and if there's a region, so if there is a region in this locale, we can do locale plus equals dash lang.region. So this means that for this code for example here, it'll be en-gb. For this one which does not have a region it'll be en with nothing after there and this same here, so de with nothing after. And then we can append the locales to our locales array. Okay. Context plus locales, yes, okay. And we're gonna deal with this type error in just a second here. But one thing we wanna do first is if the user has not actually specified an accept languages header, which is like all browsers specify this but let's say the user makes a request using curl or something, we don't want it to break. So what we're gonna do is if there are no locales specified so if context.locales.length is equal to zero, then we can context.stats.locales.push EN. So it'll just default to English if there's no locales. Let's get rid of this type error.

47. Changing the Type of Context.State

Short description:

Let's change the type of context.state to a concrete type. We can do the same for middlewares and handlers by creating a file called state.ts and defining the state type as an array of locales. By specifying the state type, the typings will be correct for context.state and locales.

So right now you can see that context.state is this like records string unknown type. Let's change that, let's change it to be a concrete type. So just like we did with the handlers earlier, where we could explicitly specify what type the render data is, we can do the same with middlewares. So we're gonna create a little file here called state.ts, just so we have somewhere to put this definition of state because we're gonna share it across multiple files, we're gonna have the state contain the array of locales as strings. So if we now specify this, state, and we import that, then people see all the typers go away and state is typed to be a state here. And we can actually do the same for handlers now. So we can do state as a second argument to handlers. Import that, and now if we call context.state here, we'll have locales that will have the right type.

48. Passing Locales from Handler to Page

Short description:

We want to pass the locales from our handler into our page. To do this, we'll create a wrapper type called data that extends state and includes a post field. We'll use this type for our page props and handler data. By spreading context.state and adding the post, we can access the post and locales from the page data. We can now format the locales using Intl.dateTime format.

So we want to pass the locales from our handler into our page now. So we can use it in a state time format. To do this, we're going to have to pass it through the render data again. So the render data currently is just the post, but we want it to be the post and the locale data. So what we're going to do is we're going to create a little wrapper type called data, which is going to extend state, extends state, and it also has a post field on it, and we're going to use that as the type for our page props and handler data. And then we can do context.state, spread that into this object and add the post. And here we can now do post and locales to get the post and the locales from the page data. We can now put these locales into Intl.dateTime format. And if we go to the page now, and where we're trying to see this is the blog page. This is currently a UK rendering. And if I switch this to be German at the top and refresh, it says it in German. So it works. So we go to the middleware. Let me recap what we're doing. Go through middleware, get the except language header. We parse it. We then build the list of all the locales that the user supports. If they don't support any locales, we just default to English. And go to the next context or the next handler, which is the handler of the route. In here, we get the blog post, as we saw earlier.

49. Rendering and Middleware for Auth

Short description:

We call the render function with the state, extract the blog post and locales, and format the publish at date using Intel.datetime. A question was asked about building an auth framework for Deno, which involves creating a middleware to extract authorization information and adding it to the context states. Importing modules from NPM using GSM is generally unrestricted, as long as they run in a browser and do not require node-specific modules like FS or child process.

We then call the render function with the state, the items in the state. So that is the locales plus the blog post. We then extract the blog post and the locales out of the data in the page function. We put that into the Intel.datetime format. We then use the Intel.datetime format to format the publish at date. And then that gets displayed on the page.

That was a very long session. I hope you have questions. So I can stop talking about code for five minutes. If you would want to have, for example, so somebody asked me the other day how you would write, I don't know if any of you have used NextAuth, that's an auth framework for Next.js, how you would build that for Deno. And you would essentially do this. You would create a middleware. Hands it like this, which intercepts all of the requests, extracts out the authorization, tokens, does all that. And then adds the authorization information into the context states. So then users can use that in the handlers to authorize that a user has access to something.

I have a question. You mentioned that we can also import things from NPM using GSM, is there any restriction on that or can we pretty much expect any modules to work? Yeah, so the easy way, okay. So first of all, the first thing to say is anything which is on NPM and runs in a browser is going to pretty much work in DNO as well. Like if you're not importing any like node-specific modules like the FS module, for example, it is definitely going to work. DNO does shim out or ESM Node.SH does shim out the FS module, for example, in Node.js or the process like the child process module or the like a bunch of other things.

50. Porting Libraries to Deno

Short description:

The compatibility of a module with Deno depends on the module itself. Modules that use native libraries or APIs specific to Node.js will not work with Deno. However, modules that use common Node.js modules like FS or process will generally work. The process of porting a library to Deno depends on the type of library. Purely computational libraries may only require deleting unnecessary files, while libraries that rely on Node.js features will require rewriting import statements. Deno provides a tool called dnt that allows you to transpile a Deno library into something that can be published to NPM. If you want more details, we can cover it later or discuss it on Discord.

If you input those things, there's a higher chance it won't work than if it doesn't import them at all but it probably will still work. So, yeah, it really depends on the module itself. Like, if you have a module which uses nodes, native, what's it called? Native libraries API, NAPI, or it gives the FFI or anything like that, that's not going to work because DNO does not support nodes like native modules system but anything which uses just like the FS module or the tell process module or the node HDP module, anything like that, that'll just work. So, usually it's just try it out and it probably will work. And if it doesn't, then it doesn't, but it most cases it'll work. So it's a really only things which are like very, very node specific on NPM will not work.

So what is the process of supporting it to DNO? Let's say I want to port a library to DNO? Yeah, so if you have a, Okay, so again, this really depends on what kind of library it is. So if it's a library, which is like purely computational, it does nothing other than take some input and return some output. Porting probably involves like deleting a couple of files, like deleting the package JSON file and like deleting your Prettier config and deleting your like, just config because that's all built into DNO. But you probably don't need to do any of that because you can just import the package via esm.sh. If you have a package which really relies on node features, and you wanna port that, then that means you have to go through your source code and rewrite like the places where you import from the FS module, change that to use like dno.open or dno.readfile or like dnodesfs stuff. So yeah, so that's something you have to do. But we actually provide you with some tooling called dnt. Actually give another workshop at, I don't remember where, I think at Node Congress, maybe not Node Congress, I think it was Node Congress, where I demonstrate how this works. So dnt, let's dnt, that's this one. And this allows you to take a library that you wrote for Deno and it will transpile it into something that you can publish to NPM. So you can like write, if you have a library, you can write it for Deno first, and then sort of like transpiled for NPM, so your Node users can still use it, but you get all the benefits of using Deno. So there's some ways of doing this. But it's, if I go into like all the details, this workshop is going to explode. Like let's, like if we have time at the end, let's cover it then, and otherwise, maybe hit me up on Discord after the fact, and we can chat about it there.

51. Deploying Deno to Other Platforms

Short description:

It's possible to deploy Deno to other places like Deno deploy and AWS. There are multiple options available, including fly.io, begin.com for AWS, and Cloud Run for GCP. Official Docker images for Deno are also provided, which can be used to deploy anywhere. Additionally, there is an AWS Lambda runtime for Deno maintained by Deno-lambda.

But yeah, it's possible. It just really depends on what kind of library it is, what exactly you need to do. Yeah.

I have a question about deploying Deno to other places like Deno deploy, like AWS. Yeah. How hard it is? Like what would you recommend? Are there any like recipes already? Yeah.

So there's a bunch of places you can deploy Deno other than Deno deploy. So Deno deploy obviously is really easy because it's built for deploying Deno code, but fly.io is another one, which it's really easy to deploy Deno too. I think they have it here. Example, if you're deploying to AWS, there's begin.com. They have some documentation of how to deploy to AWS. Let's see if we can find that real quick. No, this page is taking too long to load. Okay, I can't find it right now, but you can deploy to AWS as well. You can deploy to GCP using like Cloud Run and Deno in a Docker container. We publish official Docker images if you go to github,com, slash Deno-land, slash Deno-docker. Yeah, we have official Docker images which are updated at the same time we update Deno itself. You can use those to build like a Docker image to deploy anywhere. There's a AWS Lambda runtime for Deno as well. It's not an official one yet, but it's maintained by, right, let me see if I can find it, Deno-lambda.

52. Deploying Deno and Compatibility with STD

Short description:

Deno can be deployed to various platforms such as Lambda, Versel, Netlify, Superbase, Azure's Cloud Functions, and anywhere that supports Docker containers. Deno's APIs are stable and only additions are made. STD, however, is still unstable but always released and tested with the latest Deno version.

Let's see, is this the one? I think this is the one. Yeah, so this is Deno, deploying to Lambda. It's possible. You can deploy it to Versel. You can deploy it to Netlify using Netlify Edge Functions. You can deploy to Superbase using Superbase Edge Functions. You can deploy to Azure's Cloud Functions. You can deploy it anywhere that supports Docker containers, so like Kubernetes, if you want to, you can just run it on a VM. Yeah, you can pretty much deploy anywhere.

Amazing, thanks. I have a question about the version of the std namespace versus the version of Deno itself. Is there a compatibility that we should be aware of?

Yeah, so Deno is past the 1.0 which means that it, like Deno's APIs are completely stable as in we only ever add things. We never remove anything from the Deno API. For STD, it is not yet at 1.0. You can see there's like, it's still pre-1.0, so STD is technically still unstable, which means that we sometimes remove things, move them around to get it ready to go to 1.0. But we always publish Deno STD and Deno at the same time. Like, on the same day when we cut Deno, we also cut Deno STD. Which means there's always a Deno STD release which has been tested with the latest version of Deno. The latest version of Deno STD always works, like 100% definitely works with the latest version of Deno. But, a Deno STD version from like 10 weeks ago will most likely also work with the latest version of Deno.

53. API Changes and Stability of Deno and STD

Short description:

Deno has no breaking API changes, even a version from a year ago will work with the current version. When updating STD, check the change log for any API changes. STD is stable and deprecates features before removal. Using an older version of STD is fine. STD will go stable with the same version as Deno.

Because Deno has no breaking API changes, like even a version from Deno or a version of STD from a year ago will still work with the current version of Deno. What you do need to be aware of, if you're updating STD, so an STD version from 10 weeks ago to an STD version from today, you may have to look through the change log to see if there's anything in the API that has changed in one of the APIs that you're using. But, it's pretty rare. We usually don't change things in STD, even though it's not stable yet, we don't change things in the parts that people very actively use, and we try to be very careful about it. We deprecate things first, and then it'll show up in your editor that it's deprecated, and then you can update in your own time, and there's nothing that requires you to update to a newer version of STD. Using a version of STD from a year ago, it's probably fine. It's not the end of the world. And we are working towards ultimately having STD go stable, and when it goes stable, it will be the same version of Dino. So if Dino 1.24 is released, we'll have STD 1.24 released on the same day. But currently they're still out of Singapore, even though they are published on the same day.

54. Adding Settings Page and Locale Display

Short description:

We will add a new route for the settings page and display the current locale. To do this, we will create a handler for the get request and pass the locales from the state to the page props.

Okay, are there any other questions or you wanna hear me blab about code again? Okay, I guess we're continuing with talking about code. So we are now using the default language version that the user selected by their browser. But in the reference application, you can select your own version on the settings page. So on the settings page it shows your current locale, and you can update the locale using this field here. We also want to support this now.

So the first thing we're going to do is just add a new route for the settings page. So let's add settings.tsx here. Am I just going to copy all of this? No, I'm not. I'm just going to do this manually. Export, default, function, settings, page. Let's copy this. Let's import the TW. And add the title. And you could see that if I go to localhost slash settings, this page now exists. There's nothing on it yet, but it's there.

Then, let's add the little thing that says what locale you're currently using. To do that, we're going to need a handler again. Because we need to show. Because we need to get the locales from the state and pass it in to the page pops. We're going to do a handler for the get request again, and it's going to take the request and the context.

55. Setting User Locale with Cookies

Short description:

It's going to set the current locale based on user input and save it as a cookie on the server. The next time the user makes a request, the saved locale will be used instead of the one specified in the accept language's header.

It's going to do context.return context.render context.state. Let's import handlers for server.ts and import state from utils.tels. And I can now get my props from my page props. Again, I have the arg.state. And I can now get my locale, locales from props.data. And I can then do p your current locale is locals zero. And if I look at this, it shows my current locale. The margin here.

OK, so this is pretty trivial. This is just another read only page. There's no interaction that's happening here. You're just retrieving data from the server. It's rendering it. It's being displayed to you. And read only settings page.

What we want to be able to do is we want to let the user input a locale and submit that. And then send it to the server, save it on the server. And then the next time the user makes a request, we want to retrieve that locale and use that instead of the one that's specified in accept language's header. To do that, we're gonna use cookies. So we're gonna have an input field, which when you enter something into the input field and hit save, it will set a cookie where I can actually show you this.

56. Creating Form for Locale Selection

Short description:

We will set a cookie with the user's selected locale. By creating a form on the server, users can input their preferred locale. The form includes a label, an input field for the locale, and a save button. Styling is added to improve the appearance of the form.

It will set a cookie. Yes. Where the value is, this one is irrelevant, we can remove that, where the value is the locale that you set. We can then inside of our middleware extract this cookie, see what the value is. If there is a cookie set with a value, we will use that instead of the except file, which is better.

The first thing we're going to do is, we're going to create a form on the server, sorry, on the... We're going to create a form that the user can use here, that the user can input things into, which is going to have a label. It's going to have an input field... Local ID, local. It's going to have a button to submit things. That's going to say save. And this looks like this right now.

Let's add some styling to make this look less terrible. Let's do a border on this. Let's do a padding. That looks much better. Let's make this a space x2, so there is space in between all the things. Let's make this button a little nicer. We're going to have the button also have some padding. We're going to do a background blue.

57. Adjusting Form Styling and Setting Up POST Handler

Short description:

We made some adjustments to the text color, font, and size, but ultimately decided to keep it as it was. Now, we have a form where you can enter information. Pressing Enter will display 'area 405,' indicating an unsupported method. However, we still need to set up a handler for the POST request.

It's going to be 500. By default, if you hover over it, it becomes a little darker. And if it's disabled, it becomes a little lighter. We want to change the text color to white. And let's change the font to medium. And yeah, I think that looks pretty good. Let's change this to be font bold. Actually, I don't like that. Let's change that to just be a little bigger. Text excel, maybe. Actually, I don't like that either. Let's just keep it the way it was.

OK. So we have a form now. You can enter things into this form. And if you hit Enter, it will say area 405. And area 405 means this method is unsupported. So this just sent a POST request to the server when I navigated or when I pressed Save. But we don't actually have a handler set up for a POST request. So we're going to do that.

58. Setting Up Handler for POST Requests

Short description:

We're going to set up a handler for POST requests. We'll parse the form data from the request using request.formData and retrieve the locale from the form using form.getLocale.

We're going to set up a handler for POST requests. We've taken the request. We've taken the context. We actually don't need the context. We're just going to take the request. So this was a form that was submitted. So we can call request.formData. It's going to parse the form data out of the request and return it to us as a form data object. We can then call, let's see, we can get the locale out of the form. So we can do form.getLocale. The name of the input was locale. So the form element is ultimately going to be called locale.

59. Input Validation and Setting Cookies

Short description:

Let's do some input validation by setting a cookie if the type of locale is a string. We can import the setCookie function from the DM standard library, which takes the response headers and the cookie name and value as arguments. To set the cookie, we create a headers object and call setCookie with the headers, name, and value. We don't need to worry about expiration for now.

Let's do some input validation. So if type of locale is equal to string, then let's set a cookie. So to set a cookie, we can import some utilities from the DM standard library again. There's a function called setCookie in SCD HTTP cookie.ts. And let's actually look at that in the Docs group real quick. HTTP cookie.ts. Yeah, called setCookie. And this takes the headers as the first argument that we're trying to set the cookie on. So this is the response headers and the cookie itself. And the cookie has a name, has a value, and a bunch of other options, which we don't particularly care about right now. So to do this, we first need to create a headers object. New headers. We can then call setCookie. Headers. Name local. Value locale. We don't need to. Let's not worry about like expiration. Actually, that's the expiration date. Hi.

60. Setting Cookie and Redirecting

Short description:

So we set the cookie and return a redirect response to the settings page. The header or cookie changes when the locale value is modified.

So it's going to be, that's one hour, that's 24 hours, that's a year. So this cookie's not going to be set for a year. And then let's return a response, which is going to have no body. It's going to be status 303, which is a redirect. It's going to have the headers. And actually, redirect headers also redirect responses with redirect status codes also need to have a location header, which is where you're redirecting them to. So we're just going to redirect these back to the settings page when they emit. So this is going to turn the POST request, where we set the cookie, back into a GET request, where they just get the settings page again. So if I now call, let's see, NL, you see nothing changed, because we're not taking the header into account yet for this display. But if I go to my application panel and I look at my cookies, and I get rid of all of this garbage here, which is not relevant, and change the header value here, or change the locale value, you can see the header or the cookie changes. So that's part one of this.

61. Handling POST Requests and Form Handling in Fresh

Short description:

We need to parse out the accept language header in the middleware. Import the cookie utilities and set the cookies on the response headers. By changing the locale, it updates the current locale and reflects the changes correctly on the settings and posts. Pressing Enter resets the locale to the default except languages locale. This is how to handle POST requests and form handling in Fresh using Guino.

Part two is that we actually need to parse out this header in the middleware. So before we parse out the accept language header, let's import these cookie utilities here as well. So let's get cookies. So const cookies is equal to get cookies with request headers. So cookies are sent on the request headers from the client. And if we want to change them, we have to set them on the response headers. We can do cookies. Why did I call it locale? If cookies.locale, then we do context.state.locale. Cookies.locales.push cookies.locale. OK, that was pretty easy. And now you actually have seen it already. If I change this here, it updates my current locale. And if I go to the home page now, it reflects this correctly on the settings. Or the settings that I changed are being correctly reflected on the posts. If I change this like Polish, and look at this, you will see it now says it's in Polish. Very nice. If I just press Enter, it'll reset back to whatever my default except languages locale is, because it'll set the cookie header to the empty string. The empty string does not pass the if statement. So it is not pushed onto the locale array. OK, so that is now how to do POST requests and form handling in Fresh using Guino.

62. Client Interactivity in Fresh

Short description:

For some things, server-side interaction is not necessary. Fresh supports client interactivity in a different way than other frameworks, like Next.js. Next.js renders the entire website on the server and ships it to the client, resulting in unnecessary rendering on the client. In Fresh, you only render things on the client that make sense to render on the client.

Are there any questions about this just so far? We're going to there's more to do still. OK, that does not sound like it. That is the wrong project again. OK, if there's no questions, that's great. Let's add some padding to the top of this and commit that.

OK, so now we're going to get into something interesting again, not to say the other stuff wasn't interesting, but this is especially interesting, which is that everything we've done so far has been server-side interaction only. It's always you do something on the server, like you render the page, it displays that on a client, the client clicks something on the client that sends a request to the server, updates something on the server, sends back some data to the client. For some things, this is not really necessary. Something you might want to do is, when you type a locale into this field here, why doesn't it live show you what the name of this locale is or a preview of the locale? Right now, if I want to preview what a given locale would look like, enter it, press save, to then navigate back to the index page or this page and refresh and check what it looks like and then navigate back and I don't like it. Very annoying. I just want it to show up here and ideally without any network requests, just on the client. So this is a great use case for some client interactivity. And Fresh supports client interactivity, but it does it a lot different to a lot of other frameworks. So if you've ever used Next.js before, then you will know that you can essentially write on, or you can essentially put like state manipulation things, or like use state and onClick handlers and all that kind of stuff anywhere in your entire website. And what Next.js will do is it will take the entirety of your website, like the entire rendering code that is run on the server, it'll ship it to the clients. This is like 70 or 80 or a hundred kilobytes of JavaScript, and it will re-render your entire page on the client. So for example, for the blog page where we show blog entries, it will take the entire markdown library that is used to do the markdown rendering, and it will ship that entire thing to the client. Even though the rendering already happened on the server, it will re-render it on the client again, but like the user won't really notice that anything happened because it'll hopefully give the same output on both the server and the client. So it's not really useful to do this, right. Like why are you rendering everything on the client again, even though it's not necessary? Ideally you only want to be rendering things on the client, which actually makes sense to render on the client.

63. Using Islands for Client Interactivity

Short description:

Fresh uses the concept of islands, which are interactive components in a sea of static content. Islands contain the code required to render on the client and can perform all the functions of a regular Preact component. The term 'islands' was coined by Jason from the Preact team. For example, the header, sidebar, and image carousel can be interactive, while the blog post itself remains static. Fresh simplifies this by using an 'islands' folder to contain the interactive components, which are rehydrated on the client for full interactivity.

To do this, Fresh uses a concept called islands and islands are, how do I best explain islands? Islands, actually, okay. I have a nice way of putting this. Okay. Islands are islands of interactivity in a sea of static content. So you have static content and then part of your application is like a little island that is interactive. And this island has all of its rendering code shipped to the client and is able to do like anything that a regular Preact component can do on the client, for example. And this term was actually coined by Jason from the Preact team. Let's actually pull up the blog post, Jason, format. Do we have this one here? This actually is a really nice example.

So for example, you can have your HTML page here, which part of this, the header, for example, you wanna have the header be interactive because it's like a pop-up or something. You wanna have the sidebar be interactive because it has like a newsletter signup form in it. And you wanna have the image carousel still be interactive because there are buttons that you click on. But the content, so for example, the blog post itself does not need to be interactive, right? Like the blog post is static content. You can render that on the server. There's no need to re-render that on the client. So that can be static. And then all of your interactive components that actually do need to be interactive can be little islands, which only contain the code that actually they require to render on the client. Fresh makes this really easy to do by having this folder called islands. And anything inside of the island's folder, any component you put in there, it's just a regular pre-app component, but that component will be rehydrated on the client, which means that that component will have full client interactivity on the client. So if I have a component which is called, I don't know, the locale selector, selector.tsx, and I export this locale, locale selector function.

64. Client Interactivity with Preact

Short description:

We can now do client interactivity on this component. We can put an onclick event on the button that opens an alert. This JavaScript happens on the client, not the server. We can use useState.preact.

And actually I need to import preact. And I return, let's see, let's put this entire form here into the locale selector. I can get the locale selector, import it from the islands directory. And so far this looked like nothing has changed, right? This is just, we've moved this code from being directly inlined into the page to being in a separate file. But by putting it into the separate file inside the islands directory, we've actually done something pretty special, which is that if we look at the spec panel here, look at the network panel to refresh, you will see that we're loading this island locale selector.js file. And if we look at this and pretty format it, let's see if we can actually view what's going on here. Can we? Yes. So this is, so this is that form that, like this form here, look how selected component, but the jsx is transpiled out. So you can see like, this is the rendering code we need to render this form on the client. Um, yeah. But it doesn't contain for example, the word settings anywhere in the bundle, because this is only server side rendered. The only thing that is client side rendered is this form itself here. That means that we can now do client interactivity on this component. That means that, we can do things like, actually, let me make this a little easier. We can put an onclick event onto this button, that is when you click on it, it opens an alert. And I save this and it opens an alert. And this is like client interactive, right? Which means if I do this, this JavaScript that open this alert happened on the client out on the server. If I do this, if I try to do this anywhere else, like if I want to put this onclick on my settings button up here, or on my settings center, this does not work, this is not a island, this is not this service that rendered only, not a client component. This means that we can now do things like useState. So we can do let's do this useState.preact.

65. Client Interactivity and Localization

Short description:

We can control the input using a state hook. The value of the locale is updated as the user types, and the localized name of the locale is displayed. This is all done on the client without making any requests to the server. The intel.locale API is used to show the localized name of the locale.

useState.preact hooks. Const locale, set locale. And where we can do things like control this input using a state hook. So we'll set the value to this locale and whenever you input anything, you update, oh, you update the state. So set state is equal to e.currentTarget.value. And you can see this does nothing yet, or nothing observable at least, but what we could do is we could, for example, render out the locale here. And what this will do is if I type something in here, it will render it out right next to there as I'm typing, without making any requests to the server. So this is all in the client, nothing happening on the server. There's no network traffic. And this means we can do things like show the localized name of the locale that the user typed. So for example, let's say the user types en in here. We want to show English as the locale. We can do this using another INTL API, an Intel API called intel.locale. I did not think this through with my names of variables, but that's fine. And actually, we only want to do this if locale is not empty. So what we're going to do is say if locale exists, then set this to locale. Otherwise it'll be undefined or an empty string actually. Let's do this differently. Let's do like this. Okay, so if locale is defined, it'll get the intel.locale value for it.

66. Handling Locale Formatting

Short description:

Otherwise, if the locale exists, we can print out the formatted version of the locale's language. However, if an incorrect locale is provided, an error will occur. To handle this, we can use a try-catch block and simplify the code.

Otherwise it'll be null. What we can then do is we can create an intel. What is it? Display names formatter, which is going to have its locale be set to English and its type be set to language. And we're going to find that up here. And we can then do if locale exists, we can print out locale format of the loc dot language. So what this will do is if I type in e n. If I type in e n. In theory if I type in, it's not working. Why it's not working. Incorrect locale for now. Sorry can you say it again. Incorrect locale information provided. Ah yes right there it is. Oh because okay I know why. So this will actually happen if I just enter e for example. So e is not a valid locale. So it will try to do this locale format that of with the language and it will fail because e is, like it doesn't know what the formatted version of the e locale is because it doesn't exist. So what we can do instead is we can do a try-catch. We can do lets um let's see so language is a string. And if locale exists we will we can so simplify this I completely didn't realize, let's get rid of this.

67. Client Interactivity and Partialization

Short description:

If locale exists, we create our intel.locale and try formatting the language. We print out the language if it exists. No questions about client interactivity and islands. Partialization is a popular concept for static JavaScript components.

Okay, if locale exists, we create our intel.locale. Then we try language equals locale format Phone equals locale format.of loc.language. We didn't catch this if it fails and just completely ignore it there. Actually this probably seems anywhere near and apparently this can be string or defined, right? And default is undefined. And then we can say if locale exists, we just print out locale where we just print out the language there we go. There we go, okay, much better. Let's put this into a PL so it's underneath. And so if I type in EN, it'll print out English. If I print in JA it says Japanese. If I do PL it'll be Polish so on and so forth. Okay, very nice.

Are there any questions about this so far? I've been talking for a while. No question about client interactivity and islands. I've heard that people refer to it as partialization. Yeah, it's pretty much. Everything is static and new grade lines of JavaScript. I think it's pretty nice. Yeah, exactly, partialization. Yeah, that's pretty much the same concept of components, yeah. It's been getting more popular recently.

68. Rendering and Client Interactivity

Short description:

When rendering on the server, the component that's not on the island is also rendered. The form is rendered on the server and replaced with the client interactive version on the client. Conditional rendering can be used to render content only on the client or server based on the browser or server environment. On the reference page, entering something will provide a sample of the date or time.

When we render this on the server side what's going to be sent as the component that's not on the island? Is it going to be an empty thing? No, it'll actually render the component on the server as well. If I look at this and, okay this is not very nicely formatted, is it? How am I going to demonstrate this? Well, I guess I could just scroll through here a little. You can actually see that somewhere over here, this is the server side render and on the server side render, this is where the island starts. The form is also rendered on the server and it goes up to here. And then on the client, everything from this comment up to this comment here is replaced client interactive version of the island. If you want this, you can also, for example, say if I'm not, if not his browser is browser is something you can import from fresh runtime. Fresh runtime. If you are in a browser you can return like an empty div. And then what you will see happens is it apparently says empty and then it says the form. Okay, I don't know. Something is broken here. Yeah, you can do like conditional rendering from only on the client to only on the server depending on how you want to use things like detecting if you are in the browser or on the server. Does that answer your question? Yep. Great. Any other questions? Does not look like it. Cool. Then let's add one more thing, which is on the reference page when we enter something in here, it'll give you a sample of the date or the time, not just the language name. Let's do the same thing. We're going to change this to be a description list. Then we're going to have a DD is the name, so that would be a language, and the DT.

69. Setting Book and Time Sample

Short description:

We set the book to be DLTTTT and created a time sample using a new date. We formatted the time sample using a date time formatter with the full date style. After some styling adjustments, the time sample is now displayed in German with improved styling.

I think that's the book by Lee Rant. I never remember. I think it's DLTTTT. Probably.

And then let's do the other one which was time sample. We're going to set this to be time sample. And to do this time sample, we're going to create a new date, which is what we're going to use to show the time sample. And we're going to create another let time sample. And if time sample is equal to, we need to create a date time formatter. Intel.dateTimeFormat locale, with the option being date style full. You can do dateFmt.format with the date. You pass that here, and if we now look at the page, it will show german, and it is not showing time sample. Why is it not showing time sample? Do you need to provide a locale? Yeah. I didn't have this in quotes here, so that was a problem. Okay, so that works now. Let's quickly do some styling on this to make it look less terrible, and actually wrap this in a thing to make it only show up if the language in Time Sample actually exists. So if it exists, it'll show it, and if it doesn't, it doesn't. Um, yeah, so that works. Let's... make the styling a little nicer, font bold, cool, and that works, and let's add some padding on here as well.

70. Client-interactive Component and Deno Features

Short description:

We now have a client-interactive component that can perform actions on the server and persist state. It can be deployed globally using Deno Deploy or other methods. Deno also has a built-in linter and a deno-task feature similar to NPM run, which allows you to define tasks and configure options.

Okay, so we now have a client-interactive component which does things on the client. If I press the Save button it does things on the server, it persists state on the server here that is then accessed later when I render other pages. This can be deployed globally, time sample, very quickly, using Deno Deploy or to any number of other ... using a Docker container or a VM or something like that.

Yeah, and that's pretty much the entire reference page or reference app we built now. I think, I hope I didn't miss anything. Which means we can now go into the extra stuff that I didn't think we'd get to because I thought this was already going to be too much. So do people, first of all, have any questions about the entire thing, about the entire process? Questions that you thought, like, might not fit in very well and other places? No questions. Okay! That is cool. Let's do some other things, then.

I'll demonstrate some more of what Deno is capable of. So I have previously shown off the test framework and the formatter. Deno also has a built-in linter. This linter is like ESLint or anything similar like that. If I put some invalid code in here, which is, like, syntactically valid but logically not correct, for example, if true, why would you ever write this, you know lint is going to lint this and say you cannot have constant expressions in your if statements because what's the point, or you cannot have an empty if statement because what's the point. Like, this will also show up in VS Code through the Deno language server integration. So, yeah, something else which we've used, I've not showed you what it does though is deno-task. So deno-task is very similar to NPM run. You have this deno.json file which is an optional configuration file which let's you define tasks and default to which import map you want to use, if you want to use an import map. Also lets you configure things like formatting options and linting options.

71. Import Map and Editor Integrations

Short description:

You probably shouldn't though, just stick with the defaults. Can I ask a question related to the import map? Wouldn't it be super nice if it was just automatically read it from the import map? Yes, I agree this would be fantastic. We are, actually, there's an open issue about it. But the person who was going to work on it is currently on vacation. So, he'll be back I think at the end of this week. And then we'll hopefully have it in a couple of weeks here. But I agree that would be really, really nice. The editor integrations consist of multiple parts, such as the extension for VS Code, Deno LSP, and the Deno binary. This allows us to reuse the same code for all of our editor extensions.

You probably shouldn't though, just stick with the defaults. So I can have a task here which is my start task, so deno-task.start is just the same as running this command. Let's see what other things that I did not cover.

Can I ask a question related to the import map. It's not strictly related to import, but every time that you VS Code extension, you auto-imported stuff. You needed to change it to be the alias. Wouldn't it be super nice if it was just automatically read it from the import map? This is something that should be contributed at the extension.

Yes, I agree this would be fantastic. We are, actually, there's an open issue about it. But the person who was going to work on it is currently on vacation. So, he'll be back I think at the end of this week. And then we'll hopefully have it in a couple of weeks here. But I agree that would be really, really nice. And the other thing about where one would contribute to this is, the editor integrations consist of multiple parts. So, it's one, the extension for VS Code that's written in JavaScript, or in TypeScript actually. But it really uses the Deno LSP under the hood, the Language Server. And Deno LSP is it essentially does all of the editor completions things that you've seen, and the linting and formatting integration, everything like that is contained within the Deno LSP. This is written in Rust and it's part of the Deno binary. And it allows us to reuse the same code for all of our editor extensions. So, the Vim extension and the Nano, or I don't know if there's Nano extension, but like Vim and Emax and VS Code and probably Visual Studio.

72. Remote URL Imports and Documentation

Short description:

We support auto-completions of remote URL imports for certain registries. You can import from Deno land and choose from STD or slash X. It provides information on GitHub stars, name, and description. Searching is also possible. It dynamically auto-completes imports and filters out unnecessary files. You can click on imports to be redirected to the corresponding page or documentation.

IntelliJ, exactly. Yeah. They all use the same Deno LSP. It makes total sense. Actually, another cool thing that I didn't actually demonstrate, but also related to imports, which is super cool, is we support auto-completions of remote URL imports for certain registries. So, for example, if you want to import something from Deno land, I can do slash here and it'll ask me if I want to import from STD or from slash X. Let's say I want to import from slash X. It'll give me all of the things on STD here with the amount of GitHub stars they have, the name, and the description. I can actually search here as well. For example, I want to import fresh has like two and a half thousand stars. You click on here to be sent to Deno land X. I can press slash again and it'll auto-complete all of the things inside of the directory here that are code fonts that you could import. Totally dynamically. It does request while you type? This is super impressive. Yeah, it's really, really great. It filters out things like, I don't know, you probably don't want to have underscore test files complete here, right? It filters those out and you can do this and then import stuff from here. It's pretty cool. You can even click on, let's see, so I'm going to import, let's import something else, like OAK as a middleware framework for Dyno. I can hover over this, click on docs and then I'll be sent to DocDynoland, which is an automatic documentation generator for any module on the internet which uses ECMAScript module syntax. It will automatically generate all this documentation based only from the js.comments in your source code.

73. Documenting URLs with Deno LSP

Short description:

You can use the Deno LSP to document any URL. The documentation for PREACT and Deno built-ins is extensive. The LSP provides warnings and quick fixes for importing specific versions. The deno doc command is also supported on the command line.

And you can actually do this with any URL, just enter a URL in here and it'll document it. For example, if I want to get the documentation for PREACT, click on it and the PREACT types actually don't have any nice type comments. So, this is not a very nice documentation, but you get the point. And this is actually the same documentation we use for our Deno built-ins as well. So, this is like all the stuff built into Deno and this is like very extensive documentation. Wow, this scroll is a lot longer than I thought. Yeah, and there's some other cool things, like for example, I imported oak here without a specific version. It'll give me a warning here that I should really be importing a specific version and it'll even give me a quick fix. So, if I press command dot, it'll say update specifier to redirected specifier and if I do that it'll update to the latest version. So, yeah, there's a bunch of really cool DX stuff built into the LSP. And also, deno doc by the way, also supported on the command line. So, if you want to like, you know, doc, I don't know, you tilt slash post.ts and like give me the documentation and if I would actually write some documentation here. Let's see, lists all posts in the data directory and run do you know doc again, it will pull this out and show here.

74. Inspiration from Rust and Go for Formatting

Short description:

Deno FMT is inspired by Go's formatting style, which prioritizes consistency. It ensures that code looks the same everywhere and is easy to work with, even if there are disagreements about specific formatting choices.

It seems like influenced by cargo, this kind of stuff. Yeah, it's actually, yeah, we've taken a lot of inspiration from other modern languages like Rust and Go. Like the built in formatting in Deno FMT is very much inspired by Go in the sense that there's very little configuration options. We want everyone to use the same formatting configuration. Like there's this saying in the Go ecosystem where Go FMT is nobody's favorite, but it's everyone's favorite because it makes everything look the same everywhere. And it's really easy to get started with a new project because the code looks exactly how you expect it to look, because everybody uses the same formatter. Even if you might not agree about how it does a tab indent somewhere or how it line breaks something. But just because it uses the same formatting for everywhere, that's what makes it so nice.

75. Understanding VPO and Using Adno for CLI

Short description:

Because of VPO, the JavaScript world is understanding this concept more and more. Linting rules are inspired by ESLint, and there's a website called lint.venoland that shows all the rules. Adno is great for CLI applications and works even better than for front-end. There are cool frameworks like Cliffy for CLI tooling.

I think that because of VPO, the JavaScript world is understanding this concept more and more. Like same goes for linting as well, right? Like lint, all the rules are mostly inspired by ESLint. There's this lint.venoland website which shows all the rules. This page is very inspired by Rust's Clippy page. Let's see if we can find it. Clippy Rust. This is the one here. By this page where you can, like, see all the worlds. And we have something really similar here. All the worlds.

Yeah. If I want to create a CLI application, can I still use Adno? Yeah, definitely. So Adno you can use for, I've been mostly showing it for front-end stuff here, but I actually think that Adno works probably like 50% better for like CLI applications than it does for front-end. Like it's really fantastic for front-end, but it is like very, very, very, fantastic for CLI applications. Because like on CLI, you have issues with like dependency and the same issues that you have with NPM, you can also get into with, you know, with like duplicate transitive dependencies or whatever. But with CLI it's like, it's fantastic. There's a bunch of really cool frameworks for like CLI things. For example, Cliffy is one of them. If you've ever used clap and Rust, it's like a framework for CLI applications, which makes it really easy to create like nice CLI tooling, which like has automatic like, like it automatically generates completions for all the editor for all of the shelves and stuff like that. Yeah, cliff is a really, really awesome, like CLI framework that you can use.

76. Choosing Between Deno and Rust for CLIs

Short description:

Regarding CLIs, the choice between Deno and Rust depends on the specific use case. Deno is suitable for extensible and rapidly changing tools, while Rust is preferred for tasks like building a JavaScript runtime. Deno's WebAssembly support allows for seamless integration with Rust. The stock.deno.land website, for example, utilizes Rust compiled to WebAssembly for parsing JavaScript files. The combination of Deno and Rust offers exciting possibilities.

So regarding the question about CLIs, I know that it depends. But for your next CLI, would you use Dino or Rust? It depends. So, yeah, it really depends on what I'm trying to do, right? If I'm trying to write something which is extensible to the user, and like, needs to meet, I need to be able to change it very quickly, I will I would use Dno for sure. But if it's like, like a CLI can be many things, right? Like if I'm writing a CLI, which is like a tool for deploying some code to AWS, for example, I'd write that Dno. But if I'm writing a JavaScript runtime, right I don't want to write a JavaScript runtime in JavaScript that is not going to be fast enough. So I would write that in Rust. But yeah, for pretty much anything which isn't like, it is where it is not the number one important priority that it is the fastest thing on the planet, I would use Dno. Yeah. But Rust is also really awesome. And you can actually use Dno, or Rust inside of Dno very well using WebAssembly. Like Dno has awesome support for WebAssembly. And you can compile Rust to WebAssembly very easily. We actually make a lot of use of that. For example, like the stock dot Dnoland website. The thing that pulls out the documentation out of your JavaScript files. It's written in Rust, and it's compiled to WebAssembly. But this website runs on Dno Deploy. So like there's a WebAssembly thing on Dno Deploy, which parses out your source code as it comes in and then uses JavaScript to render out the page using JSX. So there's like some really nice synergies that you can get there. That raises another question.

77. Running WebAssembly with Rust and TypeScript

Short description:

Is the website? Yes, there we go. The entry point is a TypeScript file, and this TypeScript is running inside of V8 as is. But it calls into a Dnodoc crate or Dnodoc library, which then in turn imports in WebAssembly. So this is like JavaScript importing WebAssembly. It is not like WebAssembly like running JavaScript inside of an engine inside of WebAssembly or something like that.

Is this thing running on WebAssembly written in Rust and then deployed using the Rust code? Or is it first compiled to WebAssembly and then deployed as WebAssembly to Dno Deploy?

Do you mean, you know, Doc specific, like doc.dnoland specifically? Yeah. Or, okay, yeah, so doc.dnoland, I'll just pull it up and show you. It's all open source. Let's see. Is the website? Yes, there we go. Okay, so, the entry point is a TypeScript file, and this TypeScript is running inside of V8 as is. But it calls into a, let's see here. It imports this Dnodoc crate or Dnodoc library, which then in turn imports in WebAssembly. So this is like JavaScript importing WebAssembly. It is not like WebAssembly like running JavaScript inside of an engine inside of WebAssembly or something like that. It gets JavaScript driving the WebAssembly, not the other way around. I don't know if that was the question you asked.

78. Collaboration with Rust and TypeScript

Short description:

No, you have to compile the rust WebAssembly yourself. We have a tool to make it easy. Dnodeploy does not have a build step, but you can use GitHub Actions to run a build step. We have integration with GitHub Actions and a deployctl tool for that.

No, my question was, let's say I want to have collaboration between Rust and TypeScript. Can I just rely on DinoDeploy to make everything work or do I have to pull it as a separate package like what you did?

Sorry, I misunderstood the question. No, you have to compile the rust WebAssembly yourself. So this module here, we have actually a tool to make it really easy to do that, I'll show a demo of that in a second. But this has your rust code inside of this source folder, I think, yeah, so this is all the rust code. And we run this WASM build tool, which takes your rust code and turns it into a WebAssembly file plus a D.TS file. It generates TypeScript types from the rust code and just outputs it with some JavaScript root code. So you can write your code in rust and run the Wasm Build Tool and it will generate a JavaScript API wrapper for the rust. But you have to run this manually. This is not automatically run by dnodeploy.

Cool, thank you. Which actually brings up another point like dnodeploy does not have any build Step, right? Like you deploy something as is from your repository, which is great for many things. Sometimes you want to deploy stuff with a build Step. So if you want to do that, we have integration with GitHub Actions so you can run your build Step inside of GitHub Actions, which most people are already familiar with, and you can just, let me actually show you that. Do I have an example of that? Actually, even in your example, I guess the tests are not running at the moment, right? They're not running at CI. Let's do that in a second. Let's add a GitHub Actions file to run tests on CI. That's a great, great point. So yeah, we have this deployctl tool here. You can essentially do this inside of GitHub Actions.

79. Setting Up Deno Deploy and GitHub Actions

Short description:

You can deploy your project to Deno Deploy by simply entering your project entry point. No token or additional configuration is required. Setting up GitHub Actions is a great idea as it supports Deno out of the box. Simply configure the actions, choose the desired version, and specify the necessary tasks such as formatting, linting, and running tests. By using Deno Deploy without a build step, deployments can be faster compared to other providers that require spinning up a VM for the build process. However, it's important to note that having tests is crucial for real-world projects.

Which is just like, you enter your project entry point and it deploys it to Deano Deploy. And you don't need to specify any token or anything for that.

Okay, so let's, yeah, let's set up GitHub Actions. That's actually a great idea. So if I go to this Actions tab here, GitHub obviously is very awesome. And just supports Deano out of the box. So you can just press configure right here. And let's just use the latest version. Because I trust the authors surprisingly. And let's verify formatting, run the linter, run the tests. We don't need unstable. And let's change the name here. Commit it. And see if that runs.

There we go. There you go. Yeah. So, this is actually the reason why we don't have a build step in DENO Deploy by default, because, like, we usually, without a build step, we're usually able to deploy before most other providers have even spun up, like, a VM to do your build step in. And we don't need to spin up a VM or anything like that. Yeah, and it's super impressive, it's just not realistic for a real-world project without tests.

80. Running Tests and Deploying with Pull Requests

Short description:

We use pull requests for everything. When a user pushes something, we deploy it immediately and run tests in parallel. The PR can only be merged once the tests and deployment are complete. This allows for quick preview deployments and ensures that the VM is properly executed before merging the PR.

Well, actually, you would say that. Well, so, yes. Like, you will still need to run your tests at some point, right? So the way we do this, for example, in dno.land, this also open source, let's get to this repo. So this is a fresh project. And it has tests. And these tests run inside of GitHub Actions. And what we do is, we use pull requests for everything. So, when a user pushes something, let's actually not use that one, let's do this one here. When a user pushes something, we will deploy this immediately, and then in parallel run the tests. And you can only merge this PR once the tests are complete and the deployment is complete. So this means we can create the preview deployment really, really quickly. So, once you open the PR, you have a preview deployment in like five seconds. But before you can actually merge the PR, you still need to wait 10 to 15 seconds to actually run the VM.

81. Promoting Deployments and Running Live Tests

Short description:

When deploying the master branch, we essentially promote the deployment by creating a new deployment with the same source code and environment variables. This is an optimization to avoid unnecessary rebuilding. We also run live tests on preview deployments using actual production infrastructure to ensure accurate results. The GitHub Actions run completed in 10 seconds.

Wait, so do you promote this version or just create a new deployment when you're deploying the master branch? When we deploy the master branch, we create... So, we essentially promote the deployment. Technically it is creating a new deployment with the same source code, and the same environment variables, and the same everything. But, yeah, essentially we can think of it as promoting the... Yeah, it's just an optimization to not build it again, but you don't need to build... Yeah, but we don't need to build it, so like... So, that's just an optimization, doesn't matter. Yeah.

So, actually I'm doing the same in regard of not running tests on the master in order to get faster deployment. I understand this. Yeah, exactly. So, one actually really cool thing that we do as well, or you can do at least, we do this for the Fresh project, is that we can run live tests on your preview deployments. So let's say you have a PR here, I have this PR for example, and I have my preview deployment here. This Lighthouse test runs on my preview deployment, it runs on live production servers. And all of the live production setup. So like, I can run my tests on actual production infrastructure to validate that everything works, rather than just running it into an isolated VM on like some system, which may give you better, more accurate test results than- That's the only way to do performance like lighthouse, like you did for sure. Let's go back to my GitHub Actions run. I think it completed, right? I was like, did it pay any attention to it anymore? Yes, it did. Okay. Yeah, so 10 seconds.

82. Benchmarking Performance with Deno

Short description:

Deno is really fast and performs well in tests, linting, and formatting. Benchmarking works similarly to testing, where you create a file with a 'Bench' suffix and register a new benchmark using 'dino.bench'. By running 'dino bench', you can measure the time it takes to execute a specific function. The benchmark runs until the standard deviation is low enough to be considered accurate. This can be used to compare the performance of different approaches.

Deno's like, did I mention Dino's really fast? Yeah, ran all the tests. I checked Linting, and Formatting, and everything's fine. Cool, is there anything else that you want to talk about? Or want me to demonstrate, which I talked about earlier. No, okay. We still have 20 minutes. I can run through a couple more of the commands here, or we can just be done at this point. Maybe let me run through the benchmarking thing real quick, because I had alluded to that earlier. So benchmarking works the same way that testing does, in that you create a file called Something underscore Bench.ts. And you can call dino.bench here to register a new benchmark. Works exactly like testing. List posts. And this function is the function that you're benchmarking. So we can just list. Let's see, list posts. And if I run dino bench, it'll benchmark it and give me the result of how long it takes to run this, which is apparently 97 microseconds, okay. And it does this, yeah, it runs the test until the standard deviation becomes low enough that it considers it to be accurate. So it's pretty accurate, it's the same way that gotest works. And I don't feel like we're writing this to be like the serial way but I could use this to prove that the parallel way is faster than the serial way, for example.

Watch more workshops on topic

JSNation 2023JSNation 2023
174 min
Developing Dynamic Blogs with SvelteKit & Storyblok: A Hands-on Workshop
Featured WorkshopFree
This SvelteKit workshop explores the integration of 3rd party services, such as Storyblok, in a SvelteKit project. Participants will learn how to create a SvelteKit project, leverage Svelte components, and connect to external APIs. The workshop covers important concepts including SSR, CSR, static site generation, and deploying the application using adapters. By the end of the workshop, attendees will have a solid understanding of building SvelteKit applications with API integrations and be prepared for deployment.
JSNation Live 2021JSNation Live 2021
156 min
Building a Hyper Fast Web Server with Deno
WorkshopFree
Deno 1.9 introduced a new web server API that takes advantage of Hyper, a fast and correct HTTP implementation for Rust. Using this API instead of the std/http implementation increases performance and provides support for HTTP2. In this workshop, learn how to create a web server utilizing Hyper under the hood and boost the performance for your web apps.
GraphQL Galaxy 2021GraphQL Galaxy 2021
161 min
Full Stack GraphQL In The Cloud With Neo4j Aura, Next.js, & Vercel
WorkshopFree
In this workshop we will build and deploy a full stack GraphQL application using Next.js, Neo4j, and Vercel. Using a knowledge graph of news articles we will first build a GraphQL API using Next.js API routes and the Neo4j GraphQL Library. Next, we focus on the front-end, exploring how to use GraphQL for data fetching with a Next.js application. Lastly, we explore how to add personalization and content recommendation in our GraphQL API to serve relevant articles to our users, then deploy our application to the cloud using Vercel and Neo4j Aura.

Table of contents:
- Next.js overview and getting started with Next.js
- API Routes with Next.js & building a GraphQL API
- Using the Neo4j GraphQL Library
- Working with Apollo Client and GraphQL data fetching in Next.js
- Deploying with Vercel and Neo4j Aura
Vue.js London Live 2021Vue.js London Live 2021
116 min
Building full-stack GraphQL applications with Hasura and Vue 3
Workshop
The frontend ecosystem moves at a breakneck pace. This workshop is intended to equip participants with an understanding of the state of the Vue 3 + GraphQL ecosystem, exploring that ecosystem – hands on, and through the lens of full-stack application development.

Table of contents
- Participants will use Hasura to build out a realtime GraphQL API backed Postgres. Together we'll walk through consuming it from a frontend and making the front-end reactive, subscribed to data changes.
- Additionally, we will look at commonly-used tools in the Vue GraphQL stack (such as Apollo Client and Urql), discuss some lesser-known alternatives, and touch on problems frequently encountered when starting out.
- Multiple patterns for managing stateful data and their tradeoffs will be outlined during the workshop, and a basic implementation for each pattern discussed will be shown.
Workshop level

NOTE: No prior experience with GraphQL is necessary, but may be helpful to aid understanding. The fundamentals will be covered.
React Summit 2020React Summit 2020
162 min
Zero to Production with Fullstack GraphQL and React with Hasura team
Workshop
Hasura is a free and open-source GraphQL Engine that can help supercharge your GraphQL adoption, whether it is for a new application or for an existing one. The workshop will cover:
- A basic introduction to GraphQL- Using GraphQL to make CRUD operations from a React application- Setting up access controls to data- Building real-time components in React using GraphQL Subscriptions- Wrapping existing REST APIs with GraphQL servers that can be deployed on serverless platforms, and then merging them into a single endpoint using Remote Schemas- Triggering serverless functions on database events- In short, we’ll go through how React developers can adopt realtime GraphQL and serverless.
React Advanced Conference 2021React Advanced Conference 2021
67 min
Build Fullstack Apps in Record Time with Blitz.js
Workshop
Blitz.js is the Fullstack React Framework. It's heavily inspired Ru on Rails and is focused on making you as productive as possible. It's built on Next.js and adds all the missing pieces you need for building a fullstack app with a database. By far the biggest innovation of Blitz is the new "Zero-API" data layer that abstracts away the API so you don't have to mess with REST or GraphQL APIs!

Simon will introduce all the important parts & guide you through getting started with Blitz, so you'll know if you might want to use it or not.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Remix Conf Europe 2022Remix Conf Europe 2022
37 min
Full Stack Components
Remix is a web framework that gives you the simple mental model of a Multi-Page App (MPA) but the power and capabilities of a Single-Page App (SPA). One of the big challenges of SPAs is network management resulting in a great deal of indirection and buggy code. This is especially noticeable in application state which Remix completely eliminates, but it's also an issue in individual components that communicate with a single-purpose backend endpoint (like a combobox search for example).
In this talk, Kent will demonstrate how Remix enables you to build complex UI components that are connected to a backend in the simplest and most powerful way you've ever seen. Leaving you time to chill with your family or whatever else you do for fun.
React Summit Remote Edition 2021React Summit Remote Edition 2021
43 min
RedwoodJS: The Full-Stack React App Framework of Your Dreams
Tired of rebuilding your React-based web framework from scratch for every new project? You're in luck! RedwoodJS is a full-stack web application framework (think Rails but for JS/TS devs) based on React, Apollo GraphQL, and Prisma 2. We do the heavy integration work so you don't have to. We also beautifully integrate Jest and Storybook, and offer built-in solutions for declarative data fetching, authentication, pre-rendering, logging, a11y, and tons more. Deploy to Netlify, Vercel, or go oldschool on AWS or bare metal. In this talk you'll learn about the RedwoodJS architecture, see core features in action, and walk away with a sense of wonder and awe in your heart.
React Day Berlin 2023React Day Berlin 2023
30 min
Javascript Should Come With Batteries
Setting up JavaScript projects is no fun. Getting started involves installing and configuring node, tsc, prettier, eslint, a testing framework, a database driver, and more. Why is JavaScript not batteries included? In this talk we'll talk about how Deno fixes this, letting you focus on building stuff. We explore what benefits full tooling integration unlocks, and remember how fun it is to program if your tools help you, rather than requiring your babysitting.
Node Congress 2023Node Congress 2023
36 min
Deno 2.0
Deno 2.0 is imminent and it's bringing some big changes to the JavaScript runtime. In this talk, we'll introduce the new features including import maps, package.json auto-discovery, and bare specifiers. We'll discuss how these improvements will help address issues like duplicate dependencies and disappearing dependencies. Additionally, we'll delve into the built-in support for deno: specifiers on the deno.land/x registry and its role in providing a recommended path for publishing. Come learn about how these updates will shape the future of the JavaScript ecosystem and improve backwards compatibility with Node applications.
Remix Conf Europe 2022Remix Conf Europe 2022
23 min
Remix Architecture Patterns
Remix provides amazing flexibility and can be deployed anywhere where JavaScript is running. But how does Remix fit into the bigger application landscape of an organization? Remix provides great utility, but how to best take advantage of it? What things should be handled inside of Remix, and what things are better off done elsewhere? Should we use the express adapter to add a WebSocket server or should that be a standalone microservice? How will enterprise organizations integrate Remix into their current stacks? Let’s talk architecture patterns! In this talk, I want to share my thoughts about how to best integrate Remix into a greater (enterprise) stack.