Coding with Jesse

Know when to fold 'em

February 4th, 2020

The last few weeks on Twitch, I've been working on the user authentication to use for my side projects, including my upcoming course The Joy of Svelte.

I wrote it in a generic way as Express middleware. It uses a MySQL database with express-session and express-mysql-session. It doesn't have any HTML, it just adds a REST API to your server with some routes like /auth/signup, /auth/login, /auth/forgot and /auth/reset. This way, each of my sites can have a different UI, and use fetch to do everything.

Oh man, I wasted so much time when I was building it. I wanted to try building the whole thing with pure ES modules. ES modules are now supported in Node without a flag, so I figured it's finally time to use them without a build process.

I wasted hours trying to get it to work. Of course Mocha doesn't work with ES modules yet. Eventually, I gave up and went with using a library called esm that gets things working somehow. When I thought I was done, and tried to add it to a Sapper project. Since it was now in my node_modules, and being transpiled by Rollup, it all broke. Sigh.

I was facing another never-ending black hole of googling and debugging. So you know what I did? I went and rewrote the ES modules to CommonJS syntax. It took three minutes.

Lesson is, just because a new feature is available, you still have to wait for your entire tool chain to catch up and adapt as well. It's fun to push the envelope, but it can get exhausting too. Sometimes it's faster to cut your losses and take another route.

As Kenny Rogers warns, "You got to know when to hold 'em, know when to fold 'em, know when to walk away and know when to run."

If you're curious about the authentication middleware, you can see the source code here. It's not perfect for everybody, but if you want to use it and think I should publish it to npm and document it, let me know.

If you're excited about The Joy of Svelte, it'll be launching soon. Sign up for the Coding with Jesse newsletter, and you'll get a subscriber discount as soon as it's ready.


Interested in web development? Subscribe to my newsletter!

Statically generating a blog with Svelte + Sapper

December 18th, 2019

I've been working on rewriting my blog since forever. In fact, here's a video I made back in 2015 introducing codingwithjesse.com and my plans to rebuild my PHP blog using the latest and greatest web technologies. In 2015, this meant I was going to make a REST API with Node.js, and build a React frontend. So that's where I started.

Fast-forward three-and-a-half years, and the site still wasn't done. I hadn't spent that much time on it really, so it just had a REST API and an administration area for writing and editing blog posts. I had done a tiny bit of the public side using React but it was still in rough shape.

That's about the time I fell in love with Svelte and decided I wanted to use Svelte for everything. In July, I started migrating my blog from React to Svelte + Sapper. (I enjoy rewriting React code using Svelte so much, I would do it all day if I could!)

Static Site Generation

Sapper by default comes with a Node.js web server, which serves dynamic server-side rendered markup that gets re-hydrated in the browser. Alternatively, you can choose to use the Sapper "export" feature to generate a static web site that works with any web hosting, no Node.js needed.

My administration area using the REST API is not part of this static website; the admin will only run on my local computer, using a local database. The site does not need user authentication or any kind of session state, and it only changes when I write new posts, so I decided that a static website would be perfect, at least for now.

What was easy & awesome?

My experience with Sapper was mostly positive. Often I was surprised at how easy things were. Here are some of those surprises.

1. Getting started

Getting started with Sapper is really easy. The Sapper sample template already has a blog as its example code, and comes with all the build and testing infrastructure that you'll need to get a Sapper website up and running.

2. Rollup

I really enjoyed working with Rollup, also created by Rich Harris, the creator of Svelte & Sapper. If you don't want to use Rollup, you can also choose to use Webpack or another build tool, if that's what you're into.

3. Static site generation

The static generation worked great! It starts at your homepage and crawls your site like a spider, looking for new links in any <a> tags it can find. This meant that my secret administration area was excluded, which was exactly what I wanted anyway. It creates directories and index.html files, to create all the URLs you've defined.

4. Static websites are fast!

Once the static site was live, it didn't take long to achieve a perfect lighthouse score! I honestly did not think that was possible, but here we are:

5. Routes without a router

The way routes work in Sapper is really easy and powerful. You put Svelte components inside the src/routes/ folder to define new routes. If you want a URL like /blog/my-post, you can make a Svelte component in src/routes/blog/[slug].svelte and use the slug to dynamically look up the blog contents in order to render the page. This syntax for dynamic routes is so awesome that even Next.js was inspired to do the same.

I wasn't sure if I'd be able to keep the search box on my blog, since there would be no database to search. Turns out all I needed to do was have the search page use the /blog/all.json route as a data source. I passed the search terms as a query parameter like /blog/search?terms=example The search page parsed the URL to get the search terms, then filters the blog posts client-side to render the results. Might seem ridiculous to have a single JSON file with all the blog posts in it, but on my blog the all.json is only 142kb which is smaller than some JavaScript frameworks! I might write a blog post going into more detail about this client-side search, if anybody is interested?

