How I recreated the Hey "Feed" with AWS Lambda, SES and React

July 07, 2020

Newsletters are great, but too much of a distraction if they end up straight in your inbox with no predictable schedule.

Hey — the email service by the Basecamp folks — solves this by creating a separate space for newsletters, "The Feed". Here newsletters are shown in a timeline UI without read states. You can casually scroll through newsletters whenever you feel like it, and you do not see them in your main inbox. For most newsletters, this is a great experience.

I liked this concept so much that I decided to integrate it in Mailbrew, my SaaS that lets people receive an email digest from their favorite sources (even DHH himself uses it).

My implementation works like this: when signing up for Mailbrew, you get your own email address to receive newsletters: <username>@inbox.mailbrew.com.

All emails sent to that address end up in daily digest with the schedule you decided. We also created a Hey-style feed browsable at app.mailbrew.com/inbox.

Mailbrew Inbox in action

We built all of this in a couple of weeks and it was easier to setup than I originally predicted. The tooling around email is great these days.

Backend

Amazon SES handles our inbound emails with some help from Lambda.

Here's how it works:

  • When an email arrives at an @inbox.mailbrew.com address, SES uploads the raw email message to S3 and launches a lambda function. Configuring this from the AWS console was 5-10 minutes work.
  • The lambda function calls Mailbrew's backend (a majestic Django monolith), webhook-style, with recipient address, sender address, and message id.
  • If we don't recognize the recipient address (no user with that username) we send a special response to the lambda that causes the email to bounce.
  • If the recipient address matches, then we use the message id to retrieve the raw message content from S3 and process it.
  • Processing the message consists in extracting the text/html version of the email, applying some basic CSS styles, uploading this processed version to S3 and saving the message metadata in Postgres for easy retrieval.

Frontend

In the frontend, emails are presented in a CRA React app. We embed the content of each email in a dedicated iframe.

A cool trick that we used was to create the iframe in JavaScript and set its content programmatically instead of settings its src property. This granted us an higher degree of customization and allowed us to do things like having all links inside newsletters open in new tabs.

The HTML of the emails in the S3 bucket is private, so we generate signed urls backend-side to show them.

One last cool thing we wanted to do was to have a link that allowed to read a single newsletter without the hassle of authentication. When opening a newsletter from the links in the daily email digest that we send users, we didn't want them to have to authenticate each time (almost a certainty with multiple devices and the in-app browsers of email clients).

We achieved this by using JSON Web Tokens. Each linked newsletter email in the digest has a token in the URL allowing read-only access to just that message. These links don't look great, but they work, are sharable, and don't require me to keep any state in the backend.


This update brings the product closer to my vision of the single email you read everyday to stay on top of your game without wasting time.

You can now create a daily digest with Newsletters, Hacker News posts, RSS feeds, Reddit links, Dev.to posts, YouTube videos and much more. The best part is that you receive this just once per day, on your own schedule.

Mailbrew is how I have stayed informed for the past 6 months and here is my daily digest if you want to check out what the product is capable of.

I am @frankdilo on Twitter if you have any feedback about Mailbrew or this post.