
## How it works

Vary lets you write a service as a plain interface and implementation, then tell the compiler how to expose it. You do not write routes, annotations, or framework boilerplate. The `expose` statement handles that.

```vary-snippet
expose ItemService via http
```

The `via http` part is a plugin. Today the only backend is HTTP (powered by Quarkus), but the design is intentional: `expose` separates what your service does from how it gets exposed. Future plugins could target gRPC, WebSockets, message queues, or CLI wrappers without changing the service code. You write the interface once, and the exposure mechanism is a one-line swap.

This is still early. Only the HTTP plugin exists today. But the architecture is there for others.

## The `expose` statement

Define a service as an interface, implement it, and the compiler generates REST and RPC endpoints:

```vary
interface ItemService {
    def get_item(self, id: Int) -> Str {
    }
    def list_items(self) -> Str {
    }
    def create_item(self, name: Str, price: Int) -> Str {
    }
    def delete_item(self, id: Int) -> Str {
    }
}

class ItemServiceImpl() implements ItemService {
    def get_item(self, id: Int) -> Str {
        return json_dumps({"id": id, "name": "Widget"})
    }
    def list_items(self) -> Str {
        return json_dumps([{"id": 1, "name": "Widget"}])
    }
    def create_item(self, name: Str, price: Int) -> Str {
        return json_dumps({"created": True, "name": name})
    }
    def delete_item(self, id: Int) -> Str {
        return json_dumps({"deleted": id})
    }
}

expose ItemService via http
```

Running with `vary run` starts a Quarkus server. The compiler generates JAX-RS resource classes.

## HTTP verb inference

The method name prefix determines the HTTP verb:

| **Prefix** | **HTTP verb** | **Example** |
|--------|-----------|---------|
| `get_` | GET | `get_item` |
| `list_` | GET | `list_items` |
| `find_` | GET | `find_orders` |
| `create_` | POST | `create_item` |
| `update_` | PUT | `update_item` |
| `patch_` | PATCH | `patch_item` |
| `delete_` | DELETE | `delete_item` |

## Path generation

The base path is the interface name in kebab-case: `ItemService` becomes `/item-service`.

The method path is the name with the verb prefix stripped: `get_item` becomes `/item`.

## Parameter rules

Parameters are classified based on their name and the HTTP verb:

| Verb | `id` / `*_id` (Int or Str) | Other parameters |
|------|---------------------------|-----------------|
| GET | Path parameter (`/item/{id}`) | Query parameter (`?name=...`) |
| POST / PUT / PATCH | Path parameter | JSON body |
| DELETE | Path parameter | **Not allowed** (compile error) |

### Path parameters

Any parameter named `id` or ending in `_id` (of type `Int` or `Str`) becomes a path parameter:

```vary-snippet
def get_order(self, user_id: Int, order_id: Int) -> Str {
    # GET /order/{user_id}/{order_id}
}
```

### Query parameters (GET)

For GET requests, non-id parameters become query parameters:

```vary-snippet
def list_notes(self, limit: Int, offset: Int) -> Str {
    # GET /notes?limit=10&offset=0
}
```

### JSON body (POST/PUT/PATCH)

For POST, PUT, and PATCH requests, non-id parameters are sent as a JSON body:

```vary-snippet
def create_note(self, title: Str, body: Str) -> Str {
    # POST /note
    # Body: {"title": "...", "body": "..."}
}
```

If the method takes a single data class parameter, that data class is used directly as the request body. Otherwise, a synthetic request DTO is generated automatically.

### DELETE strictness

DELETE methods only accept id parameters. This prevents ambiguous endpoints:

```vary-snippet
# OK
def delete_note(self, id: Int) {
}

# Compile error: non-id parameter 'reason'
def delete_note(self, id: Int, reason: Str) {
}
```

## Inspecting routes

Run `vary routes <file>` to see your exact endpoints:

```text
$ vary routes main.vary
NoteService (/note-service):
  GET     /note-service/health                get_health() -> Str
  GET     /note-service/notes                 list_notes() -> Str
  GET     /note-service/note/{id}             get_note(id: Int) -> Str
  POST    /note-service/note                  create_note(title: Str, body: Str) -> Str
          Body: { title: Str, body: Str }
  DELETE  /note-service/note/{id}             delete_note(id: Int) -> Str

  GET     /q/openapi                          OpenAPI spec
  GET     /q/swagger-ui                       Swagger UI
```

| Flag | Effect |
|------|--------|
| `--json` | Machine-readable JSON output |
| `--curl` | Copy-paste curl examples |

When you run `vary run`, a route summary is printed in the server startup banner.

## RPC fallback

Every method also gets an RPC endpoint at `POST /<service-path>/rpc/<method_name>` that accepts a JSON body with named parameters.

## OpenAPI / Swagger

Every exposed interface gets OpenAPI documentation automatically:

| Endpoint | Purpose |
|----------|---------|
| `GET /q/openapi` | OpenAPI spec |
| `GET /q/swagger-ui` | Swagger UI |

Synthetic request DTOs appear as named schemas in the OpenAPI spec, making the API self-documenting.

## Typed service clients

While `expose` defines the server side, `service` declarations define typed client stubs that consume HTTP APIs:

```vary-snippet
service UserApi at "https://api.example.com" {
    endpoint get_user(id: Int) -> User via GET "/users/{id}"
    endpoint create_user(body: CreateUserRequest) -> User via POST "/users"
    endpoint delete_user(id: Int) -> Str via DELETE "/users/{id}"
}
```

Service declarations specify:

| Element | Description |
|---------|-------------|
| **Base URL** | Where the API lives |
| **Endpoints** | Name, parameters, return type, method, path |
| **Body parameter** | For POST/PUT/PATCH, `body` is the request body |

### Generating client stubs

Use `vary client` to generate service declarations from existing `expose` statements:

```bash
vary client main.vary            # print to stdout
vary client main.vary -o client.vary  # write to file
```

This inspects the module's exposed interfaces and produces matching `service` declarations.

### Stdlib modules

Two stdlib modules support typed service communication:

| Module | Functions |
|--------|-----------|
| `service_client` | `new(base_url)`, `new_with_headers(base_url, headers)` |
| `service_response` | `decode(response)`, `from_error(error)`, `from_result(result)` |

### Drift detection

The `VCA005` check rule compares service declarations against expose statements and flags mismatches in endpoint names, HTTP methods, paths, parameter types, and return types. See [Check rules](/docs/check/) for details.

### Host restrictions

Configure allowed and denied hosts in `vary.toml` under `[services]`. The `VCS008` rule enforces these restrictions. See [Configuration](/docs/configuration/) for details.

## Port configuration

Set `QUARKUS_HTTP_PORT` to change the listening port (default: 8080).
