Fresh: a new full stack web framework for Deno

Rate this content
Bookmark
SlidesGithubProject website

Fresh is a web framework based on Deno and Web standards built to run on the edge

24 min
17 Apr, 2023

Video Summary and Transcription

Today's Talk introduces Fresh, a full-stack web framework for Deno, and covers its features, such as static files, routes, and data-fetching. It also discusses middleware, error pages, and styling options. The Talk explains the Islands architecture used by Fresh to enable client interactivity. The demo showcases the use of hybrid routes and islands components, and resources for Fresh, Deno, Preact, TypeScript, and web standards are provided.

Available in Español

1. Introduction

Short description:

Today we're going to talk about FRESH, a full-stack web framework for Deno. I work at Netlify. I'm from Montreal, Quebec, Canada. If you're looking for me online, I'm at NikkieTOnline pretty much everywhere. If you want to know more about me, visit IAmDeveloper.com. I also stream on Twitch and have a YouTube channel.

Hi, everyone. Today we're going to talk about FRESH, a full-stack web framework for Deno. Before we get started, a little bit about me. As mentioned, I work at Netlify. I'm from Montreal, Quebec, Canada. If you're looking for me online, I'm at NikkieTOnline pretty much everywhere. If you want to know a little more about me, you can visit my website at IAmDeveloper.com. I also stream on Twitch, so if that's something you're interested in, you can check out IAmDeveloper.live. I also have a YouTube channel that you can check at YouTube.IAmDeveloper.com. I am also not a big fan of spiders.

2. Introduction to Fresh and Deno

Short description:

Today we're going to cover what Fresh is, discuss web standards, and dig into the features of Fresh. Deno is a runtime for JavaScript, TypeScript, and WebAssembly (Wasm) that uses V8. It has built-in linter, code formatter, test runner, and interoperability with Node.js and npm. Deno follows web standards and is part of the WinterCG, a community group for API interoperability. To install Fresh, use the command 'deno run-A-R https://fresh.deno.dev project-name' and choose options like Tailwind and VS Code integration. Start Fresh with 'deno task start' in the project's root folder.

Alright, so what are we going to cover today? We're going to go over what Fresh is. We're going to discuss web standards, and then we'll dig into the features of Fresh. After that, there'll be a short demo, and then we can move on to questions and comments.

Alright, let's get to it. So, what is Fresh? Well, hold on. First, we need to talk about Deno. So what is Deno? Deno is a runtime for JavaScript, TypeScript and WebAssembly, or Wasm, that uses V8. For the web, it runs on the Edge. You can also use Deno to create command line interfaces, i.e. CLI's. It's got a built-in linter, there's a built-in code formatter, a built-in test runner, there's node.js interoperability via node specifiers, and there's also npm interoperability via npm specifiers and CDNs.

Alright, let's talk web standards. So, Deno uses web standards. For example, import maps, fetch, request, and response. Like the little drawing over there says, just look it up on MDN. Great docs, but also that's pretty much what you'll need to reference most of the time if you're working with fresh. As part of web standards, Deno is a part of the WinterCG. Web Interoperable Runtimes Community Group. It's a space to collaborate on API interoperability for JavaScript runtimes. Feel free to read more about the WinterCG at wintercg.org.

All right, so where were we? Assuming that you have Deno installed, getting fresh installed is pretty quick. You can just run the command that you see on the slide deck here. So that's deno run-A-R and then https://fresh.deno.dev and the name of your project. That was a lot to say. All right. The installation is pretty quick and you have a couple of options. You can choose Tailwind for Styles, go with VS Code integration via the Deno VS Code extension, and that's pretty much it. We'll go more into the styling story a little later in the talk. To start Fresh, we go into the root folder of the project in a shell and run Deno task start. We won't get into it in this talk, but Deno has a built-in task runner that you can configure via a deno.json file.

3. Introduction to Fresh

Short description:

