Home Services Work About Blog FAQ Contact
← Back to blog

Where n8n should stop and your app should start.

A workflow that runs clean on a schedule is one thing. A workflow a person is staring at in a browser is another. Short answer: never call n8n straight from your frontend. Put a database in between, and let both sides talk to that instead.

A question that keeps coming up in the n8n community, worded slightly differently every time: someone has a real workflow doing real work — data collection, enrichment, an AI scoring step, results landing in a spreadsheet — and now they want an actual interface on top of it. Next.js frontend, Supabase underneath, and one honest question: where does n8n's job end and the app's job begin?

It's the same question we get from clients who already have three or four n8n workflows running their business and are done looking at spreadsheets. They want a dashboard. And the instinct almost everyone starts with is the one that breaks first.

The obvious wiring, and why it only holds up in the demo

The instinct is to connect the frontend straight to n8n. A button click fires a webhook, the workflow runs, the response comes back, you render it. It works the first time. It works for the first real user, too. Then a run that touches three APIs and a model call takes 35 seconds, your serverless function times out at 10, and you're looking at a spinner with no idea whether the workflow ran, failed, or is still going. Click it again and you might process the same record twice.

There's a second cost that's easy to miss: your frontend now inherits n8n's uptime. If the automation instance restarts mid-request, or a queue backs up, your customer sees an error for a problem that has nothing to do with your app.

Calling n8n straight from your UI turns an async job into a slow synchronous one. Everything downstream inherits that.

Put a database between them

The pattern that actually holds up is boring, which is why it works. n8n and your frontend don't talk to each other. They both talk to the same database — Postgres, Supabase, Neon, whatever you're already on. Your frontend writes a request row and reads state from that table. n8n runs on its own schedule or its own webhook, picks up new rows, does the multi-step work, and writes the result back to the same place. The UI polls that table, or subscribes to it if your database gives you realtime for free — Supabase does — and updates the second the row changes.

Nobody's sitting on an HTTP response from a job that might take a minute. If n8n restarts mid-run, the row just sits there until the next cycle picks it up; nothing user-facing breaks. And you get an audit trail for free — the same status column driving your progress indicator also tells you exactly when a request moved from queued to processing to done.

The database isn't just storage here. It's the contract between two systems that don't need to know about each other.

What actually belongs in n8n, and what doesn't

Once the database sits in the middle, the split falls out on its own. n8n is the worker: anything multi-step, anything that calls a third-party API or a model, anything that needs retries and error branches — that's what n8n is built for, and rebuilding that orchestration in application code is wasted effort. Your app backend owns what's tied to a logged-in person: who they are, what they're allowed to see, and the live state of the UI itself. If a step needs "call three APIs and combine the results," it's n8n's. If it needs "is this specific user allowed to see this," it's yours.

One gray area trips people up: what about a request that genuinely needs to feel instant, like a chat reply? Even there, the database-in-the-middle pattern holds — you're just polling on a shorter interval, or streaming partial rows as n8n writes them. The mental model doesn't change because the clock speed does. What changes is how often the frontend checks.

What this looks like on an actual build

We built a version of this for a lead-scoring dashboard — n8n handled enrichment, Claude handled the scoring, and the client wanted a live view instead of refreshing a spreadsheet every hour. The frontend never once called n8n directly. It read from and wrote to Postgres; n8n ran on its own trigger against the same tables; the "processing" spinner on the dashboard was nothing more than a status column changing value. When something has needed debugging at an inconvenient hour, it's been useful that the two systems don't share a request lifecycle — a slow API call on n8n's side has never once taken the app down with it.

If you're already running n8n as the backend and thinking about a real frontend on top of it, that's the exact intersection we build in — automation and the app it lives inside of are usually one project, not two vendors handing off a spec to each other. Worth a quick call before you wire the UI straight to a webhook and find out why that breaks the first time a real user shows up.

— Cole

Sources

Running n8n as the backend and want a real frontend on it?

30-minute call. We'll tell you honestly whether the database-in-the-middle pattern fits your build or whether something simpler will do.

Book a Discovery Call →