🎬 Getting Started with Deno Fresh: Part II #
In this second post on Deno Fresh getting started, we see how Fresh islands of interactivity work. As well as that, we look how you can use Deno’s in-built testing in your Fresh app.
In the first part of the series, we saw how you can add web pages to your Fresh app using its file-based routing system. We extend on what we saw there, in this follow-up, to see how you can also add API routes as well as resource routes (serving an XML RSS feed, for example).
We started the first post looking at why you might consider Deno Fresh and saw how you can set up Deno on your system and spin up your first Fresh app. If you are new here, please do skim through the previous post if something here does not click.
We will start with a quick introduction to partial hydration and the Islands Architecture. Then we will see how Deno implements it’s opt-in Island components philosophy. Then we move on to look at testing and API routes.
🤔 Why Deno Fresh for Content Sites? #
Deno Fresh is a fantastic choice for content sites. Here we are talking about
blog sites, documentation sites and such like. In the near-recent past, statically generated sites
were the most popular choice for content sites, with something like GatsbyJS a common choice. The
reasoning behind choosing static generators over server-side rendered (SSR) ones were principally speed and security. There was a Developer Experience trade-off, though, which lay in the fact that sites took long
to build (as the static content was generated). However, the user got the benefit when the static
content could be served quickly from a global CDN
Deno Fresh is fast despite being a server-side rendered generator. Designed for the modern, serverless world, using Preact it generates pages on the server, adding JavaScript only where needed. That last little trick — shipping zero JavaScript by default — lets Deno apps load faster on the user device. On top, because fewer bytes need to be shipped to the user’s device, the device receives the page data quicker. This pattern, especially with the performance benefits of using Preact over React for rendering, is what makes Fresh a good choice for content sites. Typically, the parts of content site page which are JavaScript-heavy are distinct and isolated islands of interactivity. This is what we look at next.
🧵 10 More Deno Fresh Getting Started Tips #
Following on from the previous Getting Started with Deno Fresh post here are ten more Deno Fresh getting started tips.
1. 🏝️ Islands #
Deno Fresh ships zero JavaScript by default but makes islands available for when you need interactivity. A great example of an Island of Interactivity on a blog post page might be the social sharing buttons. Most of the page will be text (no JavaScript there). There might be a bit of JavaScript to control opening a hamburger menu or for a newsletter sign-up form.
Those instances will typically be isolated to small parts of the page, and in fact, by using the platform, we can remove JavaScript completely from the sign-up form. The Deno Fresh philosophy is only to ship JavaScript for those parts of the page which require it, rather than for the whole page. This means we ship less hydration code — the code which makes sure state is consistent for interactive parts of the page.
For this to work, we put our component which have state or interactivity in the islands
directory of the project, rather than the components
directory.
So returning to the social share buttons example, we can add that code to a ShareButtons.tsx
file, which looks just like any other React component:
Here, we are using the WebShare API with graceful degradation for devices which do not yet support it. To achieve that, we:
-
have a
useState
hook, assuming initially that the device supports the WebShare API -
add a
useEffect
hook to feature detect the WebShare API and update are support assumption is needed - show the WebShare icon when the API is supported but show manual Telegram and Twitter share button otherwise
All three of those points need JavaScript to work with to track state, to add interactivity, or both. This is definitely an island and to let Deno Fresh know it has to manage interactivity for us, we have to put it in the Islands directory.
2. 🚉 Use the Platform #
Deno Fresh leans heavily on Web APIs. Using these in your project you can work with Deno, limiting the use of islands and keeping your app lightning fast. For example, returning to a blog site, we can add a newsletter sign-up form without needing to ship JavaScript to the browser:
Here, the opening form
tag includes a method attribute set to POST
as well as an action. The action can be the pathname for the current page, fed in to this component
as a prop.
When the form is submitted, the device now sends a POST
request to
the same route. We just need to add a POST
handler in the Fresh code
for this page (much like we had in the Tweet Queue Deno Fresh route file example).
3. 🥊 Static Assets & Cache Busting #
You will have files like favicons and perhaps web manifests for PWAsstatic
folder. Deno will serve them for you from there. As an example, static/favicon.ico
will be served on your built site from https://example.com/favicon.ico
.
That’s not all Deno does for you, though! There is a handy cache busting feature, even for these static assets! What it does is add a hash to the filename (under the hood). How is this helpful? If you change a favicon (lets say you go for a different colour) and you keep the same file name as before. Deno will recompute the file hash, this will be different to the previous one.
This means browsers and CDNs will know to download the new coloured favicon instead of serving the cached one. To make it work:
-
Import the
asset
named import function from$fresh/runtime.ts
. -
Wrap the asset URL in a call to the
asset
function when you add the favicon, image, CSS or other asset in a link tag: - how Deno Fresh islands work
- examples of using the platform with Deno Fresh
- an introduction to more advanced Deno Fresh features like WASM, API and resource routes
What are Deno islands of interactivity, and what is partial hydration? #
- For many sites, especially content sites (typical blogs, brochure, and documentation sites), very little JavaScript is actually needed. In fact, it will often be found in isolated corners of the page. Examples are an interactive image carousel, share buttons or a hamburger menu. Hydration is the process of converting the HTML for these little interactive enclaves and making their state consistent. In fact, it is only these islands which require hydration, the rest of the page will just be plain HTML and CSS. That said, traditionally, servers shipped JavaScript for hydrating the entire page and not just the little sections where it is actually needed. Partial hydration and islands architecture are a smarter, more modern approach. We ship zero JavaScript by default, and only ship JavaScript for the islands (which opt-in to hydration).
Does Deno Fresh support resource routes? #
- Typically, most pages on your site will serve HTML content. However, you might want to serve a PDF brochure or even an XML RSS feed. This is where resource or non-HTML routes come in. To add one to your Deno Fresh project, just create a TypeScript file in your project `routes` directory. Remember, this follows the file-based routing system. So put the handler function for your PDF brochure in `routes/brochure.pdf.ts` and this will be served on your production site at `https://example.com/brochure.pdf`. Write the handler, which looks a lot like the handler on an HTML route, returning an HTTP Response. Remember to set appropriate headers for the data you serve (`Content-Type: application/json`, for example, on a JSON route).
How can you create a Deno Fresh handler for an API route? #
- For API routes, the handler will not look too different to what you have on a page route. There are a few things to remember, though. Routing works just like for pages, and the API route file can go in the routes directory. As an example, create `routes/api/index.ts` to listen on the `https://example.com/api` route. You can add `GET`, `POST`, etc. handlers there. You will return a Response object instead of `context.render` (as you do for a page). Remember to add in any headers in the Response constructor. For example, on a `routes/api/data.json.ts` route, you will probably want to serve a `Content-Type: application/json` header.
4. 💄 Styling #
You can choose Tailwind for styling in the interactive prompts when you initialize
your project. If you prefer vanilla CSS then, of course, Fresh handles that too!
You can even self-host fonts! Add the CSS in the static
folder (like we mentioned for favicons above). Then just remember to include it in the Head
for your page or layout component markup (again just like for favicons):
If you do want to self-host the fonts, there is a handy Web Fonts helper which generates the CSS and lets you download the .woff2
files
you’ll need.
5. 🪚 Tooling #
We mentioned earlier that there is no need to spend time configuring ESLint or Prettier in each Deno Fresh project you start. Deno comes with its own linter and formatter, all with sensible defaults. To run these from the command line, just use:
To have VS Code format on save, see the VS Code config in the quick tips in the previous Getting Started with Deno Fresh post. If you are a Vim person, try the denols LSP plugin for Neovim .
👀 TypeScript #
Nothing to see here. TypeScript support comes out with Deno out-of-the-box: no need to add a tsconfig.json
or typescript-eslint
config. Just start coding in TypeScript.
🗳 Poll #
6. ☑️ Testing #
Just like linting and formatting, Deno has testing built in — there is 0
test runner config. That said, you might want to set up a test script, just to fire off tests
quicker. Update your deno.json
file to do this:
Then to rattle off tests, in the Terminal type:
If you have already used Jest or Vite, then there are no surprises when it comes to writing the
tests themselves. Import assert
and friends from std/assert/mod.ts
and you are already at the races!
See more detail on end-to-end testing with Deno Fresh in the dedicated post.
7. ☎️ API Routes #
You might use API
The actual API route file is essentially just a regular route file handler. You can name the
file with a .ts
extension in the API case though. Here is an example
where we send SMS message via the Twilio API from a Deno Fresh API route:
Although we put the file in an api
subdirectory this is by choice
and not necessary.
Notice the Deno way of Base64 encoding BasicAuth parameters. We import the
encoder in line 2
, then use it in line 18
to generate Base64 string which we need to send in the Basic authorization header in line 29
.
The rest just uses the same JavaScript APIs you already learnt from MDNResponse
object. You can just as easily return a redirect or server error.
For convenience, there are also Response.redirect
and Response.json
, providing a spot of syntactic sugar:
That last one adds extra convenience, saving you having to construct the customary headers manually.
8. 💰 Resource Routes #
You might use resource routes to serve PDF files, JSON data or even an RSS
9. 🧑🏽🔧 Middleware #
Middleware or Edge Functions in Deno let you intercept incoming requests and run code snippets on them before proceeding. Again, these are build into Deno Fresh. As well as intercepting the incoming request, you can effectively alter the response. See the video on Deno Fresh Middleware for more explanation on this.
Add a _middleware.ts
file to any folder containing route files you
want it to apply to:
10. 🦀 WASM #
WASM
Although the process is not complicated, we will not go into it here as there is an example with fully working code on using Rust WASM with Deno Fresh.
🙌🏽 Deno Fresh Getting Started: Wrapping Up #
We have seen a lot in the last two Deno Fresh getting started articles. I have tried to integrate my own learnings and take you beyond what is in the Deno Fresh docs. I do hope this has been useful, for you especially covering helpful details for anyone new to Deno. In particular, we have seen:
Get in touch if you would like to see more content on Deno and Fresh. Let me know if indeed you have found the content useful, or even if you have some possible improvements.
🏁 Deno Fresh Getting Started: Summary #
🙏🏽 Deno Fresh Getting Started: Feedback #
Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also, if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, then please consider supporting me through Buy me a Coffee.
Aimed this new Deno Fresh post at anyone new to Deno wanting to try the 🍋 Fresh approach to partial hydration.
— Rodney (@askRodney) January 20, 2023
You see static asset cache busting, in-built testing and a whole lot more.
Hope you find it useful!
#learndeno #usetheplatform #futureofweb https://t.co/JsERXYQHNf
Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter, @rodney@toot.community on Mastodon and also the #rodney Element Matrix room. Also, see further ways to get in touch with Rodney Lab. I post regularly on Astro as well as Deno. Also, subscribe to the newsletter to keep up-to-date with our latest projects.