Fresh is a full-stack web framework that runs on Deno. It provides TypeScript support out of the box and uses Islands architecture for client interactivity. Preact is used on the server and client side, offering JSX support. We'll explain Islands architecture in more detail later.

All right. So let's dig into what Fresh is. So what is Fresh? It's a full-stack web framework that runs on Deno. It's server-side rendered framework. It's got just-in-time rendering on the edge. It provides TypeScript support out of the box, and there's nothing to configure to get up and running. There's no build step. There's no JavaScript delivered by default. It uses Islands architecture for client interactivity. It uses Preact on the server and client side. And there's JSX support thanks to Preact and TypeScript. If you're unfamiliar with the amazing Preact project, it's a fast and smaller alternative to React with the same modern API. It only gets a mention here for now, but we'll dig into what Islands architecture is a little later in the talk.

4. Features of Fresh

Short description:

Fresh features include static files, routes and routing, and data-fetching. Static assets in the Static folder can be cached using the built-in asset helper. Fresh supports three types of routes: handler, component, and hybrid. File-based routing patterns are supported, but Fresh only supports server-side rendering. For data-fetching, handler routes require exporting a function that returns a response, while hybrid routes use the handler object to define functions for actions. Props in Fresh are accessed through the props.data property.

Alright, let's go over some of the features of Fresh. We've got static files we'll talk about, routes and routing, data fetching, middleware, error pages, styling, and then we'll get to Islands.

So first up we have static files. All static assets can be found in the Static folder. Static assets, aside from image and source tags, do not have cache headers set on them. You can set cache headers manually or you can use the built-in asset helper to automatically cache an asset for a year. Here's an example of the asset helper in action. So I have this on my style sheet. I'm using the asset helper and what happens is it generates a unique URL which is composed of the asset file path, followed by a query string with one key, underscore FRSH, underscore C with a value that is the build ID from the deployment. And you can see, as I mentioned, there also gets this in the cache control headers set in the response. So we have it there for a year.

Alright, let's dig into routes and routing. There are three kinds of routes. There's the handler route, which is typically for APIs, the component route, which is for pages, and then the hybrid route. And for hybrid routes, it's for pages that require handlers routes, for example, a login page or a search page. Like in other frameworks, routes can also be dynamic. You can see in the table here that I took from the fresh documentation, that there are many types of file-based routing patterns that are supported. One thing to note, though, is fresh only supports server-side rendering. So there is no concept of something like get static paths in Next.js or Astro since pages are never statically generated.

Alright, let's look at the data-fetching story. So for data-fetching, we have handler routes, which I just mentioned, and we can also use hybrid routes to handle data-fetching. For a handler route, all that is required is exporting a function that takes two arguments, a request and a context, and it returns a response. So in this case here, we have a handler which is using the, this is the jokes API that ships with the fresh demo site, and we can see here it's just generating a list of random jokes from an array, and then it's returning that as a response. For hybrid routes, we define functions for actions in an exported variable called handler. We name the async functions in the handler object after HTTP verbs. For example, get. As the handler route in the previous example returns a response, typically in a hybrid route, you'll want to return the result of the context render method. Think of context render as passing in server-side rendered props to the page being rendered. When I say props, I mean pre-act props or react props if you're new to pre-act. Looking at the example on the slide, one thing to note about props and fresh is that it's not props.movies, the props from context.render are always in the props.data property.

5. Middleware and Headers

Short description:

The array of movie props is actually props.data, not props.data.movies. Fresh supports middleware, and multiple middlewares are supported. An example is given where two middlewares are used for a dynamic movie route. The least specific middleware runs first, adding headers to the response. The result shows the cache control header and the headers provided by the middlewares.

So for example, the array of movie props is actually props.data, it's not props.data.movies. That's a lot of dots in that talk.

Alright, we're going to move on to middleware. Again, like other frameworks, fresh supports middleware. All our files are named underscore middleware dot ts and need to reside in the routes folder. Multiple middlewares are supported. Although Deno encourages you to use TypeScript, you could write the middleware in a JavaScript file.

