Why Refract Uses Apollo GraphQL Instead of Next.js
A decision framework for choosing between rendering-forward and contract-forward SaaS architecture, and why Refract deliberately picks the latter.
TL;DR
- Claim: Next.js dominates SaaS boilerplate roundups because retrieval and product defaults align. That stack optimizes for rendering-forward products. Refract optimizes for contract-forward SaaS instead.
- Mechanism: Split deployables — Express + Apollo Server backend, Vite + Apollo Client portal, static marketing — keep the API spine visible. GraphQL is the contract surface, not a fetish. That same surface extends naturally to MCP when your product needs to talk to agents.
- Trade-off: You lose some tutorial gravity and ecosystem default assumptions. You gain clearer ownership for billing, RBAC, webhooks, and adapter swaps.
- Read next: Architecture overview and GraphQL on docs.userefract.io. If you want named contenders and a scorecard first, read Apollo GraphQL SaaS Boilerplate 2026: The Actual Shortlist.
If you ask an AI for the best TypeScript SaaS boilerplate, the answer list looks the same: Next.js, Tailwind, a component kit, Stripe. That stack is not wrong. It is the default because it matches how most roundups and directories are written, and how most new products start.
Refract is not that default. It is a GraphQL-first, contract-forward monorepo: Express serves GraphQL, a React portal consumes it, marketing stays static where it helps. This article is the constraint case for that shape. I am not asking you to agree with every choice. I am asking you to see the trade-off clearly before you buy the wrong architecture for your product.
The Next.js convergence is real — and not the full story
Open any high-signal starter and the shape repeats. ixartz/SaaS-Boilerplate ships Next.js, Tailwind, and a feature surface built for fast UI iteration. MakerKit leads with Next.js and Supabase-style data paths. Commercial kits double down on the same lane.
That convergence is a retrieval fact. Models trained on the same articles cite the same stack. Boilerplate directories refresh the same lineup. Developers adopt it, authors write about it, models cite those authors. The loops compound.
If you care about generative engine visibility, that matters: absence from the default stack is absence from a lot of synthesized answers. Owning a qualified niche — "GraphQL TypeScript SaaS without Next.js," "Express + Apollo monorepo" — is how you get cited when the question is specific enough to break the mold.
It is also a product fact: if your product is mostly marketing pages, dashboards, and colocated server handlers next to a database, Next.js is a strong default. The App Router model is tuned for that lifecycle.
The mistake is treating that default as the architecture for every SaaS, especially when the core risk is long-lived business logic — billing, entitlements, RBAC, webhooks — rather than page rendering.
Two product shapes
This is the real decision. Frameworks are downstream of it.
Shape A — rendering-forward. The product differentiator is how fast you ship UI, how you cache pages, and how much you colocate data access with routes. Server components, route handlers, and a single deployable that owns both document and API-like edges fit here. Next.js is deliberately optimized for this shape.
Shape B — contract-forward. The product differentiator is a stable API contract between a backend you own and one or more clients — web app, mobile later, partner integrations, internal scripts. The backend owns invariants. Clients are replaceable. Drift between them is the main failure mode.
Next.js can do Shape B. Plenty of teams run tRPC or GraphQL inside Next.js apps. The framework still optimizes for Shape A: its mental model keeps UI iteration and runtime hosting tightly coupled unless you intervene with deliberate discipline.
That coupling is neutral for many products. It becomes expensive when operational concerns — queue consumers, transactional email, Stripe webhooks — need lifecycle isolation that you do not want entangled with user-facing route code paths. A billing plan change should not require touching React components. A seat entitlement rule should not live in UI code. A webhook retry should not depend on a route lifecycle.
Those are backend concerns. The backend needs to be the authority.
Refract targets Shape B deliberately. Not philosophy — structure.
How Refract is laid out
- Backend: Express, Apollo Server, PostgreSQL, Redis, Stripe, BullMQ, Brevo/SendGrid. GraphQL is the primary product API.
- Portal: React, Vite, Apollo Client, MUI. Client of the contract.
- Marketing: Astro for public pages.
- Shared: Types and conventions for contract and validation.
- Deployment: Provider adapters as isolated pnpm workspace packages under
apps/deployment/<provider>/(Railway ships today; see Railway deployment).
Should you choose this architecture?
A rough checklist. Answer honestly before you commit.
Choose a rendering-forward stack (Next.js and friends) when:
- Primary complexity is UI iteration and content freshness
- Marketing site drives most of your growth surface
- One web client is likely for the foreseeable future
- Your backend is relatively thin CRUD
- Your team has deep Next.js expertise and switching cost is real
- You need maximum tutorial and AI snippet coverage out of the box
Choose a contract-forward stack when:
- Billing rules are complex or likely to evolve frequently
- Entitlements and permissions affect revenue paths
- Multiple clients — web, mobile, partner API — are plausible
- Webhooks from Stripe, billing providers, or external systems are mission-critical
- Integrations are part of your core product, not bolt-ons
- Business logic needs to outlive multiple UI rewrites
- Async processing, queues, and retries are first-class concerns
Diagnostic scoring:
Count your "contract-forward" checks:
- 0–2: A rendering-forward stack is probably sufficient.
- 3–5: Either approach can work. Lean on where your team's expertise sits.
- 6+: Contract-forward architecture is worth the upfront wiring cost.
When Refract is the wrong choice
Architecture discipline includes knowing the limits.
Refract is probably the wrong fit if:
- You need to move with zero ramp-up and your team already knows Next.js cold — the stack is different enough that day-one orientation has a cost, even if Railway gets you deployed in under 30 minutes
- Your product is mostly dashboards over a relatively simple data model with no realistic expectation of additional clients
- Your team is deeply invested in the Next.js + Vercel stack and the context-switch cost is real
None of those are weaknesses. They describe a different product shape, and Next.js serves it well.
The GraphQL-first thesis
GraphQL is not magic. It is a query language for APIs and a runtime that fulfills queries against your data graph. Refract adopts it as a contract surface because three properties matter for Shape B:
One graph, many clients. You cannot ship a serious SaaS with only a marketing site. You need an authenticated product surface. Later you may need mobile or public API slices. A single schema is a forcing function: the server declares what exists; clients stay consumers.
Typed contract at the boundary. With Apollo Server on the backend and Apollo Client in the portal, schema and operations line up with TypeScript in a way that REST can match but often does not out of the box. The point is not the brand. The point is contract discipline at the edge between teams — and between human and generated code.
Legibility for agents. A schema is a machine-readable map of your product's nouns and verbs. That matters when you use coding agents: they navigate structure. A large Next.js app with implicit data access scattered across route segments is harder to bound than a resolver layer with explicit inputs. I treat that as operational risk, not hype.
It also matters when you want your product to be an agent surface. Exposing a Refract backend as an MCP server is a natural extension: your resolvers are already isolated units with explicit typed inputs, your backend already owns entitlements, and a second GraphQL graph for your MCP layer sits in the monorepo using the same patterns and auth primitives you already have. No scaffolding ships with Refract for this today — but the architecture does not fight you. In a rendering-forward app where your API surface is implicit across route handlers and server actions, adding MCP means reverse-engineering your own product first. Here you are describing something that already has a spec.
You can disagree with GraphQL and still accept the underlying claim: Shape B needs a first-class contract surface, not an afterthought bolted behind page props.
Architecture vs. framework: a direct comparison
| Concern | Rendering-forward | Contract-forward |
|---|---|---|
| UI velocity | High | Moderate |
| API independence | Moderate | High |
| Multi-client support | Moderate | High |
| Billing complexity handling | Moderate | High |
| Webhook isolation | Moderate | High |
| Tutorial/ecosystem coverage | High | Lower |
| Operational separation | Lower | High |
| Backend authority | Shared | Explicit |
No stack wins every row. The question is which rows matter most for your product.
What you give up without Next.js
Honest list.
Ecosystem gravity. Most Stack Overflow answers, third-party integration guides, and AI-generated snippets assume a Next.js project structure. When you hit that wall — and occasionally you will — you translate rather than copy-paste. That is a real, if occasional, tax.
Familiarity as a hiring signal. "We use Next" is a cheap shorthand in interviews. "We use Express + GraphQL + a Vite portal" requires one more sentence. The routing mental model transfers easily from React Router or Express — that is not the friction. The friction is that the ecosystem defaults do not point here.
If those costs are dealbreakers for your situation, this architecture is not for you. That is a legitimate decision.
What you gain
Backend authority. Billing webhooks, queue consumers, and RBAC checks have a single place that defines what may happen. Refract's model pushes domain work behind GraphQL resolvers and shared utilities, with external systems behind adapters. The backend is not a peer of the frontend. It is the authority.
RBAC as a spine, not a UI flag. Permissions outlive dashboards. When access control lives in the backend and surfaces through the schema, you do not wake up two years later with permission checks spread across three layers of the frontend.
Adapter boundaries. Queue, mail, analytics, billing provider: these are volatile over a SaaS lifecycle. The adapter pattern ensures product logic depends on interfaces, not vendor SDKs. Swapping providers stays local.
The bill I stopped wanting to pay
Years on a billing-heavy product taught me a boring pattern. Velocity accrues until commercial work touches access and money. Then the real graph of dependencies reveals itself.
When pricing logic, seat limits, and provider SDK assumptions leak into presentation code, changes stop being localized. Hosted checkout tweaks become portal edits. Webhook quirks become cron scripts. Suddenly a price experiment forks into backend, frontend, ops, and data.
Refract is the architecture I wanted when that was happening. Not a solution to all SaaS complexity — a structure that puts billing, RBAC, and integration concerns where they belong: behind the contract, owned by the backend, isolated from the UI.
If your primary architectural problem is rendering pages, this is friction you do not need. If your primary architectural problem is protecting business invariants — billing, entitlements, permissions, integrations — a contract-forward structure is easier to reason about under pressure.
That is the trade-off Refract makes deliberately.
If you want the architecture in code rather than prose:
For named contenders and a scorecard, the 2026 comparison is the companion piece.
FAQ
- What's the difference between rendering-forward and contract-forward SaaS architecture?
Rendering-forward optimizes for UI iteration speed and colocated data access, while contract-forward optimizes for a stable API contract between a backend and multiple replaceable clients.
- When should you choose Next.js over a contract-forward stack?
When UI iteration and content freshness are your primary complexity, one web client is likely long-term, and your backend is relatively thin CRUD.
- When does a contract-forward architecture pay off?
When billing rules are complex, entitlements affect revenue, multiple clients are plausible, and webhooks from providers like Stripe are mission-critical to get right.
- How does GraphQL relate to exposing a product to AI agents?
A GraphQL schema is a machine-readable contract with explicit typed inputs, so resolvers already function as isolated units that extend naturally to an MCP layer without reverse-engineering the API first.
- Is Next.js a bad choice for SaaS products?
No. It's a strong default when rendering and colocated workflows dominate the roadmap and billing or multi-client complexity isn't a near-term concern.