Skip to content

Add BFF route example for sending contact form inquiries as mail - #1656

Open
VPS-julia wants to merge 9 commits into
mainfrom
claude/com-2702-bff-route-11gyec
Open

Add BFF route example for sending contact form inquiries as mail#1656
VPS-julia wants to merge 9 commits into
mainfrom
claude/com-2702-bff-route-11gyec

Conversation

@VPS-julia

@VPS-julia VPS-julia commented Aug 31, 2026

Copy link
Copy Markdown

Description

Adds an example for a BFF route to the starter: it receives the values of a contact form, validates them and sends them as a mail. The mail is sent in the BFF and not in the API, because the inquiries are of no interest to the CMS.

The route follows the example implementation in vivid-planet/dextinity#4963 and adds the mail sending in its try block. Since the starter contains no form, the validation is reduced to the fields any contact form needs: the address to reply to and the message. A project extends the schema with its own fields.

The corresponding form is not part of this PR — the starter only provides the route as an example.

Changes

  • site/src/app/[visibility]/[domain]/api/contact-form/route.ts (new): POST handler. Validates the request body with zod, answers with 400 and the validation error on invalid input, and otherwise sends the message as a mail with nodemailer, with the submitted address in Reply-To. Errors are logged and answered with 500. The [visibility] and [domain] segments are added by the domain rewrite middleware, so a form would submit to /api/contact-form.
  • docker-compose.yml and .env: add Mailpit, which catches all mails sent in development and shows them at http://localhost:8025, together with the MAIL_* and CONTACT_FORM_TO_EMAIL variables. MAIL_HOST and MAIL_PORT point at Mailpit; the sender and recipient addresses are left empty, because the starter is boilerplate and a project fills them in itself. The deployment configurations are unchanged — a project supplies its own SMTP credentials there.
  • site/package.json: add nodemailer, zod and @types/nodemailer.
  • AGENTS.md: document the BFF route pattern and the Mailpit service.

Testing

Sending was tested against Mailpit: the mail arrives with the expected From, To, Reply-To and subject.


https://vivid-planet.atlassian.net/browse/COM-2702

https://claude.ai/code/session_014QS28L7gMaUqTqRSJmFejN

Contact form inquiries are of no interest to the CMS, so they are handled
in the BFF instead of the API: the new route handler validates the
submitted values and sends them as a mail via nodemailer.

The corresponding form is not part of the starter.

Mailpit is added to the docker-compose services so mails sent in
development are caught locally and can be viewed at http://localhost:8025.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QS28L7gMaUqTqRSJmFejN
@VPS-julia VPS-julia self-assigned this Aug 31, 2026
claude added 2 commits August 31, 2026 05:05
Read the required environment variables directly where they are used.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QS28L7gMaUqTqRSJmFejN
…kage

Parse the port with parseInt and a string default, as in server.ts and
cache-handler.ts, and validate the host where it is used.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QS28L7gMaUqTqRSJmFejN
@VPS-julia VPS-julia changed the title Add contact form API route with email functionality Add BFF route example for sending contact form inquiries as mail Aug 31, 2026
claude added 6 commits August 31, 2026 05:24
The starter is boilerplate, a project fills in the sender and recipient
addresses itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QS28L7gMaUqTqRSJmFejN
The site name was only used to prefix the mail subject, which the form
already provides.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QS28L7gMaUqTqRSJmFejN
Send the mail inline in the route instead of in a separate util, and
adopt the validation and response shape of the example.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QS28L7gMaUqTqRSJmFejN
The starter doesn't contain a form, so it can't know which fields a
project needs. Validate and send only the address to reply to and the
message.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QS28L7gMaUqTqRSJmFejN
…oute-11gyec

Resolved the conflicts in site/package.json by taking the dependency
updates from main and keeping nodemailer, zod and @types/nodemailer.
site/package-lock.json was regenerated with npm instead of merged by hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QS28L7gMaUqTqRSJmFejN
@VPS-julia
VPS-julia marked this pull request as ready for review September 2, 2026 14:52
@github-actions
github-actions Bot requested a review from VPS-Obi September 2, 2026 14:52

@VPS-Obi VPS-Obi left a comment

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.

I don't think this should be in the Starter as there's no contact form. Instead, the contact form's data should be send as email in the Demo's BFF (it currently only performs validation): https://github.com/vivid-planet/dextinity/blob/main/demo/site/src/app/%5Bvisibility%5D/%5Bdomain%5D/%5Blanguage%5D/api/contact-form/route.tsx#L49

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants