# Project Structure

A Zuplo project is a standard Node.js-style project managed via Git. Here is the
typical layout:

```
my-api/
├── config/
│   ├── routes.oas.json     # Route definitions (OpenAPI format)
│   └── policies.json       # Policy configuration
├── modules/
│   └── my-handler.ts       # Custom handlers and policies
├── docs/                   # Developer portal (optional)
│   └── zudoku.config.ts
├── zuplo.jsonc             # Project configuration
├── package.json
├── .env                    # Local environment variables (do not commit)
└── .env.zuplo              # Generated by `zuplo link` (do not commit)
```

## Core files

### `config/routes.oas.json`

This is an OpenAPI 3.1 specification that defines your API routes. Each route
specifies the HTTP method, path, handler, and which policies to apply. Zuplo
extends the OpenAPI spec with `x-zuplo-route` to attach handlers and policies to
each operation.

```json
{
  "paths": {
    "/users/{userId}": {
      "get": {
        "operationId": "get-user",
        "x-zuplo-route": {
          "corsPolicy": "anything-goes",
          "handler": {
            "export": "urlForwardHandler",
            "module": "$import(@zuplo/runtime)",
            "options": {
              "baseUrl": "https://api.example.com"
            }
          },
          "policies": {
            "inbound": ["api-key-inbound", "rate-limit-inbound"]
          }
        }
      }
    }
  }
}
```

You can have multiple OpenAPI files. They are processed in alphabetical order
during route matching.

See [OpenAPI](../articles/openapi.mdx) and [Routing](../articles/routing.mdx)
for details.

### `config/policies.json`

This file defines policy instances by name, type, and configuration. Routes
reference policies by name in their `policies.inbound` and `policies.outbound`
arrays.

```json
{
  "policies": [
    {
      "name": "api-key-inbound",
      "policyType": "api-key-inbound",
      "handler": {
        "export": "ApiKeyInboundPolicy",
        "module": "$import(@zuplo/runtime)"
      }
    },
    {
      "name": "rate-limit-inbound",
      "policyType": "rate-limit-inbound",
      "handler": {
        "export": "RateLimitInboundPolicy",
        "module": "$import(@zuplo/runtime)",
        "options": {
          "rateLimitBy": "ip",
          "requestsAllowed": 100,
          "timeWindowMinutes": 1
        }
      }
    }
  ]
}
```

### `zuplo.jsonc`

Project-level configuration including the runtime compatibility date and
deployment type.

```json
{
  "version": 1,
  "compatibilityDate": "2025-02-06",
  "projectType": "managed-edge"
}
```

The `projectType` can be `managed-edge`, `managed-dedicated`, or `self-hosted`.
The `compatibilityDate` locks runtime behavior so updates don't break your
project unexpectedly.

See [Project Configuration](../programmable-api/zuplo-json.mdx) for all options.

### `modules/`

Contains your custom TypeScript code for handlers and policies. These modules
are referenced from `routes.oas.json` and `policies.json` using
`$import(./modules/...)`.

```json
{
  "handler": {
    "export": "default",
    "module": "$import(./modules/my-handler)"
  }
}
```

### `docs/` (optional)

If you use the Zuplo Developer Portal, this directory contains the portal
configuration and custom pages. See
[Developer Portal](../dev-portal/introduction.mdx) for details.

## How the files relate

1. **`zuplo.jsonc`** sets project-wide configuration (runtime version,
   deployment type)
2. **`config/routes.oas.json`** defines API routes and wires each route to a
   handler and policies by name
3. **`config/policies.json`** defines the named policy instances with their
   configuration and points to either built-in modules (`@zuplo/runtime`) or
   custom modules in `./modules`
4. **`modules/`** contains the TypeScript implementations for custom handlers
   and policies

All of this lives in Git and deploys automatically when you push.

## The `$import()` syntax

JSON configuration files (`routes.oas.json` and `policies.json`) use the
`$import()` syntax to reference code modules. This is a Zuplo-specific syntax
that resolves module references at build time.

```json
{
  "module": "$import(@zuplo/runtime)"
}
```

References starting with `@zuplo/runtime` point to built-in Zuplo modules
(policies, handlers, and utilities).

```json
{
  "module": "$import(./modules/my-handler)"
}
```

References starting with `./modules/` point to your custom TypeScript files in
the `modules/` directory. The `export` field specifies which named export to use
from that module.
