Loading "Intro to Email"
Run locally for transcripts
Email is one of the most common ways to verify a user's identity. If they lose
access to their account, need to reset their password, or want to change their
email address, we send them a link to their email address.
The trouble with sending email is summed up in one word: deliverability. It's an
unfortunate state of affairs, but if you're not using a service for email then
you're probably not going to be able to send email reliably. Read more about
this in this depressing blog post:
After self-hosting my email for twenty-three years I have thrown in the towel. The oligopoly has won..
๐ข
So which service do you use? After evaluating many options, I have settled on
Resend which has a great free tier for while you're
getting started and then a very reasonable pricing model after that.
Additionally, deliverability is excellent and they have a great API.
There is a standard for sending email and that's called SMTP. It's a bit of a
pain to work with, so I recommend using a library to do it for you. A common
library for Node.js applications is Nodemailer.
The nice thing about using a service is it means you should be able to swap out
your email provider without issue.
However, the problem with SMTP is that it's difficult to mock for local
development. I prefer all my local development to work offline, so instead of
using Nodemailer, I use the Resend REST API directly. It's simple and if you
package things up well you shouldn't have any issue switching to a different
provider anyway. I've actually done this (when I
switched to Resend)
and it didn't involve much work.
Another challenge with email is the limitation around styling and client
support for various features. It's an enormous pain. However, Resend has worked
on an incredibly useful library of React components and a custom React renderer
so you can actually write your emails using React components! It even supports
a custom tailwind config and converts those classes to styles supported by email
clients. The library is react.email and you can find all
the components via
@react-email/components
.function PositiveAffirmationEmail({ name }: { name: string }) {
return (
<E.Html lang="en" dir="ltr">
<E.Container>
<h1>
<E.Text>You are great, {name}</E.Text>
</h1>
<p>
<E.Text>I just think you should know you are awesome.</E.Text>
</p>
<E.Link href="https://kcd.im/emoji">Here's my favorite emoji</E.Link>
</E.Container>
</E.Html>
)
}
const [html, text] = await renderReactEmail(<PositiveAffirmationEmail />)
async function renderReactEmail(react: ReactElement) {
const [html, text] = await Promise.all([
renderAsync(react),
renderAsync(react, { plainText: true }),
])
return { html, text }
}
Finally, it may be useful for you to get an idea of how the auth flow will work
in this app:
Here's a description of the flow:
- The user submits an email to the server
- The server creates a verification in the database
- The server redirects the user to a
/verify
route - The server sends the code to the user's email address
- The user checks their email client for the code
- The user submits the code
- The server verifies the code with the verification in the database
- The server redirects with a cookie
- The user proceeds to onboard with a signed cookie identifying them as the owner of the email address their onboarding with.
And that's what you're going to build in this exercise. Let's go!