7. Deployment

Deploying a static site is easy. I use shared hosting so I wrote a bash script that does the following: 1) npm run export to generate the static site, 2) zip up the static files into a zip file, 3) upload the zip file to my web server with scp, 4) ssh into the server and unzip the zip file into the correct folder, 5) delete the zip file. I don't need a complex CI system, though maybe I'll set that up down the road. For now, running a bash script after each blog post is fine for me.

What was hard & confusing?

Learning any new tool has its ups and down. There were some concepts that I didn't understand correctly, and that led me to make mistakes, causing a few bugs and broken pages. Here are some of the things I learned in the process.

1. JSON API

It took me a while to figure out that all the API calls needed to be "JSON API" calls inside server routes that would later generate .json files. Confused already? Let me walk through an example.

When you're viewing a page of the blog, and you click a link to another article, the Sapper client-side code will fetch the contents for that page asynchronously. It can't access the actual REST API so it needs to get the data from a static file, and the best approach for that is to have static .json files in your static site.

For the src/routes/blog/[slug].svelte component I mentioned above, I created a related src/routes/blog/[slug].json.js file that acted as a "server route" that will cause Sapper to export a /blog/my-post.json file for each post.

For more on this, including code examples, check out the Sapper documentation about Server routes.

2. Every page needs to be discovered with a crawler

As I mentioned above, Sapper uses a web crawler to start at your homepage, and dig through looking for links to pages. This means that any pages you might have needs to be linked from an <a> tag. You cannot have any truly secret pages.

To achieve this, I made a single route /blog/all that acts as a site index, with a link to every blog post, plus some extra links at the bottom just so Sapper will know about them. For example, I needed to include an extra link to my RSS feed and my Newsletter signup that weren't linked to with <a> tags anywhere else.

3. Vendor CSS was awkward

Of course I needed to have beautiful looking code examples on my blog, so I integrated Prism.js. I couldn't figure out how to import the prismjs-monokai.css vendor stylesheet into the Svelte component that needed it, so I ended up just using a <link> tag to load it from the template.html, similar to the global.css example file that comes with the Sapper template.

Seems there is a solution that uses a Rollup plugin to allow you to import stylesheets from the Svelte <script> block but I didn't go down this road (yet). Maybe doing an @import in a Svelte <style> block will be something we can do one day, but not today.

Conclusion

Unlike Svelte v3, which is very much ready for production, Sapper is technically still in early development, and hasn't yet reached version 1.0. Still, it was a joy to use, and for something like a blog I think it's perfect. I'm already using Sapper in two other production web applications, as I feel Sapper is mature enough for my needs.

Further reading


Interested in web development? Subscribe to my newsletter!

React Hooks vs. Svelte

November 13rd, 2019

I get asked a lot on my Twitch channel to show a comparison of React and Svelte. I thought I'd record a short video to show everyone how to take a basic React example and rewrite it using Svelte:

Let's look at a basic example from the React Hooks documentation. Here we have a simple component with a button, and some text showing you how many times you've clicked the button. We're using some state in our component, to keep track of how many times the button was clicked. It's a nice, simple example of using a stateful component:

import React, { useState } from 'react';

function Example() {
    // Declare a new state variable, which we'll call "count"
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>Click me</button>
        </div>
    );
}

What would this look like written with Svelte? Turns out, most of the code above is React boilerplate that we can do without. Let's start by commenting out all the boilerplate:

// import React, { useState } from 'react';

// function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);

// return (
<div>
    <p>You clicked {count} times</p>
    <button onClick={() => setCount(count + 1)}>Click me</button>
</div>;
// );
// }

That gets us some of the way, but that's still not valid Svelte. Svelte is an extension of HTML, so we need to put our JavaScript code in a <script> block, and change it to use a local state variable instead of React's useState function:

<script>
    // Declare a new state variable, which we'll call "count"
    let count = 0;
</script>

<div>
    <p>You clicked {count} times</p>
    <button onClick={() => setCount(count + 1)}>Click me</button>
</div>

This is very close to Svelte, but we have to change one more thing. We need to change React's onClick attribute to Svelte's on:click, and then change the click handler so it simply increments count:

<script>
    // Declare a new state variable, which we'll call "count"
    let count = 0;
</script>

<div>
    <p>You clicked {count} times</p>
    <button on:click={() => count++}>Click me</button>
</div>

