# Services

> Directories that Encore runs, tests and deploys as a unit

An Encore service is a directory with an `encore.service.json` file in it. Every Python file in that directory and its subdirectories belongs to the service. Encore runs each service as its own process, gives it its own tests, and grants it access to the infrastructure its code uses.

Services are declared in this file rather than in Python, so Encore.py has no service class or decorator. The service's code is an ordinary Python project with your own web framework.

## Defining a service

A minimal service file names the service, sets its language, and tells Encore which HTTP paths the service serves:

```json
-- encore.service.json --
{
  "name": "orders",
  "lang": "python",
  "api": [
    { "type": "http", "prefix": "/", "name": "orders", "expose": true }
  ]
}
```

Next to it go the service's code, its dependencies and its lockfile:

```
my-app/
├── encore.app
└── orders/
    ├── encore.service.json
    ├── pyproject.toml
    ├── uv.lock
    ├── main.py
    ├── resources.py
    └── migrations/
        └── 1_create_orders.up.sql
```

A single-service app can also put `encore.service.json` at the app root, next to `encore.app`.

The file is parsed as strict JSON: no comments and no trailing commas.

## The service file

| Field | Required | Description |
|---|---|---|
| `name` | Yes | The service's name. Must be non-empty and unique within the app. |
| `lang` | Yes | `"python"` for an Encore.py service. Selects the defaults for `run`, `test` and `build`. |
| `description` | No | A sentence or two about what the service is for, shown in Encore's tools. |
| `api` | No | The HTTP paths (or API specs) the service serves, and whether the API gateway exposes them. See [Serving HTTP](#serving-http). |
| `run` | No | How `encore run` starts the service. See [Running and testing a service](#running-and-testing-a-service). |
| `test` | No | How `encore test` runs the service's tests. |
| `build` | No | How the service is built for deployment. |
| `docker` | No | How `encore build docker` packages the service: `base_image` and `entrypoint`. |
| `workers` | No | The service's background processes, which serve no requests. See [Workers](/docs/py/primitives/workers). |

### Serving HTTP

Encore.py has no API framework, so a Python service usually declares a single `http` entry: a path prefix the service serves itself, with whatever framework you choose. Encore doesn't need to know the routes below the prefix; it forwards requests at or below it to the service.

```json
"api": [
  { "type": "http", "prefix": "/", "name": "orders", "expose": true }
]
```

| Field | Description |
|---|---|
| `prefix` | The path prefix, starting with `/`. |
| `name` | The entry's name, a valid identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to the prefix's last segment, so a prefix of `/` needs an explicit name. |
| `expose` | Whether the app's API gateway forwards outside requests to the prefix: `true`, `false` (the default), or a list of gateway names. |
| `methods` | The uppercase HTTP methods to forward. Omit it to forward all of them. |

A service can list several entries, for example a public prefix and an internal one:

```json
"api": [
  { "type": "http", "prefix": "/orders", "expose": true },
  { "type": "http", "prefix": "/internal", "name": "internal_orders" }
]
```

Instead of an `http` prefix, a service can describe what it serves with an OpenAPI document (`{ "type": "openapi", "spec": "openapi.yaml" }`), a GraphQL schema (`{ "type": "graphql", "schema": "schema.graphql" }`) or protobuf definitions (`{ "type": "grpc", "protos": "proto" }`). Paths are relative to the service directory and can't leave it.

### Running and testing a service

`run`, `test` and `build` are each an object with a `type`. For a Python service they default to type `"python"`, which has no options:

- **`run`** starts the service's entry point, `main.py` (or `app.py`), through the package manager detected from the lockfile in the service directory: `uv run main.py` with a `uv.lock`, `poetry run python main.py` with a `poetry.lock`, and so on. Without a lockfile it runs `python3 main.py`. See [Installing the SDK](/docs/py#installing-the-sdk) for the full list.
- **`test`** runs `pytest` the same way, for example `uv run pytest`.
- **`build`** packages the service's source tree together with its dependency manifests and lockfile, and recreates the environment with the same package manager on deployment.

Encore starts the command in the service's directory and passes the address to listen on in the `ENCORE_LISTEN_ADDR` environment variable, as `host:port`.

To use another command, set the type to `custom` and give the command as a list of arguments:

```json
{
  "name": "orders",
  "lang": "python",
  "run": { "type": "custom", "cmd": ["uv", "run", "python", "-m", "orders"] },
  "test": { "type": "custom", "cmd": ["uv", "run", "pytest", "-x"] }
}
```

A custom `run` can also have a `build` command, run to completion each time before `cmd` starts. The command is run as given, not through a shell.

## Multiple services

An app can have any number of services. Each one is a directory with its own `encore.service.json`, and, for Python, its own dependencies and lockfile, since Encore detects the package manager per service directory:

```
my-app/
├── encore.app
├── orders/
│   ├── encore.service.json
│   ├── pyproject.toml
│   ├── uv.lock
│   ├── main.py
│   └── db.py
└── billing/
    ├── encore.service.json
    ├── pyproject.toml
    ├── uv.lock
    ├── main.py
    └── invoices.py
```

Services in one app don't have to share a language: a Python service can sit next to Go, TypeScript or Rust services, and use resources they declare. Start with one service and split it when there's a reason to; [App Structure](/docs/app-structure) discusses where to draw the boundaries.

## Which service owns what

Encore decides which service a file belongs to from its location: a file belongs to the service whose directory contains it. This is what Encore uses to grant access to infrastructure.

- **A resource is declared by the service whose directory holds the declaration.** For example, a `postgres.Database("orders", migrations="./migrations")` in `orders/db.py` is the `orders` service's database, with migrations in `orders/migrations`.
- **Every use is attributed to the service whose directory contains the code using it**, even if another service imports that file. If `billing` imports a helper from `orders/` that queries a database, the query is still recorded for `orders`.
- **Uses must be inside a service.** Code outside every service directory, such as a shared library, can declare resources but can't use them directly. Have the service pass it a handle instead, for example `db.read()` or `db.write()` for a [database](/docs/py/primitives/databases#handles).

To use another service's resource, reference it by name in your own service rather than importing the other service's code. For a database that's [`postgres.Database.named("orders")`](/docs/py/primitives/databases#using-another-services-database); buckets and topics have the same `named` method.

## Service URLs

Your services call each other over HTTP with any client you like. To find another service's address, use `base_url` from `encore.services`:

```python
-- billing/invoices.py --
import httpx
from encore.services import base_url

async def fetch_order(order_id: int) -> dict:
    async with httpx.AsyncClient() as client:
        response = await client.get(f"{base_url('orders')}/orders/{order_id}")
        response.raise_for_status()
        return response.json()
```

`base_url` returns the service's configured base URL with no trailing slash, so the request path goes right after it. It reads the runtime configuration and makes no network request. A service name the process doesn't know raises `ValueError`.

Looking up a service's URL makes the calling service depend on it, and Encore reads that dependency from your code. So:

- Pass the service name as a string literal: `base_url("orders")`, not `base_url(name)`.
- Call `base_url` directly. Don't store it in a variable or pass the function around.
- Call it from code inside a service.
- Name a service that exists in the app. Encore reports an unknown name when it parses the app.

A service looking up its own URL is allowed, and doesn't count as a dependency.
