---
name: headless-api
description: Guide for Vvveb CMS headless access — REST and GraphQL endpoints, enabling, authentication, route tables and schemas. Use when exposing data to external apps, adding a REST resource or GraphQL field, debugging 401/404 API responses, or wiring CORS for a decoupled frontend.
---

# Vvveb Headless API

Vvveb exposes the same data to decoupled frontends twice: REST at `/rest/` and GraphQL at `/graphql`. Both are disabled unless configured — check `env.php` (`REST = true`) and role permissions first when an endpoint 404s or 401s.

References: `docs/developer/rest-api.md` (+ `headless/rest-api.md` for setup/CORS), `docs/developer/graphql-api.md` (+ `headless/graphql.md`, `headless/graphql-schema.md`), route tables `config/rest-routes.php` and `config/graphql-routes.php` (+ `config/graphql.php`, `config/rest.php`).

## Workflow

1. Find the closest existing route in `config/rest-routes.php` (or `config/graphql-routes.php`) and copy its module/methods/schema shape.
2. Implement or reuse the backing `rest/controller/` module; keep schemas in sync with the models they expose.
3. Verify with `curl` from the project root: unauthenticated GET for public reads, Basic/Bearer for admin writes, and an explicit CORS check for browser frontends.

## REST

Base path `/rest/`, enabled with `REST = true` in `env.php`. Resources follow `/rest/{resource}` and `/rest/{resource}/{id}`; the table maps each pattern to a module, HTTP methods and schema:

```php
'/rest/posts'            => ['module' => 'default', 'methods' => ['get', 'post'], 'schema' => 'post'],
'/rest/posts/{slug}'     => ['module' => 'default/crud/index', 'methods' => ['get', 'post', 'put', 'patch', 'delete'], 'schema' => 'post'],
'/rest/media/{file}'     => ['module' => 'media/media/index'],
'/rest/themes/{slug}'    => ['module' => 'theme/theme/index'],
'/rest/plugins/{slug}'   => ['module' => 'plugin/plugin/index'],
```

Placeholders: `{slug}` matches a string segment, `#post_id#` matches a numeric segment and becomes a named request param.

Authentication — any admin user with REST role permissions; session cookies also work:

```bash
curl --user demo:demo 'https://demo.vvveb.com/rest/posts/hello-world-1'
curl -H 'Authorization: Bearer mytoken' 'https://demo.vvveb.com/rest/posts/hello-world-1'
```

## GraphQL

Endpoint `/graphql` (schema at `/graphql/schema`), routes in `config/graphql-routes.php`:

```php
'/'               => ['module' => 'index'],
'/graphql'        => ['module' => 'index'],
'/graphql/schema' => ['module' => 'schema/index'],
```

See `docs/developer/graphql-api.md` and `headless/graphql-schema.md` for the type map before adding fields.

## Pitfalls

- `REST = false` or missing role permission looks like a broken endpoint — check config and roles before touching code.
- New resources need **both** a route entry and a backing module; one without the other 404s.
- `{param}` vs `#id#` mismatch silently fails to bind — copy the placeholder style of the neighboring routes.
- Browser frontends need CORS setup (`headless/rest-api.md`) — curl success plus browser failure means CORS, not auth.
