Sending Transactional Email from Flask
Sending with an API
import os
import resend
resend.api_key = os.environ["RESEND_API_KEY"]
def send_email(to, subject, html, text):
return resend.Emails.send({
"from": "PandaDev <hello@mail.pandadevhub.com>",
"to": [to],
"subject": subject,
"html": html,
"text": text,
"reply_to": "support@pandadevhub.com",
})
Other providers have equivalent SDKs; wrap them behind one send_email function so you can switch providers without changing the rest of the app.
Templates
Render email bodies with Jinja like pages:
from flask import render_template
html = render_template("emails/verify.html", user=user, link=link)
text = render_template("emails/verify.txt", user=user, link=link)
send_email(user.email, "Verify your email", html, text)
Email HTML is old-fashioned: use simple layouts (tables or a framework such as MJML), inline styles, web-safe fonts, alt text on images and a maximum width around 600px. Always include a plain-text version: it helps deliverability and accessibility.
Links and tokens
- Use absolute URLs:
url_for("auth.verify", token=token, _external=True)withSERVER_NAME/PREFERRED_URL_SCHEMEconfigured, or aBASE_URLsetting. - Tokens must be random (
secrets.token_urlsafe(32)), stored hashed, single-use and short-lived (password resets: about an hour). - Do not reveal whether an email exists: "If an account exists, we have sent a link" for password resets.
Send in the background
A slow provider should not slow down sign-up. Queue the email (RQ, Celery or a thread for simple cases) and return the response immediately. Retry transient failures, and log the provider's message ID for support.
Development and testing
- Use the provider's test mode or a local catcher (Mailpit) so you never email real users by accident.
- In tests, mock
send_emailand assert it was called with the right recipient and link.
Exercise
Implement email verification for sign-up: a hashed, single-use, 24-hour token; HTML and text templates; background sending; and tests that mock the send function.
Check yourself
- Why include a plain-text version?
- Why store email tokens hashed?
- Why send emails outside the request?
進捗はこのブラウザーにのみ保存されます。
レビューに提出(10 ポイント)