All done! When you change React code into Svelte code, you spend most of your time deleting code, and deleting code feels amazing!

The major difference here is that your state is kept in local JavaScript variables instead of being tied up inside useState. This means you can set your state variable to a new value without calling a function, and that makes it possible to keep your component code very clean and succinct.

In fact, if you came up to me and said you had a new framework that was even simpler than Svelte, I'd have a hard time believing it! I mean, what could we remove from that Svelte component? Even Vanilla JavaScript would be way more complicated than this basic Svelte example. Svelte makes our web components as simple as possible, but no simpler.


Interested in web development? Subscribe to my newsletter!

How to use Node.js Streams (And how not to!)

October 30th, 2019

When I first started to understand Node.js streams, I thought they were pretty amazing. I love JavaScript Promises, but they only resolve to one result. Streams, however, can provide a constant stream of data, as you might expect!

Functional Reactive Programming is all the rage these days. Libraries like MobX, RxJS and Highland.js make it easy to structure your front-end application as data flowing in one direction down through a chain of pipes.

You can pipe a stream to another stream so that the output of the first becomes the input to the next. Sounds like a really neat way to structure an application, right?

I've already rewritten a lot of my JavaScript code to use Promises. Are streams the next step in the evolution? Is it time to rewrite all our applications to use Node streams? (Spoiler: NO!)

Unix pipes are the best

I love working with pipes in Linux (or Unix). It's really nice to be able to take a text file, pipe that into a command, pipe the output to another command, and pipe the output from that into a final text file.

Here's an example of using the power of pipes on the command line. It takes a text file with a list of words, sorts the list, counts how many times each word appears, then sorts the counts to show the top 5 words:

$ cat words.txt | sort | uniq -c | sort -nr | head -n5

It's not important for you to understand these commands, just understand that data is coming in to each command as "Standard Input" (or stdin), and the result is coming out as "Standard Output" (or stdout). The output of each command becomes the input to the next command. It's a chain of pipes.

So can we use Node.js in the middle of this chain of pipes? Of course we can! And Node streams are the best way to do that.

Going down the pipe

Node.js streams are a great way to be able to work with a massive set of data, more data than could possible fit into memory. You can read a line of data from stdin, process that data, then write it to stdout.

For example, how would we make a Node CLI application that capitalizes text? Seems simple enough. Let's start with an application that just takes stdin and pipes directly to stdout. This code does almost nothing (similar to the cat unix command):

process.stdin.pipe(process.stdout);

Now we can start using our Node.js application in the middle of our pipeline:

$ cat words.txt | node capitalize.js | sort | uniq -c | sort -nr | head -n5

Pretty simple, right? Well, we're not doing anything useful yet. So how do we capitalize each line before we output it?

npm to the rescue

Creating our own Node streams is a bit of a pain, so there are some good libraries on npm to make this a lot easier. (I used to heavily use a package called event-stream, until a hacker snuck some code into it to steal bitcoins!)

First, we'll use the split package, which is a stream that splits an input into lines, so that we can work with the data one line at a time. If we don't do this, we might end up with multiple lines, or partial lines, or even partial Unicode characters! It's a lot safer to use split and be sure we are working with a single, complete line of text each time.

We can also use a package called through which lets us easily create a stream to process data. We can receive data from an input stream, manipulate the data, and pipe it to an output stream.

const split = require('split');
const through = require('through');

process.stdin
    .pipe(split())
    .pipe(
        through(function(line) {
            this.emit('data', line.toUpperCase());
        })
    )
    .pipe(process.stdout);

There is a bug in the code above, because the newline characters are stripped out by split, and we never add them back in. No problem, we can create as many reusable streams as we want, to split our code up.

const through = require('through');
const split = require('split');

function capitalize() {
    return through(function(data) {
        this.emit('data', data.toUpperCase());
    });
}

function join() {
    return through(function(data) {
        this.emit('data', data + '\n');
    });
}

process.stdin
    .pipe(split())
    .pipe(capitalize())
    .pipe(join())
    .pipe(process.stdout);

Isn't that lovely? Well, I used to think so. There's something satisfying about having the main flow of your application expressed through a list of chained pipes. You can pretty easily imagine your data coming in from stdin, being split into lines, capitalized, joined back into lines, and streamed to stdout.

Down the pipe, into the sewer

For a few years, I was really swept up in the idea of using streams to structure my code. Borrowing from some Functional Reactive Programming concepts, it can seem elegant to have data flowing through your application, from input to output. But does it really simplify your code? Or is it just an illusion? Do we really benefit from having all our business logic tied up in stream boilerplate?

