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

# Services and layers

> How an Effect app tracks the services its handlers need, how to provide them with provide(app, layer), and how to serve the app with ReintersectApp.layer on HttpLayerRouter.

> Reintersect apps are in developer preview. Names and limits can still change before general
> release.

Handlers get their dependencies, such as a database, an API client or a queue, as Effect services.
Each piece records the services it yields in its type, the app collects them, and the compiler
refuses to serve the app until every one is provided.

## How requirements are tracked

A piece needs every service its handler yields. A block also needs the services of its view and its
actions, and a command or message action needs the services of the Effect it defers. The app needs
the services of all its pieces:

```ts theme={null}
import { Context, Effect } from "effect";

export class Tracker extends Context.Tag("Tracker")<
  Tracker,
  { readonly move: (id: string, status: string) => Effect.Effect<void> }
>() {}
```

```ts theme={null}
export const moveTask = defineAction(
  "move",
  { args: MoveTask, state: TaskState },
  function* ({ args }) {
    const tracker = yield* Tracker;

    yield* tracker.move(args.id, args.status);

    return { state: { status: args.status } };
  },
);
```

`moveTask` needs `Tracker`, so does any block that lists it in `actions`, and so does the app that
adds the block. Until `Tracker` is provided, calling `app.fetch` or `toNodeHandler(app)` is a type
error that names `Tracker`. `ReintersectApp.layer(app)` accepts the app and passes `Tracker` on as a
requirement of the router layer.

Services read inside a [component that returns an Effect](/effect/blocks-and-references#components-that-return-an-effect)
aren't tracked, so provide those yourself.

## Provide services with a layer

`provide(app, layer)` returns a new app whose remaining requirements are the layer's own:

```ts src/app.ts theme={null}
import { createReintersectApp, provide } from "@reintersect/sdk/effect/app";
import { TaskBlock } from "./taskBlock";
import { TrackerLive } from "./tracker";

export const tasksApp = createReintersectApp({ name: "Tasks" }).addBlock(TaskBlock);

export const app = provide(tasksApp, TrackerLive);
```

Export the app before `provide` too, so tests can provide fakes instead. See
[Testing](/effect/testing#fake-a-service).

When a layer needs another service, provide that one as well, either by composing layers first or by
calling `provide` again:

```ts theme={null}
import { provide } from "@reintersect/sdk/effect/app";
import { Layer } from "effect";
import { tasksApp } from "./app";
import { HttpTrackerLive, TrackerConfigLive } from "./tracker";

export const app = provide(tasksApp, HttpTrackerLive.pipe(Layer.provide(TrackerConfigLive)));
```

### When the layer is built

The SDK builds the layer the first time a delivery needs it and keeps it for the life of the process,
so every delivery shares the same services. Resources acquired with `Layer.scoped`, such as a
connection pool, stay open until the process exits.

If the layer fails to build, the SDK logs the cause and that delivery answers `500` with the code
`configuration` ("The app's services failed to start."). The next delivery tries to build it again.

On `app.fetch`, a layer that reads `Config` sees the string values in the `env` argument first, such
as Cloudflare Workers bindings, then `process.env`.

## Serve the app

| How you run the app | How to provide services |
| - | - |
| `app.fetch`, `toNodeHandler(app)` or [Reintersect hosting](/hosting/reintersect-hosted) | `provide(app, layer)` before you export it. |
| `ReintersectApp.layer(app)` on `HttpLayerRouter` | `provide(app, layer)`, or leave them as requirements of the router layer and provide them with the rest of your server. |

`ReintersectApp.layer(app, { path })` mounts the delivery path (default `/reintersect`), every
[route](/hosting/routes) and the [MCP server](/mcp/servers) at `/mcp`, under the same root. It
merges with your own routes, and the app's unprovided services become requirements of the server:

```ts src/server.ts theme={null}
import { HttpLayerRouter, HttpServerResponse } from "@effect/platform";
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node";
import { ReintersectApp } from "@reintersect/sdk/effect/app";
import { Config, Layer } from "effect";
import { createServer } from "node:http";
import { tasksApp } from "./app";
import { TrackerLive } from "./tracker";

const Health = HttpLayerRouter.add("GET", "/health", HttpServerResponse.text("ok"));

const HttpServerLive = NodeHttpServer.layerConfig(createServer, {
  port: Config.port("PORT").pipe(Config.withDefault(3000)),
});

HttpLayerRouter.serve(
  Layer.mergeAll(ReintersectApp.layer(tasksApp, { path: "/api/reintersect" }), Health),
).pipe(
  Layer.provide(TrackerLive),
  Layer.provide(HttpServerLive),
  Layer.launch,
  NodeRuntime.runMain,
);
```

`ReintersectApp.layer` only accepts a complete app, the same check `reintersect build` makes: an app
with a missing block or an undeclared environment value is a type error.

For other servers, mount `app.fetch` or `toNodeHandler(app)` as in [Self-hosted](/hosting/self-hosted).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.