Headless API mode
Use Tyravel as a backend-only framework: JSON routes, queues, mail, and the full service container — without views, SSR, Echo, or client assets.
Create a headless app
tyravel new my-api --headless
# or
tyravel new my-api --template=headlessWith npm create:
npm create tyravel@latest my-api -- --headlessThe scaffold includes:
src/routes/api.ts— versioned routes under/api/v1/*config/app.ts—headless: truefor toolingtyravel.json—"mode": "headless"- No
resources/views/,@tyravel/echo, or view-types CI workflow
Quick start
cd my-api
npm install
tyravel migrate
tyravel dev
curl http://127.0.0.1:3000/api/v1/healthRoutes
Headless apps register routes in src/routes/api.ts:
import { Route } from '@tyravel/core';
import { Response } from '@tyravel/http';
Route.prefix('api/v1').middleware('throttle:api').group((routes) => {
routes.get('/health', () => Response.json({ status: 'ok' }));
});The root path / returns a small JSON index with links to this guide.
Authentication
Run tyravel auth:install for guards and migrations. On headless projects it scaffolds:
config/auth.tswith default guardapi- Routes under
/api/v1/*(login, tokens, me) without CSRF middleware src/main.tswithoutViewServiceProvider
Headless apps return JSON errors by default — no HTML error pages. Use Bearer tokens (auth:api) or session login at POST /api/v1/login.
JSON fast path
Stateless API routes (GET /api/v1/health, auth:api Bearer routes, etc.) automatically skip session, CSRF, locale, and view middleware when config/http.ts has jsonFastPath: true (default in headless scaffolds). Routes with guest, auth, or csrf middleware always use the full stack.
Boot profile
After await app.boot(), call prepareHttpServer() — it applies the headless profile, registers HTTP middleware, loads storage/framework/routes.json in production, and starts hot reload in development:
import { ConfigRepository, prepareHttpServer } from '@tyravel/core';
await prepareHttpServer(app, app.make(ConfigRepository));This enables JSON exception responses even when clients send Accept: text/html. Full-stack apps can use registerViewStack(app) when not headless.
OpenAPI export
Generate a starter OpenAPI 3.0 document from registered routes:
tyravel make:openapi
# writes storage/api/openapi.json
tyravel make:openapi --stdoutRefine request/response schemas in the exported file for your API consumers.
Development
tyravel dev hot-reloads config and routes in headless mode. View watching is disabled because there are no .tyr templates.
Concurrent workers still work:
tyravel dev # web + queue worker + debug:watch (when debug is installed)
tyravel dev --no-queue # web onlyDeploy checks
tyravel deploy:check runs doctor and route-cache validation. View compilation is skipped when headless mode is detected.
tyravel deploy:checktyravel doctor reports headless mode and skips production view-cache requirements.
Converting an existing app
To mark an existing API-only project as headless:
- Set
headless: trueinconfig/app.ts - Add
"mode": "headless"totyravel.json(optional but recommended) - Move routes to
src/routes/api.tsand import them fromsrc/main.ts - Remove unused view, Echo, and SSR dependencies from
package.json
When to use headless vs --template=api
--headless | --template=api | |
|---|---|---|
| Views / SSR | None | Optional (full stack deps remain) |
| Route file | src/routes/api.ts | src/routes/web.ts |
| Client assets | None | Echo stub included |
package.json | Slim (no views/echo) | Full default stack |
Choose headless when you want a dedicated API service. Choose api when you might add SSR or views later without re-scaffolding.
Next steps
- Routing — groups, middleware, controllers
- API resources — transform models for JSON
- Authentication — guards and tokens
- Deployment — Docker, Fly, Railway