Let's look at an example. Say I navigate to slash movie slash Top Gun. Since we're hitting a dynamic movie route, that means we're using two middlewares. One in the root of the routes folder and one in the movie subfolder. In the case of multiple middlewares, the least specific one runs first. In our example, that means the root middleware runs first. It adds an X-conference header with the value nodeCongress2023. Then the second middleware runs in the movie subfolder. It adds an X-movie-page header along with a cache header that caches the page for 60 seconds. If we navigate to slash-movies, we can see the two middlewares giving a nice little handshake there. We can see the result in the response is that we get the cache control header and we get the two other headers that the two middlewares provide.

6. Error Pages and Unknown Page Props

Short description:

We're going to talk about error pages and how you can define custom error pages in Fresh. The example given is a custom 404 page or file not found page. These pages are component routes with special props passed in. The unknown page props give you access to information like the URL of a page that was not found. For dynamic routes, you can use context.render not found to render the 404 page if the page does not exist. If you're on a page associated with a dynamic route and the page being loaded does not exist, context.render not found will pass the unknown page props to the 404 page.

So the X-conference and the X-movie-page. Okay moving on we're going to talk about error pages. Like other frameworks, you can define custom error pages. For example, the current slide shows a custom 404 page or file not found page. These are component routes but they have special props passed in. In the case of the 404 page, you have access to the unknown page props when the page is rendering. The unknown page props gives you access to things like the URL of a page that was not found. For a dynamic route, we call context.render not found to render the 404 page if the page does not exist. And just to bring it back to data fetching, when we were discussing data fetching, I mentioned that we need to typically return the result of context.render for hybrid routes. That still holds, but as I mentioned, if you're on a page that is associated with a dynamic route and the page being loaded does not exist, we return context.render not found instead, and context.render not found will then pass the unknown page props that we were talking about a second ago to the 404 page.

7. Styling Options and Future Development

Short description:

Fresh gives you the option to enable TWIN, a server-side rendered implementation of Tailwind. Modern CSS is a compelling choice, with nested selectors, CSS variables, and other goodies. Adding a build step for CSS tooling goes against the no build step promise of Fresh. We may see more server-side rendered implementations of Sass, Post CSS, etc. Scope CSS, like in Vue and Svelte, would be a great addition.

All right. Let's move on to styling. When I first talked about fresh, I mentioned that there was no build step. So how does that affect styling our apps? Fresh gives you the option to enable TWIN, a server-side rendered implementation of Tailwind. This is great if you use Tailwind, but if the projects you're working on don't use Tailwind, what are your options? First, I would say modern CSS is pretty awesome these days, so you could literally go with a good old style sheet. The fact that nested selectors are coming to browsers and we only have CSS variables and other goodies like has in CSS makes this a compelling choice. You could also add a build step that commits the build artifacts of your CSS tooling. You could accomplish this with a GitHub action, or some other form of automation. Although it would work, it doesn't feel right committing build artifacts to the code base, and it also goes against one of the promises of fresh, no build step. I think what we're going to see here is innovation from user land where we may see more server-side rendered implementations of stuff like Sass, Post CSS, etc, much like the TWIN project. Another thing that I would love to see come to fresh is Scope CSS. We have that in other frameworks like Vue, Svelte. I think that would be a great addition.

8. Introduction to Islands Architecture

Short description:

Islands enable pockets of interactivity in your site or application. Fresh uses islands architecture, coined by Jason Miller, the creator of Preact. Only the JavaScript for the islands is loaded when a page with islands loads. Fresh has two component folders: components and islands. Components in the components folder render server-side only, while components in the islands folder render server-side and revive client-side interactivity.

Alright, let's talk about islands. What is an island anyway? I mentioned it briefly when describing the architecture of fresh, but we'll dig into architecture a little more now. Long story short, it enables pockets of interactivity in your site or application. I'll leave this chunk of a blog post from Jason Miller up for a minute, but Jason Miller, one of the folks alongside Katie Seiler-Miller, coined the term islands architecture.

