Supported Frameworks
Learn about Supported Frameworks in WaymakerOS.
Host detects your framework automatically on every push and deploys it the right way — static sites to Waymaker's global edge network, server apps to the server runtime. You don't configure the pipeline; you push code.
The matrix
| Framework | Static | Server (SSR / API routes) | Notes |
|---|---|---|---|
| Next.js | ✅ (output: "export") | ✅ Fully supported | App Router, Server Components, Route Handlers, API routes, streaming — all work. No special config. |
| Custom TypeScript server (Hono, itty-router, plain fetch handler) | — | ✅ Supported (manifest) | Declare it in waymaker.config.ts — see below. |
| Vite / React / Vue / Solid | ✅ | — | Static builds deploy automatically. |
| SvelteKit | ✅ (adapter-static) | 🔜 Planned | Server builds currently deploy their static output. |
| Nuxt 3 | ✅ (static preset) | 🔜 Planned | As above. |
| Remix | ✅ (static export) | 🔜 Planned | As above. |
| Astro | ✅ | 🔜 Planned | Server-adapter apps detected; static output deploys today. |
| Gatsby / Docusaurus / Eleventy | ✅ | — | Static generators deploy automatically. |
How detection works
- Your manifest wins. If
waymaker.config.tsdeclarestypeandruntime, Host obeys it — no guessing. - Signals as fallback. No manifest? Host reads your
package.jsonand framework config (for example,next.config.*withoutput: "export"means static; without it, server). - Re-checked on every push. Change your app from static to server and the next deploy reclassifies it automatically — the Runtime chip in Host shows what detection concluded.
Custom TypeScript servers
Building an API or server app without a big framework? Declare it:
// waymaker.config.ts
export default {
type: 'app',
runtime: 'server',
entry: 'src/server.ts', // optional — defaults to src/index.ts
}
Your entry file exports a standard fetch handler (Hono apps do this natively):
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello from Waymaker'))
export default app
Host bundles it and deploys it to the server runtime. This lane is manifest-only by design — Host never guesses that an app is a custom server.
Databases
Any app can have its own Postgres database (one per app): provision it from the Databases module in Host or with waymaker host db create <app>, pick a region (Sydney, Singapore, Frankfurt, London, N. Virginia — permanent for the life of the database), and your app reads DATABASE_URL from its environment — the same variable Prisma, Drizzle and every standard scaffold already expect, so a stock project connects without changes. Schema changes ship as migrations: waymaker host db migrate <app>.
Output folders — one setting worth getting right
output_dir means "publish this folder as-is". Leave it empty for a server app — Host builds
and deploys the server for you.
Never set it to .next. That is Next.js's internal build folder, not a website. Setting it
publishes scaffolding: the deploy succeeds, the app shows as active, and every page returns
404. Host now refuses the value, but the reasoning is worth carrying — if your framework builds a
server, there is no folder to publish.
Static exports do have one: Next.js with output: "export" produces out.
Troubleshooting
- Every page 404s but the deploy succeeded — the app published a build folder instead of a
site. Clear
output_dirand set the runtime toserver, then redeploy. Runwaymaker host doctor <app>to confirm what Host thinks your app is. - "Build output directory does not exist" — your build command didn't produce the configured
output directory, or the app is actually a server app: declare
runtime: "server"in the manifest. - Pages render but the data is empty — the app is being served as a static site, so nothing
runs per request.
waymaker host doctor <app>shows the runtime it actually deployed as, next to the one you declared. - Server frameworks marked 🔜 currently deploy their static output, with a warning in the build log naming what will not run. Full server support lands per framework — declare intent in the manifest and the build log tells you exactly what happened.
Talking to your database from a server app
Two things are not guessable, and each costs a day if you get it wrong.
Use @waymakeros/db, not pg. pg (node-postgres) opens a raw TCP socket, which the server
runtime cannot do. It fails at build time with Could not resolve "pg-cloudflare" — a package you
have never heard of, named hundreds of lines into a log.
npm install @waymakeros/db
import { sql } from '@waymakeros/db'
const rows = await sql`select * from centres where id = ${id}`
It reads DATABASE_URL for you, so there is nothing to wire up.
Do not hold a connection or pool across requests. Create it inside the request that uses it. A pool cached at module scope — exactly how you would write it on Node — makes requests hang non-deterministically once deployed: one page returns 200, the next 500, the next 200, on the same code and the same deploy. It reads as a bug in the failing page, and it is not.
If you genuinely need an interactive transaction, open a pool inside that one function and close it before returning.
Signing your own users in
Apps with a database can have their own sign-in — your customers, members or staff, with accounts that live in your app's database rather than in Waymaker.
waymaker host db auth provision <app>
waymaker host db auth deploy <app>
Email and password works out of the box, and so does password reset. Where the account is a shared mailbox rather than one person — a site address, a team inbox — you can offer one-time codes by email instead, so nobody has to share a password:
waymaker host db auth methods <app> --otp on
waymaker host db auth deploy <app>
Two-factor sign-in is available too, and can be required for particular roles while leaving others alone. See the developer documentation for the sign-in endpoints your app calls.