# Explore the showcase app

Tour examples/showcase, one schema using every DSL feature, served over HTTP and gRPC from one implementation.

You will run `examples/showcase`: one schema using every DSL feature, served over HTTP and gRPC from one implementation. It takes about 15 minutes. You need Go 1.27+, the `zever` CLI, and a repo clone. For a narrow flow, see [Build a booking API](https://zenta-dev.github.io/zever/tutorials/build-a-booking-api/).

All commands run from `examples/showcase/` unless noted.

```sh
cd examples/showcase
```

## See what one schema can declare

`schema/shop/shop.zen` is the whole domain (`User`, `Profile`, `Category`, `Product`, `Tag`, `Order`, `OrderItem`, `Review`, plus `ShopService`). When you wonder whether the DSL supports a shape, grep this schema first. See [Zen syntax](https://zenta-dev.github.io/zever/reference/zen-syntax/) for each feature alone.

| Feature | Where |
|---|---|
| All 11 scalars | `User` fields |
| Named `enum` | `OrderStatus`, `Role` |
| Inline `enum()` | `Order.priority` |
| Optional field and `@default` | `User.nickname`, `Order.note` |
| `has_many` / `belongs_to` | the `User`/`Order`/`OrderItem`/`Review` graph |
| `has_one` (`@unique` foreign key) | `User.profile` |
| `many_to_many` + reciprocal `join_table` | `Product.tags` |
| `index()` and `@unique` index | `Product`, `Order` |
| `@schema` | `Order` to a separate `billing` schema |
| `@renamed_from` | `Product.headline` (was `title`) |
| `message` request, nested, and returns | `CreateOrderRequest`, `CheckoutRequest`, `OrderReceipt` |
| Every HTTP verb, `auth:` shape, `permission: check`, `errors:`, `paginated: true` | `ShopService` RPCs |
| Job params, queues, retry shapes, `cron` and `dispatch` | 4 jobs, 2 schedules |

## Understand one implementation, two transports

`cmd/server` mirrors `zever generate server`: HTTP (`:8080`) and gRPC (`:9090`) from one container, with auth enforced identically on both through the same generated `GRPCPolicies` plus `authz`. The logic lives once, in `internal/service/shop/shop_service.go` (`ShopServiceImpl`, 9 RPCs); the `/api/*` routes that need richer shapes are thin adapters over it, never a second copy. `cmd/worker` mirrors `zever generate worker`: one handler per job, one registration per schedule.

## Run it

`AUTH_JWT_SECRET` (32+ bytes) is required by every entrypoint; the app fails closed ([Configuration](https://zenta-dev.github.io/zever/guides/operate/configuration/)).

```sh
export AUTH_JWT_SECRET=dev-only-secret-at-least-32-bytes-long

zever db migrate --adapter=sqlite --dsn=data/showcase.db schema/shop/shop.zen
go run ./db/seed          # idempotent
```

Checkpoint:

```text
OK applied 16 statement(s) via sqlite
{"time":"...","level":"INFO","msg":"seed complete"}
```

Serve and work in two terminals:

```sh
go run ./cmd/server       # HTTP :8080 + gRPC :9090
go run ./cmd/worker       # jobs + embedded scheduler
```

Checkpoint, server then worker:

```text
{"time":"...","level":"INFO","msg":"http server listening","addr":":8080"}
{"time":"...","level":"INFO","msg":"grpc server listening","addr":":9090"}
```

```text
{"time":"...","level":"INFO","msg":"worker started","jobs":4,"schedules":2}
```

Prove HTTP parity with curl. Every `/api/*` route except register and login needs a bearer token:

```sh
curl -s -X POST localhost:8080/api/register -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","name":"You","password":"correct horse battery staple"}'
TOKEN=$(curl -s -X POST localhost:8080/api/login -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"correct horse battery staple"}' | jq -r .token)
curl -s localhost:8080/api/products -H "Authorization: Bearer $TOKEN" | head -c 200
```

```text
[{"id":"seed-product-gizmo","category_id":"seed-category-gadgets","sku":"GIZMO-002",...}]
```

An unauthenticated call to a guarded RPC fails identically on `:8080` (`401 missing bearer token`) and `:9090`.

## Inspect the schema without running anything

Every command here is read-only:

```sh
zever check schema/shop/shop.zen
zever fmt schema/shop/shop.zen
zever routes schema/shop/shop.zen
zever graph schema/shop/shop.zen
zever explain ShopService.Checkout schema/shop/shop.zen
zever check-boundaries schema/shop/shop.zen
```

Expected output shape:

```text
# check: OK 1 file(s) valid
# fmt: no output when already formatted (lists files that would change; --write rewrites)
# routes: GET /products -> shop.ShopService.ListProducts, one line per RPC
# graph: Mermaid entity nodes with relation edges
# explain: transports, auth, permission, and error set for Checkout
```

## Regenerate after a schema change

`generated/` is CLI output; never hand-edit it. Run from the repository root so `gogen`/`protogogen` import paths resolve:

```sh
zever compile examples/showcase/schema/shop/shop.zen \
  --backend=atlas,gogen,openapi,proto,protogogen,zenorm \
  --out examples/showcase/generated
```

Checkpoint:

```text
✔ compiled 1 file(s) → examples/showcase/generated (14 outputs, backends: atlas,gogen,openapi,proto,protogogen,zenorm)
```

Everything under `internal/app`, `internal/service`, `internal/api`, the schema, `.env`, `wiring.go`, and the tests is hand-owned and never overwritten.

<details>
<summary>Troubleshooting</summary>

**The server exits naming `AUTH_JWT_SECRET`.** Same fail-closed gate as bookings. Export a 32+ byte dev value in every terminal running an entrypoint.

**`zever compile` writes to the wrong import paths.** You ran it from `examples/showcase/`. Re-run from the repo root.

**HTTP works but gRPC auth behaves differently.** That is a parity-test failure, not a config difference: both transports share `GRPCPolicies` plus `authz`. Run the cross-transport tests and fix the implementation, not the transport wiring.
</details>

## Next steps

- [Backends](https://zenta-dev.github.io/zever/concepts/backends/): What each of the seven backends produces from this schema.
- [Query builder](https://zenta-dev.github.io/zever/guides/data/query-builder/): The typed ORM the zenorm backend generates.
