> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ooneex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Networking

> How the app/API and microservices reach each other over the network — distinct ports per service and env-var-driven service discovery declared in config.

In an Ooneex project the main app/API and each microservice are **independent processes**. They do not share a runtime — they communicate over HTTP, each listening on its own port, and the API finds a microservice by resolving a URL it reads from configuration. The `microservice:create` generator wires this up for you: it assigns the new service a free port, declares it in `modules/app/app.yml`, and adds an entry to the project root `.env.yml` so you can supply the real URL per environment.

The discovery is **env-var-driven**. The API never hard-codes where a microservice lives. Instead, `app.yml` names the environment variable that holds the URL, and the actual value comes from `.env.yml` for the current environment. Swap the value per environment (local, staging, production) without touching code.

## How it works

| Concern                      | Where it lives                                 | Value                                       |
| ---------------------------- | ---------------------------------------------- | ------------------------------------------- |
| Main app / API port          | The app process                                | `3000`                                      |
| Microservice port            | The microservice's own `.env.yml` (`app.port`) | Next free port from `3001` upward           |
| Service declaration          | `modules/app/app.yml` under `microservices:`   | `name` + the env var `name` holding the URL |
| URL env var name             | `app.yml` `url` field                          | Pattern `MICROSERVICE_<SNAKE_UPPER>_URL`    |
| Actual URL per environment   | Root `.env.yml` under `microservices:`         | You fill in `url` for each environment      |
| Service-to-service transport | Over HTTP at the configured URL                | `http://...` resolved at runtime            |

The main app/API always runs on port `3000`. When you scaffold a microservice, the generator scans every `modules/*/.env.yml` for `port:` values already in use and picks the **next free port starting at `3001`** — so each microservice gets its own distinct port and no two services collide. That chosen port is written to the new microservice's own `.env.yml` under `app.port`.

## Service declaration in `app.yml`

So the API can reach a microservice, the generator declares it in `modules/app/app.yml` under a `microservices:` list. Each entry pairs a `name` with the **name of the environment variable** that holds the service's URL:

```yaml modules/app/app.yml theme={null}
microservices:
  # billing microservice
  - name: "billing"
    url: MICROSERVICE_BILLING_URL # env var name
```

The `url` value is not a URL — it is the name of the environment variable (pattern `MICROSERVICE_<SNAKE_UPPER>_URL`) that holds the actual URL. This indirection is what makes discovery environment-aware: the same declaration resolves to a different address in local, staging, and production.

## Root `.env.yml` URL map

The generator also adds the microservice to the project root `.env.yml` under a `microservices:` map. This is where you fill in the real URL for each environment:

```yaml .env.yml theme={null}
microservices:
  billing:
    url: ""
```

Set `url` to the address where the service is reachable in that environment — for example `http://localhost:3001` locally, or the deployed hostname in staging and production. The declaration in `app.yml` reads this value through its named environment variable.

## Per-microservice runtime config

Each microservice carries its own runtime configuration in its own `.env.yml` — its distinct port plus its own datastores. A service does not share the API's Redis or Postgres; it gets its own:

```yaml modules/billing/.env.yml theme={null}
app:
  port: 3001 # distinct, assigned automatically from 3001+

cache:
  url: redis://localhost:6379
pubsub:
  url: redis://localhost:6379
rate_limit:
  url: redis://localhost:6379

database:
  url: postgresql://ooneex:ooneex@localhost:5432/ooneex
```

The `app.port` here is the port the generator assigned. The Redis URLs (`cache`, `pubsub`, `rate_limit`) and the Postgres `database.url` are the service's own backing stores — keep them separate so each service owns its data.

## Talking between services

Communication is over **HTTP** at the configured URL. The API resolves a microservice's address from its `app.yml` declaration (the named env var) and the value supplied in `.env.yml`, then issues an HTTP request to that address. Use the framework's HTTP client utility (`@ooneex/fetcher`) to make the call — it is the standard way to perform HTTP requests in an Ooneex app.

Because the address always comes from configuration, you never embed a hostname in code. Change the environment, change the URL, and the calling service follows.

## Best practices

* **Supply URLs per environment.** Fill in each `microservices.<name>.url` in `.env.yml` for local, staging, and production — inject production values through your platform's secrets/env, not committed files.
* **Never hard-code URLs.** Always go through the env-var-named declaration in `app.yml` so the address is resolved from configuration, not baked into source.
* **Keep datastores separate.** Each microservice owns its Redis and Postgres (`cache`, `pubsub`, `rate_limit`, `database`); do not point two services at the same database for shared state — talk over HTTP instead.
* **Let the generator pick ports.** It scans `modules/*/.env.yml` and assigns the next free port from `3001`, so distinct ports stay collision-free; reserve `3000` for the app/API.
* **Talk over HTTP at the configured URL.** Resolve the address from config and call the service with the HTTP client; treat each microservice as a remote process, not a local import.
* **Clean up on removal.** Removing a service with `microservice:remove` strips its entry from `modules/app/app.yml`, the root `.env.yml` map, and its docker-compose block — let the generator do it so nothing dangles.

## Related

* [Microservice overview](/microservice/overview) — what microservices are in an Ooneex project and when to reach for one.
* [Microservice structure](/microservice/structure) — how a generated microservice is laid out.
* [microservice:create](/microservice/create) — the generator that assigns the port and writes these config entries.
* [Configuration](/getting-started/configuration) — how `.env.yml` and `app.yml` are loaded and merged per environment.
* [Fetcher](/utilities/fetcher) — the HTTP client utility for calling services at their configured URLs.
* [Deployment](/advanced/deployment) — supplying per-environment URLs and secrets when you ship.
