cleanup + now topper

This commit is contained in:
Cory Dransfeldt 2023-03-12 11:17:56 -07:00
parent 6c81b89874
commit 82604bd42b
No known key found for this signature in database
38 changed files with 123 additions and 8 deletions

View file

@ -0,0 +1,49 @@
---
title: '2022 reading list'
date: '2022-04-03'
draft: false
tags: ['reading']
---
I'm still plugging away with my reading habit and my streak is now at 772 days.<!-- excerpt --> Here's where I'm at for 2022 so far:
**Finished**
- [The Extended Mind by Annie Murphy Paul](https://oku.club/book/the-extended-mind-by-annie-murphy-paul-Mzlrf)
- [Drive by James S. A. Corey](https://oku.club/book/drive-by-james-s-a-corey-DXapB)
- [MBS by Ben Hubbard](https://oku.club/book/mbs-by-ben-hubbard-HTrlr)
- [Putins People by Catherine Belton](https://oku.club/book/putins-people-by-catherine-belton-cHBSw)
- [The Sins of Our Fathers by James S. A. Corey](https://oku.club/book/the-sins-of-our-fathers-by-james-s-a-corey-HKXjt)
- [The Complete Redux Book by Ilya Gelman and Boris Dinkevich](https://leanpub.com/redux-book)
- [Off the Edge by Kelly Weill](https://oku.club/book/off-the-edge-by-kelly-weill-SKujn)
- [The Cryptopians by Laura Shin](https://oku.club/book/the-cryptopians-by-laura-shin-S43ey)
- [The Intersectional Environmentalist by Leah Thomas](https://oku.club/book/the-intersectional-environmentalist-by-leah-thomas-3o8nH)
- [The Compatriots by Andrei Soldatov](https://oku.club/book/the-compatriots-by-andrei-soldatov-UMhCz)
- [The Wretched of the Earth by Frantz Fanon](https://oku.club/book/the-wretched-of-the-earth-by-frantz-fanon-8On3n)
- [Lords of Chaos by Michael Moynihan](https://oku.club/book/lords-of-chaos-by-michael-moynihan-TQeVA)
- [Going Clear by Lawrence Wright](https://oku.club/book/going-clear-by-lawrence-wright-ChtJe)
- [Blitzed by Norman Ohler](https://oku.club/book/blitzed-by-norman-ohler-CZnyf)
- [Paradise by Lizzie Johnson](https://oku.club/book/paradise-by-lizzie-johnson-BHfRA)
- [Pedagogy of the Oppressed by Paulo Freire](https://oku.club/book/pedagogy-of-the-oppressed-by-paulo-freire-nGgoW)
- [Missoula by Jon Krakauer](https://oku.club/book/missoula-by-jon-krakauer-ggUIz)
- [Free by Lea Ypi](https://oku.club/book/free-by-lea-ypi-k3V1u)
- [Reign of Terror by Spencer Ackerman](https://oku.club/book/reign-of-terror-by-spencer-ackerman-vNJMb)
- [Narconomics by Tom Wainwright](https://oku.club/book/narconomics-by-tom-wainwright-qRrxi)
- [Capitalist Realism by Mark Fisher](https://oku.club/book/capitalist-realism-by-mark-fisher-Lq4Gm)
- [An Ugly Truth by Sheera Frenkel](https://oku.club/book/an-ugly-truth-by-sheera-frenkel-RxLoN)
- [Sellout by Dan Ozzi](https://oku.club/book/sellout-by-dan-ozzi-wXvCV)
- [Will by Will Smith and Mark Manson](https://oku.club/book/will-by-will-manson-smith-mark-YfBE1)
**In progress**
- [Rotting Ways to Misery by Markus Makkonen](https://oku.club/book/rotting-ways-to-misery-by-markus-makkonen-MPt17)
- [Absolution Gap by Alastair Reynolds](https://oku.club/book/absolution-gap-by-alastair-reynolds-RHAFH)
- [Moneyland by Oliver Bullough, Marianne Palm](https://oku.club/book/moneyland-by-oliver-bullough-s9wvO)
**Next up**
- [Miles by Miles Davis](https://oku.club/book/miles-by-miles-davis-UG9m7)
- [The Nineties by Chuck Klosterman](https://oku.club/book/the-nineties-by-chuck-klosterman-QNgHC)
- [Old Man's War by John Scalzi](https://oku.club/book/old-mans-war-by-john-scalzi-H7UHv)
I've been listening to podcasts again as well, so I'll have to see how that impacts my pacing and reading.

View file

@ -0,0 +1,65 @@
---
title: 'Apple-centric digital privacy tools'
date: '2022-05-31'
draft: false
tags: ['apple', 'privacy', 'ios', 'macos', 'tech']
images: ['/static/images/blog/privacy.jpg']
---
A rundown of privacy tools that work well with Apple's technology ecosystem.<!-- excerpt -->[^1]
## Overview
<TOCInline toc={props.toc} exclude="Overview" toHeading={2} />
## Email providers
Ubiquitous free email providers profit by mining user data (whether humans are involved or not). Your inbox acts as a key to your digital life and you should avoid using any provider that monetizes its contents.
- [Fastmail](https://ref.fm/u28939392)[^2]: based in Melbourne, Australia Fastmail offers a range of affordably priced plans with a focus on support for open standards (including active development support for [JMAP](https://jmap.io) and the [Cyrus IMAP email server](https://fastmail.blog/open-technologies/why-we-contribute/)). They also [articulate a clear commitment to protecting and respecting your privacy](https://www.fastmail.com/values/) and offer an extensive [rundown of the privacy and security measures they employ on their site](https://www.fastmail.com/privacy-and-security/).
- I would also recommend exploring their [masked email implementation](https://www.fastmail.help/hc/en-us/articles/4406536368911-Masked-Email), which integrates seamlessly with [1Password](https://1password.com) (though using 1Password isn't required).
- [mailbox.org](https://mailbox.org): based in Germany, [mailbox.org](http://mailbox.org) also has [a long history](https://mailbox.org/en/company#our-history) and [commitment to privacy](https://mailbox.org/en/company#our-mission). Their service is reliable, straightforward and fully featured (it's based off of a customized implementation [Open-Xchange](https://www.open-xchange.com)) and supports features like incoming address blocking, PGP support and so forth.
- [Proton Mail](http://protonmail.com): Proton offers a host of encrypted tools, ranging from mail to drive, calendaring and VPN services. They're also the only option in this list that includes end to end encryption. The service is extremely polished and reliable but, it's worth noting, doesn't support access to your email via open standards like IMAP/SMTP without the use of a cumbersome, desktop-only, bridge application.
- [iCloud+](https://support.apple.com/guide/icloud/icloud-overview-mmfc854d9604/icloud): if you're paying for an Apple iCloud subscription you'll get access to the option to add a custom email domain to your account to use with Apple's iCloud Mail service. This is private inasmuch as the data isn't mined for monetization against personalized ads, but is also bare-bones in terms of functionality. It supports IMAP and push notifications on Apple's devices but features like rules, aliases and so forth are extremely limited compared to the previously mentioned providers. This is better than most free providers, but hardly the best option.
- iCloud+ _does_ also offer a [Hide My Email](https://support.apple.com/guide/icloud/what-you-can-do-with-icloud-and-hide-my-email-mme38e1602db/1.0/icloud/1.0) feature to conceal your true email address, much like Fastmail.
## Email apps
- [Apple Mail](https://support.apple.com/mail): Apple's Mail app is simple but also fully featured and reliable to the point of being a bit boring. It also has enhanced privacy features as of iOS 15 and macOS 12 in the form of [Mail Privacy Protection](https://support.apple.com/guide/iphone/use-mail-privacy-protection-iphf084865c7/ios).
- [Canary Mail](https://canarymail.io/): a third-party email with a reasonable price tag and a heavy focus on privacy and security, Canary offers a number of enhancements like read receipts, templates, snoozing, PGP support and calendar/contact integration. The design hews tightly to iOS and macOS platform norms but, naturally, is not quite as tightly integrated as Apple's first-party mail app.
- [Mailmate](https://freron.com/): a long running, highly configurable mail app with a strict focus on IMAP support, Mailmate is an excellent option on macOS and also offers strong support for authoring messages in markdown.
## Safari extensions
- [1Blocker](https://1blocker.com): a highly configurable ad and tracker blocker. Independently maintained and actively developed it also offers a device-level firewall to block trackers embedded in other apps on your device.
- [Super Agent](https://www.super-agent.com): this extension simplifies the process of dealing with the modern web's post-GDPR flood of cookie consent banners by storing your preferences and uniformly applying them to sites that you visit. This allows you to avoid the banners altogether while limiting what's allowed to something as restrictive as, say, functional cookies only.
- [Hush](https://oblador.github.io/hush/): another option to deal with cookie banners by simply blocking the banners outright.
## DNS providers
- [nextDNS](https://nextdns.io/?from=m56mt3z6): I use nextDNS on my home network for basic security and have a more restrictive configuration that heavily filters ads at the DNS level on specific devices. This allows me to block ads, trackers and other annoyances at the DNS level, which covers anything embedded in apps or other services running on my device.
- [Cloudflare 1.1.1.1](https://www.cloudflare.com/learning/dns/what-is-1.1.1.1): Cloudflare's 1.1.1.1 service doesn't offer the same features as nextDNS, but is still preferable to Google's offering or your ISP's default.
- [iCloud Private Relay](https://support.apple.com/en-us/HT212614): Another iCloud+ offering, iCloud Private Relay offers _some_ protection by relaying your traffic in Safari (and Safari only) through a pair of relays to obfuscate your actual IP address and location.
## Password managers
- [1Password:](https://1password.com): I've used 1Password for over 11 years and have yet to have any significant issues with the service. It integrates smoothly with Fastmail to generate masked email addresses, has added support for storing and generating ssh keys and application secrets, supports vault and password sharing and works across platforms. Highly recommended.[^3]
- [Bitwarden](https://bitwarden.com): I haven't made use of Bitwarden, but have heard plenty of positive feedback over the years.
## VPN providers
- [IVPN](https://www.ivpn.net/): my current choice for a VPN provider, it's apps are modern, reliable and offer support for per network default behavior, wireguard, multihop connections and numerous endpoints around the globe.
- [Mullvad](https://mullvad.net/en/): an open source, commercial VPN based in Sweden, Mullvad offers both WireGuard and OpenVPN support.
- [Mozilla](https://www.mozilla.org/en-US/products/vpn/): offered by the non-profit Mozilla Foundation, this is another compelling offering from an organization with a track record of fighting for the open web and preserving user privacy.
For now I've scoped this post to platforms and tools that are central to maintaining your online privacy. But, with that said, each app you use should be examined to determine if and how it fits with your approach towards privacy.
Everything you use is going to glean data from your interactions with it and it's worth considering that tool's stance on privacy, tracking and monetization before investing your time and data into using it.
**Other resources**
My friend Nathaniel Daught has an excellent post with similar resources on his blog [that you should take a look at as well](https://daught.me/blog/privacy-security-tools-2022).
[^1]: This post expands on a [previous post](https://coryd.dev/blog/digital-privacy-tools) with a quick rundown preceded by a link to the New York Times on the same subject.
[^2]: This is my referral link you can skip that and go straight to [fastmail.com](https://fastmail.com).
[^3]: I also generate and store answers to security questions here, rather than providing answers that may be publicly known or derived.

View file

@ -0,0 +1,25 @@
---
title: 'Apple Messages: a tale of woe OR how to fix sync, a crash loop and accept data loss'
date: '2022-04-06'
draft: false
tags: ['apple', 'services']
---
Apple's Messages app recently started crashing in a loop on my Mac Mini — it would happen every time the app was opened after a 5-10 second delay. Deleting conversations from other devices and letting that change sync over didn't appear to help.<!-- excerpt -->
If you're attached to your message history and have a device where Messages.app isn't crashing, I'd suggest backing up your messages before you try fixing this. Done? Here we go:
Navigate to `~/Library` and delete:
```
Messages
Caches/com.apple.Messages
Caches/com.apple.imfoundation.IMRemoteURLConnectionAgent
Caches/com.apple.MobileSMS
Containers/com.apple.iChat
Containers/com.apple.soagent
```
Log out of/deactivate iMessage on all of your devices. Reboot them. Log back in and hope for the best[^1].
[^1]: They should start syncing again, but it may take a while and the conversations downloaded from iCloud may be a bit disjointed, but the app should stop crashing and work going forward.

View file

@ -0,0 +1,109 @@
---
title: 'Automating email cleanup in Gmail'
date: '2022-03-28'
draft: false
tags: ['gmail', 'automation']
---
Lately I've been leaning into automating the cleanup of email I receive in Gmail using a combination of Inbox-era categories that the application still exposes via search and [Google Apps Script](https://www.google.com/script/start/).<!-- excerpt -->
I wasn't using Gmail when Inbox was available (I'm sure I missed out) and know not all of the most beloved features have been migrated over to Gmail proper. That said, there _are_ some handy filters that didn't ascend to Gmail's tabbed inbox interface but are still available to create rules against[^1].
I've created filter rules leveraging all of these legacy filters to automatically categorize messages the same way the current tabs do. These rules look like the following:
```
# emails gmail categorizes as travel related
Matches: category:travel
Do this: Apply label "Traveling"
# emails gmail categorizes as receipts
Matches: category:purchases
Do this: Apply label "Receipts
# emails gmail categorizes as finance related
Matches: category:finance
Do this: Apply label "Financial"
# emails gmail categorizes as reservations
Matches: category:reservations
Do this: Apply label "Reservations"
```
Expanding on this, I also have a few forwarding addresses in place to conditionally handle other types of messages. First up, I use some compiled search terms to redirect emails indicating something I've ordered has shipped off to [Deliveries.app](https://junecloud.com). That rule looks like this:
```
Matches: subject:({"has shipped" "was shipped" "on its way" "tracking number" "shipment from order" "order shipped confirmation" "Shipped:"})
Do this: Skip Inbox, Mark as read, Apply label "Deliveries", Forward to <UNIQUE-ID>@junecloud.com
```
For newsletters, I sign up using Gmail's plus addressing scheme to automatically label them as `newsletters`[^2]:
```
Matches: to:(cory.dransfeldt+newsletters@gmail.com)
Do this: Skip Inbox, Mark as read, Apply label "Newsletters", Forward to <UNIQUE-ID>@newsletters.feedbin.com
```
For both newsletters and deliveries this leaves me with a fair amount of archived mail that arguably decreases in or loses all value over time[^3].
I take a similar approach to actionable/alert-style messages:
```
Matches: <SUPER IMPORTANT CONDITION HERE>
Do this: Apply label "Alerts", Forward to <UNIQUE-ID>@todoist.net, Mark it as important, Categorize as Primary
```
This rule leaves alerts prominently in my inbox and as an actionable task in [Todoist](https://todost.com). Keeping the email in focus and in my Todoist inbox is, arguably, redundant but helps keep the issue front and center until it's resolved.
### On to Google Apps script
To clean up these various transactional messages I use several different Google Apps Script scripts. Each runs twice a month on the 1st and 15th and is targeted at cleaning up a category of messages. These runs are scheduled using the `Time-driven` event source and the `Month timer` time based trigger.
For example, to clear old newsletters, I use the following:
```javascript
function batchDeleteEmail() {
var SEARCH_QUERY = 'label:newsletters -label:inbox'
var batchSize = 100
var searchSize = 400
var threads = GmailApp.search(SEARCH_QUERY, 0, searchSize)
for (j = 0; j < threads.length; j += batchSize) {
GmailApp.moveThreadsToTrash(threads.slice(j, j + batchSize))
}
}
```
This rule iteratively deletes all messages with the label `newsletters`, omitting messages that, for whatever reason, might have landed in my inbox.
The rules for deliveries and alerts operate in very much the same way, but with a different query for each:
**Deliveries (omitting Gmail-identified receipts and the inbox)**
```
'label:deliveries -label:inbox -label:receipts'
```
**Alerts (omitting the inbox)**
```
'label:alerts -label:inbox'
```
Unrelated to cleanup, I also mark any unread emails in my archive as read, with this script running every minute using the `Time-driven` event source, `Minute timer` and is executed every minute (heavy-handed perhaps, but the error-rate for this has only been 0.02%):
```javascript
function markArchivedAsRead() {
var SEARCH_QUERY = 'label:unread -label:inbox'
var batchSize = 100
var searchSize = 400
var threads = GmailApp.search(SEARCH_QUERY, 0, searchSize)
for (j = 0; j < threads.length; j += batchSize) {
GmailApp.markThreadsRead(threads.slice(j, j + batchSize))
}
}
```
I have given some thought to refactoring my cleanup scripts such that the batch delete consumes an array of the individual search queries, iterating over them much like it does the threads it's operating on but, at that point, I'd be looking at a loop over the argument and then over the threads in a child loop when separate script functions can run without that being a concern.
[^1]: I am puzzled that Forums made the cut as a featured option.
[^2]: Don't email me via Feedbin. I'll miss it or it'll just be annoying.
[^3]: I care when something ships, I don't care to reference the tracking info months later.

View file

@ -0,0 +1,22 @@
---
title: 'Fixing Safari iCloud syncing'
date: '2022-05-28'
draft: false
tags: [apple, ios, macos]
---
I've been having an intermittent issue with Safari failing to sync any data via iCloud that you would normally expect — history, tabs, bookmarks and the landing page were all behaving independently despite iCloud syncing being enabled.<!-- excerpt -->
These steps fixed the issue, finally, on my devices:
1. Open a terminal and run `defaults write com.apple.Safari IncludeInternalDebugMenu 1`
2. Quit Safari
3. Open Safari, navigate to the new `Debug` menu and select `Sync iCloud History`
4. Run `defaults write com.apple.Safari IncludeInternalDebugMenu 0` to disable the `Debug` menu[^1]
5. Disable Safari in the iCloud settings of each of your devices
6. Reboot each of your devices
7. Enable Safari in the iCloud settings of each of your devices
Cross your fingers and hope for the best, but sync should settle down and start working again. I'd contend that none of these steps _should_ be necessary, but here we are.
[^1]: Unless you want to keep it.

View file

@ -0,0 +1,92 @@
---
title: 'Migrating to Fastmail'
date: '2022-04-13'
draft: false
tags: ['email', 'fastmail', 'gmail']
---
So you want to migrate over to Fastmail for your email — here's how you can go about doing so as seamlessly as possible.<!-- excerpt -->
I've used (and/or tried) nearly every email service I've heard of and have stuck with Fastmail the longest[^1]. They make onboarding and migrating easy, offer a fast and robust web application, support modern standards and nicely integrate contacts and calendar applications that also support [CardDav](https://en.wikipedia.org/wiki/CardDAV) and [CalDav](https://en.wikipedia.org/wiki/CalDAV) access[^2].
### Kicking things off
Register for an account at [fastmail.com](https://ref.fm/u28939392)[^3] — you'll be run through their lightweight onboarding process which allows you to select an address at a domain they own or use your own. If you use your own, they'll guide you through configuring the DNS records for it, often with registrar specific instructions.
They also offer [extensive documentation](https://www.fastmail.com/help/domain_management_custom_dns.html) on this process and offer a UI that validates that the records you have set are correct. For example, your finalized records would look like the following:
**MX:**
```
example.com. IN MX 10 in1-smtp.messagingengine.com
example.com. IN MX 10 in1-smtp.messagingengine.com
```
**SPF:**
```
@ TXT "v=spf1 include:spf.messagingengine.com -all"
```
**DKIM:**
These will be specific to your domain and can be found and set as follows:
1. Login to your FastMail account and go to Options > Virtual Domains (or Manage > Domains for a family/business account).
2. Scroll to the bottom, and youll see a new "DKIM signing keys" section. For each domain you have, youll see a DKIM public key.
3. Login to your DNS provider, and create a new TXT record for each domain listed and use the value in the "Public Key" column as the TXT record data to publish.
**Bonus points**
- Configure DMARC — Simon Andrews has [an excellent writeup](https://simonandrews.ca/articles/how-to-set-up-spf-dkim-dmarc#dmarc)on how to do this.
- Configure MTA-STS — there's a writeup on that [over at dmarcian](https://dmarcian.com/mta-sts/). It'll entail configuring an additional 3 DNS records and exposing an MTA-STS policy file[^6].
### Importing your email
Fastmail makes importing your email from your current provider painless via their [import tool](https://www.fastmail.com/go/settings/setup). With [detailed documentation](https://www.fastmail.help/hc/en-us/articles/360058753594-Import-your-mail) available in their [help center](https://www.fastmail.help/hc). If you're coming from Gmail or Google Workspace[^4] Fastmail will authenticate via OAuth (with the caveat that you'll need [IMAP enabled](https://support.google.com/mail/answer/7126229?hl=en)) and quickly pull over your email, contacts and calendars. Once the import is done, you can also use the [purge folders tool](http://fastmail.com/go/cleanfolders) to tidy up duplicate messages.
If you still need access to calendars from your own provider, Fastmail can [sync them and manage them](https://www.fastmail.help/hc/en-us/articles/360058752754-How-to-synchronize-a-calendar) from their web interface and then pass them down to your device alongside your dedicated Fastmail calendars.
### Syncing with your devices
First, set up [two-step verification](https://www.fastmail.help/hc/en-us/articles/360058752374-Using-two-step-verification-2FA-) — this should be done with a an authenticator app[^5]. Next, [create a password](https://www.fastmail.help/hc/en-us/articles/360058752854-App-passwords) for each app you'll access the service with — you can provision the permissions for the password to be fairly broad but, in the interest of security, I'd suggest scoping them to each app and the service they need to access (e.g. IMAP/SMTP for your Mail app on each device, CalDAV only for your calendar app on each device etc.). Fastmail has server names and ports required to access each service [outlined here](https://www.fastmail.help/hc/en-us/articles/1500000278342-Server-names-and-ports).
### Next steps
At this point you should have your data migrated, your domain configured and be able to access your account from all of your different apps and devices.
**Spam training**
Hop on over to Fastmail's [folder documentation](https://www.fastmail.help/hc/en-us/articles/1500000280301-Setting-up-and-using-folders) and scroll down to Advanced Options — I've configured custom folders I filter mail I _do_ want to receive into as well as the Archive and Sent system folders to learn messages as **not spam**. You can also flag inbound spam messages that slip through to help train the spam filter applied to your account.
**Filtering**
I would highly recommend creating rulesets to help filter messages that aren't critical out of your inbox. Fastmail's documentation on their mail rules and filters [can be found here](https://www.fastmail.help/hc/en-us/articles/1500000278122-Organizing-your-inbox#rules). I filter messages out of my inbox based on a few broad categories, namely:
- **Updates:** anything sent programmatically and pertinent but not critical (e.g. service or utility notifications and so forth).
- **Financial:** anything from financial institutions. I do this based on the TLD, e.g. `examplebank.com`.
- **Social:** anything from social networks or services. I do this based on the TLD, e.g. `linkedin.com`.
- **Promotions:** anything from a merchant or similar mailing list. I subscribe to a handful but don't want them in my inbox. I use [Fastmail's advanced folder options](https://www.fastmail.help/hc/en-us/articles/1500000280301-Setting-up-and-using-folders) to auto-purge this folder every 60 days.
I also use a few aliases to route mail elsewhere:
- **Deliveries:** anything referencing tracking numbers or shipment status get sent off to [Parcel](https://parcelapp.net).
- **Alerts:** uptime alerts and a few other notifications get sent off to [Things](https://culturedcode.com/things/) to be slotted as actionable tasks to be addressed.
- **Newsletters:** mailing lists get routed off to [Feedbin](https://feedbin.com) to be read (or not).
- **Reports:** I route DMARC/email reports to this folder in the event I need to review them (which is rarely if ever).
All of these particular folders live as children of my Archive folder and are auto-purged at different intervals. They're messages that are useful in the near term but whose utility falls off pretty quickly over time.
**Masked email**
If you're a [1Password](https://1password.com) user you can link your accounts and generate per-service, [masked emails for improved security](https://www.fastmail.help/hc/en-us/articles/4406536368911-Masked-Email). The idea here being that if your primary email is known, it can be used to trigger password resets at different services or leveraged in brute-force attacks, but this is mitigated by using a masked/pseudo-random address for each service.
Did I miss anything? [Email me](mailto:hi@coryd.dev)[^7].
[^1]: As an aside, [mailbox.org](https://mailbox.org) is also quite nice and offers some nice privacy features but isn't _quite_ as polished as Fastmail.
[^2]: Which amounts to seamless syncing with iOS at the system level or via an app like [DAVx](https://play.google.com/store/apps/details?id=at.bitfire.davdroid&hl=en) on Android.
[^3]: This is my referral link you can skip that and go straight to [fastmail.com](https://fastmail.com).
[^4]: This one's aimed at you, free Google Workspace [forced migration](https://www.theverge.com/2022/1/19/22891509/g-suite-legacy-free-google-apps-workspace-upgrade).
[^5]: Think [1Password](https://1password.com) or [Authy](https://authy.com)
[^6]: This site is hosted at Vercel, but I have that policy file in a [categorically uninteresting GitHub repository](https://github.com/cdransf/mta-sts) configured using GitHub pages.
[^7]: At Fastmail, naturally.

View file

@ -0,0 +1,87 @@
---
title: 'Simple data fetching with custom React hooks and SWR'
date: '2022-05-23'
draft: false
tags: [swr, api, fetch, react, nextjs]
---
My site was scaffolded out using [Timothy Lin](https://github.com/timlrx)'s [tailwind-nextjs-starter-blog](https://github.com/timlrx/tailwind-nextjs-starter-blog) project (which I highly recommend checking out). As part of the build out I wanted to display the books I'm currently reading and the song I most recently listened to, data available from [oku](https://oku.club) and [Last.fm](https://last.fm), respectively[^1]. I've added the display for this data to the top of the home page using a pair of light-weight React components.<!-- excerpt -->
To fetch the data for these components I elected to leverage [vercel/swr](https://github.com/vercel/swr), described as:
> SWR is a React Hooks library for data fetching.
>
> The name "**SWR**" is derived from `stale-while-revalidate`, a cache invalidation strategy popularized by [HTTP RFC 5861](https://tools.ietf.org/html/rfc5861). **SWR** first returns the data from cache (stale), then sends the request (revalidate), and finally comes with the up-to-date data again.
On oku, each collection page contains an RSS feed exposing the data for that page. To retrieve and parse the data for my [reading](https://oku.club/user/cory/collection/reading) collection, I'm leveraging [feed-reader](https://www.npmjs.com/package/feed-reader) and passing it to the `useSWR` hook exposed by `swr`. This looks like the following:
```typescript
import { read } from 'feed-reader'
import { useEffect, useState } from 'react'
import useSWR from 'swr'
export const useRss = (url: string) => {
const [response, setResponse] = useState([])
const fetcher = (url: string) =>
read(url)
.then((res) => res.entries)
.catch()
const { data, error } = useSWR(url, fetcher)
useEffect(() => {
setResponse(data)
}, [data, setResponse])
return {
response,
error,
}
}
```
This is then implemented in a `reading.tsx` component as follows[^2]:
```typescript
const { response, error } = useRss('/books')
```
Similarly, I've implemented a hook to fetch json using, well, `fetch` and that looks like the following:
```typescript
import { useEffect, useState } from 'react'
import useSWR from 'swr'
export const useJson = (url: string) => {
const [response, setResponse] = useState<any>({})
const fetcher = (url: string) =>
fetch(url)
.then((res) => res.json())
.catch()
const { data, error } = useSWR(url, fetcher)
useEffect(() => {
setResponse(data)
}, [data, setResponse])
return {
response,
error,
}
}
```
This is then implemented in a `music.tsx` component as follows[^3]:
```typescript
const { response, error } = useJson('/api/music')
```
The `useJson` hook only supports `GET` requests at this point but, could, with a little effort, be refactored to support parameters passed through to the enclosed `fetch` call. This could be done by updating the interface to accept a `parameters` object that includes the url to be called or by adding a separate, optional `parameters` object. I would lean towards the latter approach as the usage would only become as complex as a specific implementation requires.
Both of these components are visible at [coryd.dev](https://coryd.dev). The loading state is displayed until `response` is valid and `null` is returned in the event an `error` occurs as returned by the hook.
[^1]: For the request to oku, I've configured a rewrite in `next.config.js`; for last.fm I've added a simple `api/music.ts` route that interpolates my private API key stored in my Vercel environment variables.
[^2]: The full `reading.tsx` implementation can be [viewed here](https://github.com/cdransf/coryd.dev/blob/1b33bfdc88bbef27e5916971e5db15aa600299d7/components/media/reading.tsx).
[^3]: The full `music.tsx` implementation can be [viewed here](https://github.com/cdransf/coryd.dev/blob/c2577e08e659ce739ab360f25cf5424c6e3ed922/components/media/music.tsx).