Most recent blog posts

Or you can click on the categories to filter by topic

get in JavaScript is the same as property in Python

February 13, 2025
0 comments Python, JavaScript

Almost embarrassing that I didn't realize that it's the same thing!


class MyClass {
  get something() {
  }
}

is the same as Python's property decorator:


class MyClass
    @property
    def something:

They both make an attribute on a class automatically "callable".
These two are doing the same functionality:


class Foo {
  get greeting() {
    return "hello";
  }
  place() {
    return "world";
  }
}

const f = new Foo();
console.log(f.greeting, f.place());
// prints 'hello world'

and


class Foo:
    @property
    def greeting(self):
        return "hello"

    def place(self):
        return "world"

f = Foo()
print(f.greeting, f.place())
# prints 'hello word'

Use 'key' in React components to reset them

February 12, 2025
0 comments React

I'm sure you've seen this React code:


<ul>
  {users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>

The key prop is necessary so each element is identifiable.

But did you know you can pass key to a component even though there's no .map(...) or other array of React elements.

Consider this simplified app:

Truncated! Read the rest by clicking the link below.

How to send custom headers in a loader in react-router v7

February 7, 2025
0 comments React, JavaScript

tl;dr; Use data() in your loader function and make your headers function pass it on to get headers be dependent on what's happening in the loader function.

I recently rewrote the front end of this website from Remix to react-router v7. A route is a page, which can be something like /about or have a parameter in it like /blog/:slug.

The way react-router v7 (the "framework mode") works is that your route looks like this:


import type { Route } from "./+types/post"

export async function loader({ params }: Route.LoaderArgs) {
  const post = await fetchPost(params.slug)
  return { post }
}

export default function Component({loaderData}: Route.ComponentProps) {
  return <h1>{loaderData.post.title}</h1>
}

So good for so far. But suppose you want this page to have a certain header, depending on the value of the post object. To set headers, you have to add an exported function called, surprise surprise; headers. For example:

Truncated! Read the rest by clicking the link below.

TypeScript enums without enums

January 29, 2025
0 comments JavaScript, TypeScript

My colleague @mattcosta7 demonstrated something that feels obvious in hindsight.

Instead of enums, use a regular object. So instead of


enum State {
  SOLID,
  LIQUID,
  GAS,
}

(playground here - note how "complex" the transpiled JavaScript becomes)

...use an object. Objects have the advantage that when the TypeScript is converted to JavaScript, it looks pretty much identical. TypeScript 5.8 makes it possible to disallow "non-erasable syntax" which means you can set up your tsconfig.json to avoid enum.

The alternative is an object. It's a bit more verbose but it has advantages:


const State = {
  SOLID: "solid",
  LIQUID: "liquid",
  GAS: "gas"
} as const

type State = typeof State[keyof typeof State]

(playground here - note how simple the transpiled JavaScript is)

In the above code, if you hover the mouse over State it'll say

'solid' | 'liquid' | 'gas'

Truncated! Read the rest by clicking the link below.

How I run standalone Python in 2025

January 14, 2025
1 comment Python

I don't do as much Python as I used to do. The few projects I still maintain, in Python, have a pyproject.toml and uv.lock. I.e. I'm using uv for getting the right executable version of Python and for installing dependencies. No more pip install ... and no more requirements.(in|txt). And definitely no poetry.lock.

And pyenv stopped working entirely when Python 3.12 came out. I used to use pyenv instead of Homebrew to get different versions of Python for different projects. uv is just that much better. I still use virtual envs, in the form of uv sync && source .venv/bin/activate when working inside a project and want to be able to type python ... and that referring to the exact version of Python with the relevant dependencies (from the pyproject.toml) installed.

However, there's a problem how: Outside of projects (that have a pyproject.toml and uv.lock) I no longer have a valid python executable. There's still a python3 executable that comes from /opt/homebrew/bin/python3 but that one I can't add dependencies to.
And many times I just want to whip up a quick script or start a repl, but with some certain dependencies installed. For example, to run...


import requests
print(requests.get('https://www.peterbe.com').headers['content-type'])
# prints 'text/html; charset=utf-8'

Again, uv to the rescue! I created ~/bin/python (plus chmod +x ~/bin/python) which now looks like this:


#!/bin/bash
set -x
uv run --python 3.12 --with requests python $@

Now I can quickly start a repl. Or if I create a /tmp/test-something.py I can just run that with


python /tmp/test-something.py

Truncated! Read the rest by clicking the link below.

My 2024 golf goals

December 30, 2024
0 comments Golf

When I started with an instructor at the start of this year (2024), I texted her about my goals. They were:

  • Eliminate big tee-off mistakes (slices, pulls)
  • Break 80
  • Handicap 6
  • Predictable positioning at setup

Admittedly, only "Break 80" and "Handicap 6" are measurable.

Text message about goals
Text message about goals

So how did I do?

Truncated! Read the rest by clicking the link below.

How I built an index of my blog posts on my GitHub profile page

December 13, 2024
1 comment Node, GitHub

If you go to https://github.com/peterbe it lists the most recent blog posts here on my blog. The page is rebuilt every hour using GitHub Actions. This blog post is about how I built that, so that you can build something just like it.

In case you don't have access or it's quicker to look at a picture, this is what it looks like:

List of blog posts

The way GitHub profiles work is you create a GitHub repo that is in the same name as your username. In my case, my username is peterbe, and the repo is thus called peterbe. So, it's named https://github.com/peterbe/peterbe. It has to be a public repo for this to work.

In that repo you have a README.md and mine looks like this: https://github.com/peterbe/peterbe/blob/main/README.md?plain=1 If you look carefully, the Markdown in that README.md contains:


<!-- blog posts -->
...
<!-- /blog posts -->

By default, HTML comments work in GitHub-flavored Markdown just like they do in HTML.

Then, I have a Node script that finds that inside the file and replaces its content with a list of Markdown links.

Truncated! Read the rest by clicking the link below.

Run TypeScript in Node without extensions

December 10, 2024
0 comments Node, JavaScript

A couple of months ago I wrote about "Node watch mode and TypeScript" which suggests using the @swc-node/register package to be able to run node on .ts file without first converting it to .js using tsc.

In Node 22, you don't even need @swc-node/register. You can use --experimental-strip-types instead. (Note the prefix "experimental"). See release notes here.

Imagine a script like this:


// example.ts

function c2f(c: number) {
  return (c * 9) / 5 + 32;
}
console.log(c2f(123));

you can execute it directly in Node v22 using:


❯ node --experimental-strip-types --no-warnings example.ts
253.4

Truncated! Read the rest by clicking the link below.

An ideal pattern to combine React Router with TanStack Query

November 18, 2024
1 comment React, JavaScript

I'm writing this blog post from an admin interface I built, which is a web app frontend for the backend behind peterbe.com. It's built as a single-page app in Vite, with React.
Vite, unlike frameworks like Remix or Next, doesn't come with its own routing. You have to add that yourself. I added React Router. Another thing you have to do yourself is a way to load remote data into the app for display and for manipulation. This is done as XHR requests happen on the client side. For that, I chose TanStack Query. If you haven't used it but used React, it's sugar for this type of code:


// DON'T DO THIS. USE TANSTACK QUERY

const [stuff, setStuff] = useState(null)
const [error, setError] = useState(null)
useEffect(() => {
  fetch('/api/some/thing')
    .then(r => r.json())
    .then(data => setStuff(data)
    .catch(err => setError(err))
}, [])

Truncated! Read the rest by clicking the link below.

brotli_static in Nginx

November 8, 2024
0 comments Nginx, Linux

tl;dr; for Ubuntu (>=24.10) run apt install libnginx-mod-http-brotli-static

Nginx is the web server I use on my personal web server. It sits in front of static files and Python and Node backend servers.
For static files, you can make it server the files compressed with Brotli, if the equivalent file exists on disk as a .br file.
Suppose you have this:


server {
    root /path/to/my/app/dist;
    server_name myapp.example.com;

    brotli_static on; // THIS!

    location / {
        try_files $uri /index.html;
    }

    ...
}

Truncated! Read the rest by clicking the link below.

Previous page
Next page