Skip to content
Agentmart
Submit a tool

effect-services

Effect 4 services, layers and resources.

Skill by andabove

IndexedMIT
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/andabove-effect-services

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Effect 4 services, layers and resources. Use when you define, compose or provide a dependency with Context.Service, Context.Reference or Layer, manage a resource lifetime with Scope or acquireRelease, read configuration with Config, run Effect code inside Hono, Express, Next.js, Nitro or another framework through ManagedRuntime, or use a platform service such as FileSystem or Path.

Use cases: frontend, backend

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/effect-services

What's inside

Skill name
effect-services
Description
Effect 4 services, layers and resources. Use when you define, compose or provide a dependency with Context.Service, Context.Reference or Layer, manage a resource lifetime with Scope or acquireRelease, read configuration with Config, run Effect code inside Hono, Express, Next.js, Nitro or another framework through ManagedRuntime, or use a platform service such as FileSystem or Path.
License
MIT
Instructions
262 lines in SKILL.md · view on GitHub

Files (6)

  • SKILL.md
  • effect-services-config.md · references/
  • effect-services-frameworks.md · references/
  • effect-services-layers.md · references/
  • effect-services-platform.md · references/
  • effect-services-resources.md · references/

SKILL.md (first lines)

# Effect services, layers and resources

Checked against `effect 4.0.0` and `@effect/platform-node 4.0.0`. When the project has a newer version, read `node_modules/effect/src/Context.ts`, `Layer.ts`, `Scope.ts`, `Config.ts` and `ManagedRuntime.ts`, and `node_modules/effect/AGENTS.md`. The installed source wins over this skill.

## Decide: service or plain function

A service costs a requirement in every caller's type (`Effect<A, E, R>`), a layer, and a test double. Write a plain function, or an `Effect.fn` function, when the code has nothing to swap and nothing to close: pure logic, data shaping, a calculation, a helper with one caller.

Make a service when one of these is true:

- It reaches outside the process: a database, an HTTP API, a queue, mail, the file system.
- A test needs a fake for it.
- It owns a resource with a lifetime: a pool, a client, a socket, a background fiber.
- It needs configuration, or several modules must share one instance.

Cut services by capability, one per boundary (`UserRepo`, `Mailer`, `Payments`). A service whose methods only call pure functions is ceremony: write those as functions.

## Define a service

```ts
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"
import * as Schema from "effect/Schema"

export class UserNotFound extends Schema.TaggedError<UserNotFound>()("UserNotFound", {
  userId: Schema.String
}) {}

export interface User { readonly id: string; readonly email: string }

export class UserRepo extends Context.Service<UserRepo, {
  findById(id: string): Effect.Effect<User, UserNotFound>
}>()("myapp/users/UserRepo") {
  static readonly layer = Layer.effect(
    UserRepo,
    Effect.gen(function* () {
      const rows = new Map<string, User>([["u1", { id: "u1", email: "ada@example.com" }]])
      const findById = Effect.fn("UserRepo.findById")(function* (id: string) {
        const user = rows.get(id)
        if (user === undefined) return yield* new UserNotFound({ userId: id })
        return user
      })
      return UserRepo.of({ findById })
    })
  )
}
```

- Write `Context.Service<Self, Shape>()("id")`: the class first, the shape second. The id string is the runtime identity. Two services with the same id share one slot, so one value answers for both. Prefix the id with the app or package name and the path: `"myapp/users/UserRepo"`.
- Keep `R = never` on every method of the shape. Read dependencies (`yield* Sql`, `yield* Config.String(...)`) in the layer's constructor, so callers and test doubles never see them. A method typed `Effect<A, E, Sql>` pushes `Sql` into every caller.
- Build the value with `UserRepo.of({ ... })` so TypeScript checks it against the shape. Write methods with `Effect.fn("UserRepo.findById")` to get a span per call.
- Attach layers as static fields: `layer` with its dependencies included, and `layerNoDeps` when tests swap a dependency.
- Read the service with `yield* UserRepo` in `Effect.gen`, or `UserRepo.use((repo) => repo.findById(id))` for one call. The shape type is `UserRepo["Service"]`.
- The second form, `Context.Service<Self>()("id", { make: effect })`, keeps the constructor on the class; its layer is `Layer.effect(this, this.make)`. Use it for a service with one implementation whose shape is whatever `make` returns. Use the interface form when the shape is a contract with a live and a test implementation.

## Context.Reference: a value with a default

Use `Context.Reference` for a setting, a feature flag or a per-request value with a sensible default. It adds nothing to `R`: code reads the default until something provides another value.

```ts
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"

export const NewCheckout = Context.Reference<boolean>("myapp/flags/NewCheckout", {
  defaultValue: () => false
})

const price = Effect.gen(function* () {
  return (yield* NewCheckout) ? 90 : 100
})

export const discounted = price.pipe(Effect.provideService(NewCheckout, true))
```

The built-in services are references too: `Clock.Clock`, `Console.Console`, `Random.Random`, `ConfigProvider.ConfigProvider` and `Tracer.Tracer` need no layer. Override one for a region with `Effect.provideService` or a helper such as `Random.withSeed`. Read time through `Clock`, not `Date.now()`, so a test clock can control it. Details: [references/effect-services-layers.md](references/effect-services-layers.md).

## Compose layers

A `Layer<Out, E, In>` builds the services `Out`, can fail with `E`, and needs `In`.

| Constructor | Use it for |
| --- | --- |
| `Layer.succeed(S, value)` | a value with no construction work |
| `Layer.effect(S, effect)` | a constructor that reads services, config or resources. A resource acquired inside lives as long as the layer. Version 4 has no `Layer.scoped`. |
| `Layer.effectDiscard(effect)` | startup work or a background fiber that provides no service |
| `Layer.unwrap(effect)` | a layer chosen at build time, for example from config |

- `Layer.provide(self, deps)` feeds `deps` into `self` and hides them. `deps` can be one layer or an array.
- `Layer.provideMerge(self, deps)` feeds `deps` into `self` and outputs both.
- `Layer.mergeAll(a, b, c)` sets independent layers side by side. Their inputs add up.

```ts
import * as Config from "effect/Config"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"
import * as Redacted from "effect/Redacted"
import * as Schema from "effect/Schema"

export class SqlError extends Schema.TaggedError<SqlError>()("SqlError", { cause: Schema.Defect() }) {}

interface Pool {
  query(text: string): Promise<ReadonlyArray<unknown>>
  end(): Promise<void>
}
declare function openPool(url: string): Pool

export class Sql extends Context.Service<Sql, {
  query(text: string): Effect.Effect<ReadonlyArray<unknown>, SqlError>
}>()("myapp/db/Sql") {
  static readonly layer = Layer.effect(
    Sql,
    Effect.gen(function* () {
      const url = yield* Config.Redacted("DATABASE_URL")
      const pool = yield* Effect.acquireRelease(
        Effect.sync(() => openPool(Redacted.value(url))),
        (pool) => Effect.promise(() => pool.end())
      )

From andabove/skills, MIT.

More from andabove/skills

Signals

Stars
0
Forks
0
Open issues
0
Last commit (this folder)
Oct 3, 2026 (26 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from andabove

Cite this page

effect-services skill. Agent Mart. https://agentmart.dev/skills/andabove-effect-services (last updated 2026-10-24)