It's worse than it looks too. What if we emit an error in the middle of our pipeline? Can we just catch the error by adding an error listener to the bottom of the pipeline?

process.stdin
    .pipe(split())
    .pipe(capitalize())
    .pipe(join())
    .pipe(process.stdout)
    .on('error', e => console.error(e)); // this won't catch anything!

Nope! It won't work because errors don't propagate down the pipe. It's not anything like Promises where you can chain .then calls and throw a .catch at the end to catch all the errors inbetween. No, you have to add an error handler after each .pipe to be sure:

process.stdin
    .pipe(split())
    .pipe(capitalize())
    .on('error', e => console.error(e))
    .pipe(join())
    .on('error', e => console.error(e))
    .pipe(process.stdout);

Yikes! If you forget to do this, you could end up with an "Unhandled stream error in pipe." with no stack trace. Good luck trying to debug that in production!

Conclusions and recommendations

I used to love streams but I've had a change of heart recently. Now, my advice is to use data and error listeners instead of through streams, and write to the output instead of piping. Try to keep the number of streams to a minimum, ideally just an input stream and an output stream.

Here's a different way we can write the same example from above, but without all the hassle:

const split = require('split');
const input = process.stdin.pipe(split());
const output = process.stdout;

function capitalize(line) {
    return line.toUpperCase();
}

input.on('data', line => {
    output.write(capitalize(line));
    output.write('\n');
});

input.on('error', e => console.error(e));

Notice I'm still piping to the split library, because that's straightforward. But after that, I'm using a listener to the data event of the input to receive data. Then, I'm using write() to send the result to the stdout output.

And notice that my capitalize() function no longer has anything to do with streams. That means I can easily reuse it in other places where I don't want to use streams, and that's a really good thing!

I still think Node streams are interesting but they are not the future of JavaScript. If used carefully, you can make pretty powerful command-line tools with Node.js. Just be careful not to overdo it!


Interested in web development? Subscribe to my newsletter!

The simplest Svelte component is an empty file

August 4th, 2019

I discovered something while refactoring my Svelte code that blew my mind:

This was very useful during refactoring, because I could just create a placeholder file for the new component, import it and start using it:

<script>
import Empty from './empty.svelte';
</script>

<Empty/>

Sure, it doesn't do anything, but it doesn't break either.

I think this is very symbolic of what makes Svelte so groundbreaking and powerful. Let's dig deeper and see what it can teach us about Svelte.

A Svelte component is a file

With Svelte, components and files have a one-to-one relationship. Every file is a component, and files can't have more than one component. This is generally a "best practice" when using most component frameworks. Perhaps this practice comes from the practice of having each class in a separate file in languages like Java or C++.

By enforcing this practice, Svelte can make some assumptions that simplify your code. That brings me to the next observation.

No boilerplate, just make a new file

In most component frameworks, you need to write some code to define your component. With React, the simplest component is an empty function. In other frameworks, you need to import a library and call a special function to define and create your component. With Svelte, you just create a new .svelte file.

The Svelte compiler will take each file and generate a component for it automatically. And that brings us to another important observation.

You don't need Svelte to use a Svelte component

In order to mount a React component, you need to import react-dom. Using a Vue component requires the Vue library. An Angular application absolutely requires loading the Angular framework.

Svelte, on the other hand, is a compiler. In a way, Svelte is more like a programming language than a library. When you're programming in JavaScript, you don't need to import something to use a for loop. Similarly, you don't need to import anything in your Svelte code to use Svelte's template syntax. Your Svelte files get compiled into Javascript and CSS. It's a very different approach.

You might guess that an empty file would compile into an empty JavaScript file, but every Svelte component comes with an API that allows you to use it outside of Svelte and mount it into the DOM. Here's what it looks like to use a compiled Svelte component:

import Empty from './empty.js';

const empty = new Empty({
  target: document.body,
  props: {
      // if we had some, they'd go here
  }
});

If we compile our empty component and bundle it with Svelte internals, it ends up being 2,080 bytes uncompressed, and 1,043 bytes gzipped. So the overhead for using Svelte ends up being only a kilobyte. Compare that to other frameworks that require 10x or 100x that many bytes just to mount an empty component!

Svelte is a new paradigm

At first glance, being able to use an empty file as a component seems like a silly, impractical gimmick. But looking deeper, I think it teaches us a lot about how Svelte differs from most if not all JavaScript component frameworks that came before it.

I imagine it will inspire other framework developers to take a similar approach and reap some of the same benefits. This is the kind of shift in thinking that changes things permanently. Svelte is not just a new framework, but a complete paradigm shift.


Interested in web development? Subscribe to my newsletter!

<< older posts