It's actually fitting that Jason is one of the folks that coined this term, as he's also the creator of Preact, which Fresh uses. In islands architecture, the goal is to ship mainly static HTML and then mark certain regions of the document object model, i.e., the DOM, as available to hydrate, or to use Fresh's terminology, revive. When a page with islands loads, only the Javascript for those islands is loaded. I mentioned that Fresh serves zero Javascript to the client by default. So, how do we enable the client-side interactivity that we're talking about in islands architecture?

Fresh has two component folders. There's a components folder and an islands folder. Components in the components folder will always render server-side only, even if you add client-side interactivity to them. And components in the islands folder will render server-side as well as once the page loads and the client-side interactivity will be revived. So, let's look at a classic interactive component – a counter. It has a couple of buttons, if you click plus one it increments the counter, and if you click minus one it decrements the counter. And in this case here, we have a couple.

9. Reviving Islands and Demo

Short description:

Components that are islands get rendered server-side by Fresh. The rendered markup for the islands is visible in the view page source. Fresh adds a script with the ID __FRSH_state, containing an array of props for each island. The revive function in Fresh takes the list of components and the initial state to correctly map the initial state. Island components in Fresh are rendered with HTML comments, denoted by FRSH followed by the component name and the array index of the island component state. This is an implementation detail managed by Fresh. The demo site showcases counter components and a movie list with middlewares running.

So, how do we revive an island? So, essentially what happens is the components that are islands get rendered server-side by fresh and if you were to do a viewpage source of the page that loaded you will actually see the rendered markup for that particular island or islands. Along with the markup that's rendered for those islands, fresh also adds a script of type application JSON with the ID underscore underscore FRSH underscore state as the ID and in there there is an array. In that array the first element is another array which is an array of all the props for every island that's in the page. The second element in the array is currently empty there, but that's for plugins. That's something we're not really going to touch on today but if you're interested in the plugins you can take a peek at the fresh documentation.

So as you can see here I have JSX for my counter and it starts off on the server side where I say I'm passing in a prop with the value three for the start prop and when it gets rendered on the page we'll see that the initial props for that particular component get loaded in the array I mentioned. So how does it get revived? So we have that fresh state that I just mentioned and then one of the things that fresh does is alongside the JavaScript related to that particular island it has a revive function and that revive function takes the list of the components so for example here the counter and it also passes in the first element of state which is all the props for all the islands as I mentioned. In the previous example there was only one island but if there was more than one island how does Fresh map the initial state correctly? So if we have a couple here we will see that they just keep getting added to the array but how does Fresh know that the counter with the start prop of 3 and the counter of start with prop 5 maps correctly to the array there.

We talked about denoting interactive regions in the DOM when talking about islands architecture. Island components in Fresh get server-side rendered as I mentioned but they also get rendered with HTML comments surrounding them. The comment is prefixed with the FRSH followed by the name of the component, colon and then the array index of where the island component state lives in the initial state for all islands that was initially loaded. Doesn't matter how many different island components there are or how many instances of each, the array index will be incremented in each HTML comment based on the order of the islands in the DOM. One thing to note is that this is just an implementation detail, you'll never need to manage this yourself, I just find it useful to understand the underlying technology.

Alright, we're gonna move over to a short demo. So I'm just gonna go ahead and move us over here. So what we have here is... I just made a small demo site here. It's got a few pages so the home here where we have some islands so I have three counter components here. They're each managing their own state. And I also have a list of movies. That goes to the movie route. And we can load up a movie, for example, like Top Gun. And if I open up the network panel here, let's refresh that again, and this has two middlewares running. And like I said, there was the first middleware in the routes root folder and we can see that it has the x-conference header that that middleware provides. And then the subfolder middleware adds the x-movie dash page header, as well as the cache control of 60 seconds. All right, so we can close that. And here's an example of a hybrid page. So I'm just going to add a movie. So let's say, Lord of the Rings. And I'll give it a rating of five, and I'm going to submit it.

10. Demo Wrap-up and Resources

Short description:

In this demo, we used a hybrid route with handlers to post new movies to the page. The demo used an in-memory database instead of a real database. The view page source shows how Fresh starts up, loads the counter JavaScript and styles, and caches assets for a year. The rendered code includes the components, Fresh state, and the revive method. The demo site is deployed at imdeveloper.com/fresh-demo, and the source code is available on GitHub. Additional resources on Fresh, Deno, Preact, TypeScript, web standards, and Dino's node compatibility can be found at imdeveloper.com/fresh.

So we're on the same page, and the hybrid route has handlers in it, in the handler object. And in this particular case, I'm passing a post, and that's what allows us to post back to the page and add the new movie here. For the purposes of this demo, I didn't use a database. It's just using a variable, so an in-memory database. Talking about data is totally out of scope of the talk, but just know that there's a lot of people working on the data problem on the edge.

All right, so we're just gonna kind of wrap up the demo here with... This is what the view page source looks like, of that first page with the three different colored citrus fruits. I've taken out a lot of things just so that I can scroll through it. The first thing is this is fresh just starting up here, that's what that script is. You can see the folder here is based on the build ID. And then we can see that it only loads my counter JavaScript, which is for the island. And then we have my style, like I mentioned, and it's using the asset helper, and it's giving it a unique ID, and it's got the... It's cached for a year, as I mentioned.

I just wanted to show briefly here, so this is literally the code that got rendered, minus I took out some SVGs for brevity, but we can see here, for example, that I have the first component here, it's denoted with the HTML comments, and then there's a second one, and a third one. Then we have the fresh state here, and we can see there, the three there. And then we have that revive method down here, and we can see that it's running here, and we have our counter component, and then the state we're passing in, which is the props for all that. And that's pretty much the demo.

Alright, let's move back to the slide deck. And yeah, if you're interested in the demo, it's deployed at imdeveloper.com/fresh-demo. You can also view the source code, it's at github.com/nickyt-online/fresh-talk-demo. And we'll just finish off with just some resources that I think you'll all find useful. So there's a bunch of stuff about fresh, Dino, preact, TypeScript, web standards, and also just some newer things with Dino like the node compatibility, and also a link to the WinterCG. For folks interested, the slide deck is available at imdeveloper.com/fresh. And that's pretty much it. We couldn't cover everything in about 20 minutes, but I hope this gave you all enough of a primer to get excited about fresh.

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

React Summit Remote Edition 2021React Summit Remote Edition 2021
33 min
Building Better Websites with Remix
Top Content
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
React Summit 2023React Summit 2023
32 min
Speeding Up Your React App With Less JavaScript
Too much JavaScript is getting you down? New frameworks promising no JavaScript look interesting, but you have an existing React application to maintain. What if Qwik React is your answer for faster applications startup and better user experience? Qwik React allows you to easily turn your React application into a collection of islands, which can be SSRed and delayed hydrated, and in some instances, hydration skipped altogether. And all of this in an incremental way without a rewrite.
JSNation 2022JSNation 2022
28 min
Full Stack Documentation
Top Content
Interactive web-based tutorials have become a staple of front end frameworks, and it's easy to see why — developers love being able to try out new tools without the hassle of installing packages or cloning repos.But in the age of full stack meta-frameworks like Next, Remix and SvelteKit, these tutorials only go so far. In this talk, we'll look at how we on the Svelte team are using cutting edge web technology to rethink how we teach each other the tools of our trade.
GraphQL Galaxy 2021GraphQL Galaxy 2021
32 min
From GraphQL Zero to GraphQL Hero with RedwoodJS
Top Content
We all love GraphQL, but it can be daunting to get a server up and running and keep your code organized, maintainable, and testable over the long term. No more! Come watch as I go from an empty directory to a fully fledged GraphQL API in minutes flat. Plus, see how easy it is to use and create directives to clean up your code even more. You're gonna love GraphQL even more once you make things Redwood Easy!
JSNation 2023JSNation 2023
28 min
SolidJS: Why All the Suspense?
Solid caught the eye of the frontend community by re-popularizing reactive programming with its compelling use of Signals to render without re-renders. We've seen them adopted in the past year in everything from Preact to Angular. Signals offer a powerful set of primitives that ensure that your UI is in sync with your state independent of components. A universal language for the frontend user interface.
But what about Async? How do we manage to orchestrate data loading and mutation, server rendering, and streaming? Ryan Carniato, creator of SolidJS, takes a look at a different primitive. One that is often misunderstood but is as powerful in its use. Join him as he shows what all the Suspense is about.
React Summit Remote Edition 2021React Summit Remote Edition 2021
43 min
RedwoodJS: The Full-Stack React App Framework of Your Dreams
Top Content
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.

Workshops on related topic

JSNation 2023JSNation 2023
170 min
Building WebApps That Light Up the Internet with QwikCity
Featured WorkshopFree
Building instant-on web applications at scale have been elusive. Real-world sites need tracking, analytics, and complex user interfaces and interactions. We always start with the best intentions but end up with a less-than-ideal site.
QwikCity is a new meta-framework that allows you to build large-scale applications with constant startup-up performance. We will look at how to build a QwikCity application and what makes it unique. The workshop will show you how to set up a QwikCitp project. How routing works with layout. The demo application will fetch data and present it to the user in an editable form. And finally, how one can use authentication. All of the basic parts for any large-scale applications.
Along the way, we will also look at what makes Qwik unique, and how resumability enables constant startup performance no matter the application complexity.
React Summit 2023React Summit 2023
106 min
Back to the Roots With Remix
Featured Workshop
The modern web would be different without rich client-side applications supported by powerful frameworks: React, Angular, Vue, Lit, and many others. These frameworks rely on client-side JavaScript, which is their core. However, there are other approaches to rendering. One of them (quite old, by the way) is server-side rendering entirely without JavaScript. Let's find out if this is a good idea and how Remix can help us with it?
Prerequisites- Good understanding of JavaScript or TypeScript- It would help to have experience with React, Redux, Node.js and writing FrontEnd and BackEnd applications- Preinstall Node.js, npm- We prefer to use VSCode, but also cloud IDEs such as codesandbox (other IDEs are also ok)
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.
Node Congress 2021Node Congress 2021
128 min
Learn Fastify One Plugin at a Time
Workshop
Fastify is an HTTP framework for Node.js that focuses on providing a good developer experience without compromising on performance metrics. What makes Fastify special are not its technical details, but its community which is wide open for contributions of any kind. Part of the secret sauce is Fastify plugin architecture that enabled developers to write more than a hundred plugins.This hands-on workshop is structured around a series of exercises that covers from basics "hello world", to how to structure a project, perform database access and authentication.

https://github.com/nearform/the-fastify-workshop
JSNation 2023JSNation 2023
66 min
Build a Universal Reactive Data Library with Starbeam
WorkshopFree
This session will focus on Starbeam's universal building blocks. We'll use Starbeam to build a data library that works in multiple frameworks.We'll write a library that caches and updates data, and supports relationships, sorting and filtering.Rather than fetching data directly, it will work with asynchronously fetched data, including data fetched after initial render. Data fetched and updated through web sockets will also work well.All of these features will be reactive, of course.Imagine you filter your data by its title, and then you update the title of a record to match the filter: any output relying on the filtered data will update to reflect the updated filter.In 90 minutes, you'll build an awesome reactive data library and learn a powerful new tool for building reactive systems. The best part: the library works in any framework, even though you don't think about (or depend on) any framework when you built it.
Table of contents- Storing a Fetched Record in a Cell- Storing multiple records in a reactive Map- Reactive iteration is normal iteration- Reactive filtering is normal filtering- Fetching more records and updating the Map- Reactive sorting is normal sorting (is this getting a bit repetitive?)- Modelling cache invalidation as data- Bonus: reactive relationships