SheetLink Forms beta

Form endpoint vs writing your own serverless function

The function is five lines and free. Sometimes that is the whole story. Here is how to tell when it is not.

The five-line function, stated fairly

Every serverless platform makes the starting point look effortless, because it is. A function that receives a POST, parses the body, and forwards it somewhere - an email API, a Slack webhook, a database insert - genuinely is a few lines, deploys with your site, runs within any free tier a contact form will ever see, and involves no third party in the path of your data.

This guide is a build-vs-buy comparison written by the "buy" side, so let us do the unusual thing and start with the cases where you should build.

When writing your own function is genuinely fine

Build the function when most of these are true:

  1. The destination is something you already operate. Your own database, your own CRM's API, a Slack channel - places where you hold credentials anyway and inserting a record is one call.
  2. You already live on the platform. If the site deploys to Netlify, Vercel, or Cloudflare anyway, the function rides along with zero new infrastructure.
  3. Volume is low and losses are survivable. A personal site's contact form that drops one message during a provider hiccup is an annoyance, not an incident.
  4. The logic is truly custom. Multi-step validation against your own systems, custom enrichment, unusual routing - the flexibility of code is the point.
  5. You enjoy owning it. Not a joke. A well-understood 40-line function you like maintaining beats any service you resent.

A developer's personal site with a low-traffic contact form that emails them via an API is the archetype: build it, enjoy it, done.

What the five lines grow into

The gap appears when the form starts to matter - when a lost submission is a lost customer. The function's real spec was never "forward a POST"; it was everything around that:

  1. Spam. Public endpoints get found. You add a honeypot, then rate limiting (which needs shared state - not free in stateless functions), then content rules. Each layer you tune yourself, and every false positive is silently deleted because you have no review queue.
  2. Retries and durability. The destination API will have a bad minute. A function that tries once and dies loses the submission; doing better means a queue and a retry schedule - real architecture, not five lines.
  3. Observability. "Did Tuesday's submissions all arrive?" needs a delivery log someone can actually read, not grepping platform logs function-invocation by function-invocation.
  4. The long tail: body-size limits, malformed payloads, duplicate suppression, formula-injection escaping if a spreadsheet is downstream.

The part nobody budgets for: the spreadsheet APIs

If your destination is Google Sheets or Excel Online - and for lead capture, a spreadsheet is usually where non-developers want the data - the forwarding step is the deep end. Writing a row means registering an OAuth app, running a consent flow, storing refresh tokens somewhere safer than an environment variable, refreshing access tokens on schedule, handling quota and throttling responses with backoff, and mapping incoming field names onto column headers that a colleague will rename without telling you. On the Microsoft side, add Graph's workbook and table semantics.

None of it is exotic, all of it is work, and it is precisely the work a form endpoint amortizes across every customer: SheetLink's worker writes directly to the Sheets API and Graph with retries at 5, 30, and 120 minutes, a delivery log, automatic case- and punctuation-insensitive header matching, and the formula-injection guard - behind a permanent URL your form posts to. The whole pipeline is on how it works.

A fair scorecard

Side by side, without pretending either column is empty:

  1. Control: the function wins - it is your code, no product decisions constrain you.
  2. Cost: the function wins on sticker price (free tier); the endpoint answers with no per-submission fees ever, free during beta, and a planned flat paid plan (~$15-19/month, labeled planned) - you are paying for the pipeline, not per row.
  3. Time to reliable: the endpoint wins by weeks. Spam layers with quarantine instead of silent deletion, retries, and the delivery log exist on day one.
  4. Spreadsheet delivery: the endpoint wins outright - OAuth lifecycle, mapping, and API quirks are the product.
  5. Attribution: the sl.js embed captures utm_* and ad click IDs across pages; hand-rolling that client-side layer is its own project.
  6. Lock-in: honest note - the endpoint is a dependency. Mitigation: your data lands in a spreadsheet you own from day one, and the exit is editing one action attribute.

Deciding, and a hybrid worth knowing

The compressed decision: build when the destination is your own system, volume is low, and you want the control; use an endpoint when the destination is a spreadsheet, when lost leads cost real money, or when spam has become a chore. Signals it is time to switch: you are about to add rate limiting, you cannot answer "did we lose anything last week", or the marketing team asks which campaign each lead came from.

The hybrid: keep your function for what it is good at and let it forward a copy to the endpoint - anything that can POST can write rows, as one fetch call inside your existing code. Your custom logic stays; the spreadsheet pipeline, screening, and delivery log come free. Compare the no-code path at a contact form on a fully static site, see rows land on the demo, or join the beta from the waitlist.

FAQ

Is a serverless function ever the better choice?

Yes, genuinely: low-volume forms whose destination is a system you already operate, custom logic that code expresses best, and teams that value control over convenience. The archetype is a developer's personal site emailing submissions via an API they already use. The calculus shifts when spreadsheets, spam, or lost-lead costs enter.

What is actually hard about writing rows to Google Sheets from a function?

The write call is easy; the lifecycle is not. OAuth app registration, consent, refresh-token storage and rotation, quota backoff, and field-to-column mapping that survives a colleague renaming headers. That operational tail is what a managed pipeline amortizes.

Can I use both - my function and the endpoint?

Yes. A function can forward a copy of each submission to its endpoint with one HTTP call, keeping your custom logic while gaining spreadsheet delivery, screening, retries, and the delivery log. The reverse composition also works: land rows in the sheet and let other tools watch the sheet.

How does spam handling differ between the two?

A hand-rolled function typically deletes on suspicion, silently - you never learn about false positives. SheetLink quarantines everything suspicious for one-click review (only honeypot hits are marked spam outright), so a false positive costs seconds instead of a customer. Rate limits and heuristics come pre-tuned with a per-form strictness dial.

What does switching from my function to an endpoint involve?

Create a form, connect your sheet or Excel table, and point your page's action (or your function's forwarding URL) at the endpoint. Field names matching column headers map automatically. The exit is equally small - one attribute - which keeps the dependency honest.

Related guides: A contact form on a fully static site · Forms to a spreadsheet without Zapier (and why you might want that) · Google Sheets as your form database: how far it goes

Request an invite Developer docs