Skip to content

Add Resend email endpoint and utility#45

Merged
goldlabelapps merged 2 commits intomasterfrom
staging
Apr 2, 2026
Merged

Add Resend email endpoint and utility#45
goldlabelapps merged 2 commits intomasterfrom
staging

Conversation

@goldlabelapps
Copy link
Copy Markdown
Owner

Introduce a POST /resend endpoint and backing utility to send emails via the Resend API. Adds app/api/resend/utils/send_email.py (httpx-based sender), a Pydantic EmailRequest model and handler in app/api/resend/resend.py, and imports the new send helper. Also fixes a small typo in the root data response, updates app metadata/description and bumps version to 2.0.6. Note: RESEND_API_KEY must be set in the environment. Adds pydantic[email] to requirements.

Introduce a POST /resend endpoint and backing utility to send emails via the Resend API. Adds app/api/resend/utils/send_email.py (httpx-based sender), a Pydantic EmailRequest model and handler in app/api/resend/resend.py, and imports the new send helper. Also fixes a small typo in the root data response, updates app metadata/description and bumps __version__ to 2.0.6. Note: RESEND_API_KEY must be set in the environment. Adds pydantic[email] to requirements.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Resend-backed email-sending capability to the API by introducing a /resend POST handler and an httpx utility function, plus small metadata/version updates.

Changes:

  • Added send_email_resend() utility to send emails via the Resend API.
  • Added POST /resend with a Pydantic request model using EmailStr.
  • Updated app metadata/version and added pydantic[email] dependency.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
requirements.txt Adds pydantic[email] to support EmailStr validation.
app/main.py Updates FastAPI app description metadata.
app/api/resend/utils/send_email.py Introduces an httpx-based Resend email sender helper.
app/api/resend/resend.py Adds /resend POST endpoint + request schema; fixes a small response key typo.
app/init.py Updates package docstring and bumps __version__ to 2.0.6.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +21 to +26
try:
response = httpx.post(RESEND_API_URL, headers=headers, json=payload, timeout=10)
response.raise_for_status()
return response.json()
except Exception as e:
return {"error": str(e)}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The helper makes a new one-off httpx.post() call every time and catches all exceptions, returning str(e) (which can be noisy and may leak internal details). Consider using a shared httpx.Client (connection pooling) and handling httpx.HTTPStatusError / httpx.RequestError explicitly with a sanitized, structured error (or raise and let the API layer map it to an HTTP status).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in commit e1e12bd. Changes to send_email.py:

  • Added a module-level _client = httpx.Client(timeout=10) for connection pooling (replaces one-off httpx.post())
  • Replaced broad except Exception as e with explicit httpx.HTTPStatusError as exc (returns sanitized {"error": f"Upstream error: {exc.response.status_code}"}) and httpx.RequestError (returns {"error": "Failed to reach the email service"})

Comment on lines 2 to 10
from app import __version__
import os
from app.utils.make_meta import make_meta

from fastapi import APIRouter, Query, Path, Body, HTTPException

from app.utils.db import get_db_connection

from .utils.send_email import send_email_resend

router = APIRouter()
base_url = os.getenv("BASE_URL", "http://localhost:8000")
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several unused imports/variables here (__version__, Query, Path, Body, HTTPException, get_db_connection, and base_url). Removing unused items will reduce confusion and avoid lint/type-check noise as this module evolves.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Comment on lines +37 to +52
@router.post("/resend", status_code=status.HTTP_202_ACCEPTED)
def send_email(request: EmailRequest):
"""POST /resend endpoint to send email via Resend API."""
if not RESEND_API_KEY:
meta = make_meta("error", "RESEND_API_KEY missing. Please set it in your .env file.")
return {"meta": meta}
result = send_email_resend(
to=request.to,
subject=request.subject,
html=request.html,
sender=request.sender
)
if "error" in result:
meta = make_meta("error", result["error"])
return {"meta": meta}
meta = make_meta("success", "Email sent successfully.")
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@router.post(..., status_code=202) returns 202 even when the API key is missing or when Resend returns an error (the handler returns a body instead of signaling an HTTP error). Use HTTPException (e.g., 500 for missing server config, 502 for upstream failure) and consider returning 200/201 on success unless the send is actually queued in the background.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Comment on lines +37 to +48
@router.post("/resend", status_code=status.HTTP_202_ACCEPTED)
def send_email(request: EmailRequest):
"""POST /resend endpoint to send email via Resend API."""
if not RESEND_API_KEY:
meta = make_meta("error", "RESEND_API_KEY missing. Please set it in your .env file.")
return {"meta": meta}
result = send_email_resend(
to=request.to,
subject=request.subject,
html=request.html,
sender=request.sender
)

This comment was marked as resolved.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants