You don't need a backend to store form submissions. You need a place to ask "how many."
Originally published at nlqdb.com/blog.
Every landing page hits the same wall around hour three: the signup form works, but where do the emails actually go? The reflex is to stand up a server and a database for what is, honestly, an INSERT and an occasional COUNT. So most people reach for a form service instead - and that solves storage, but quietly splits your data from your questions. The submissions live in someone else's dashboard; the moment you want "signups per day since launch" or "which referrer actually converted," you're exporting a CSV and pivoting it by hand.
Two problems hiding in one, with different shapes:
- Capture is a write - a small one, and it genuinely doesn't need a server: an
INSERTcall from the page's ownfetch, or a ten-line serverless function, is enough, as long as the write key isn't sitting in your client HTML. - Reporting is a read, and it's the part that actually wants a database - because "how many per day," "top source this week," and "conversion by campaign" are aggregations, and aggregations want a query planner, not a spreadsheet and a human.
-- capture: one small insert per submission (no server required)
INSERT INTO signups (email, referrer, created_at) VALUES ($1, $2, now());
-- reporting: the part that actually wants a query planner
SELECT date_trunc('day', created_at) AS day, count(*) AS signups FROM signups GROUP BY day ORDER BY day;
The mistake is picking a tool that's great at the write and leaves you alone with the read. A form service nails capture and hands you a list. A spreadsheet-via-webhook nails capture and hands you a tab you pivot by hand.
What you want is for the place the rows land to also be a place you can ask questions of - ideally in plain English, so the day-one question ("did anyone sign up?") and the week-two question ("which tweet drove it?") are the same two-second action, not a data chore. That's the shape worth looking for, whatever you build it on: storage you can also interrogate.
Each submission is a row in a real Postgres, and the reporting question is one English goal - signups grouped by day with a count - that compiles to SQL you can read before you trust it. That's how nlqdb works, but the point isn't the tool - it's refusing to let your form data land somewhere you can't ask it anything.
The honest caveat that applies to any version of this: the public read widget isn't a write endpoint, so capture still goes through a key the browser never sees, and email delivery plus spam filtering stay your front-end's and your ESP's job.
Storage isn't the hard part. Not being able to ask your own data a question is.
Comments
No comments yet. Start the discussion.