Skip to content

@distilled.cloud  ·  85 providers  ·  Apache-2.0

Cloud APIs, distilled into Effect.

Typed, Effect-native TypeScript SDKs for 85 cloud providers, generated straight from each one's own API description. No hand-written client to fall behind the API — and when the description is wrong, the fix is a patch, not a fork.

add @distilled.cloud/aws effect
fetch-object.ts
import { Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/unstable/http"
import * as S3 from "@distilled.cloud/aws/s3"
import { Credentials, Region } from "@distilled.cloud/aws"

const program = S3.getObject({ Bucket, Key }).pipe(
  Effect.catchTags({
    NoSuchKey:    () => Effect.succeed(null),
    AccessDenied: (e) => Effect.fail(new Error(e.message)),
  }),
)

const AwsLive = Layer.mergeAll(
  FetchHttpClient.layer,
  Region.fromEnv(),          // AWS_REGION
  Credentials.fromChain(),   // env → ~/.aws → SSO → IMDS
)

program.pipe(Effect.provide(AwsLive), Effect.runPromise)

What you get

Built on Effect, not wrapped in it.

Every operation returns an Effect. So the things an SDK usually bolts on — error handling, retries, pagination, configuration, tracing — are the Effect features you already use, with the provider's specifics filled in.

Typed errors

S3.getObject({ Bucket, Key }).pipe(
  Effect.catchTags({
    NoSuchKey:     () => Effect.succeed(null),
    AccessDenied:  (e) => Effect.fail(new Forbidden(e)),
  }),
  Effect.catchIf(isThrottlingError, () => backOff),
)

Match the exact error with catchTags, or a category — throttling, not-found, conflict — with catchIf. Nothing is unknown, and the compiler tells you when you've missed one.

Retries and backoff

// Default: exponential from 250ms, cap 5s, jitter,
// 8 tries, transient/throttling only, Retry-After honoured.
program.pipe(AWS.Retry.throttling)   // retry throttles forever
program.pipe(AWS.Retry.none)         // or not at all
program.pipe(AWS.Retry.policy({      // or your own
  while:    isTransientError,
  schedule: Schedule.exponential("100 millis")
              .pipe(Schedule.recurs(3)),
}))

Transient and throttling errors are retried with backoff and jitter; a 404 never is. The API's own Retry-After is respected. Override the policy for one call or for the whole program — it's a Layer.

Streaming pagination

// Paginated: pages fetch themselves, on demand.
const stale = Lambda.listFunctions.items({}).pipe(
  Stream.filter((fn) => fn.Runtime === "nodejs16.x"),
  Stream.take(50),
  Stream.runCollect,
)

// Bodies stream in and out — nothing is buffered.
const copy = S3.getObject({ Bucket: src, Key }).pipe(
  Effect.flatMap((o) =>
    S3.putObject({ Bucket: dst, Key, Body: o.Body! })),
)

Every paginated operation has .items() and .pages(). Large bodies — an S3 object, an upload — stream in and out without being held in memory. Cancel the Effect and the requests stop.

Layered configuration

const AwsLive = Layer.mergeAll(
  FetchHttpClient.layer,
  Region.fromEnv(),
  Credentials.fromChain(),   // env → ~/.aws → SSO → IMDS
)

// In tests: same program, fake wire.
program.pipe(Effect.provide(MockHttpClient))

Your code calls S3.getObject; where the credentials come from is decided at the edge of the program. In tests, swap the HTTP client for a fake and nothing else changes — that is how the benchmarks on this site run without a network.

OpenTelemetry spans

// Effect's HttpClient opens a span per request with the
// standard http.* and server.* attributes.
program.pipe(
  Effect.withSpan("provision-bucket"),
  Effect.provide(OtlpTracer.layer({
    url: "http://collector:4318/v1/traces",
  })),
)

They are ordinary Effect spans, so they nest inside yours and go wherever your OpenTelemetry exporter sends them — Axiom, Datadog, Honeycomb, a local collector.

Per-operation imports

// the bundle keeps getObject — not the other 111 S3 ops
import { getObject } from "@distilled.cloud/aws/s3"
gzipped, one S3 operation
118.4 KB
gzipped, one Workers operation
61.7 KB
Cloudflare call, p50
31 µs
AWS call incl. SigV4, p50
442 µs

Every operation is its own export, so the bundler keeps only the ones you call — 1 of 112 S3 operations survives in the measured build. Runs on Node, Bun and Workers. See all the numbers →

The problem

The spec should be the only source. It never is.

Every generated SDK — ours, the vendor's, the one you'd write — is only as true as the API description it was built from. Most of those descriptions are written by hand, beside the API rather than from it. So they drift: a field marked required that the server omits, a value documented as a string that arrives null, a property the response always carries that the spec never mentions — and, almost universally, silence about what happens when a call fails. The types compile. The happy path works. Then a real response doesn't match, and your typed client either rejects a valid payload or hands you an untyped error.

What Fly's spec declaresPOST /v1/apps/{app}/machines
responses:
  "200": OK
  — that's it —

One response. No 400 for a bad config, no 403 for a scoped token, no 409 when the name is taken.

What Distilled shipsFly.Machines.createMachine
export type CreateMachineError =
  | BadRequest   // 400 · seen live
  | Forbidden    // 403 · seen live
  | NotFound     // 404
  | Conflict     // 409 · name-taken race
  | FlyIoOpError

Four typed failures in the operation's error union, each added as a patch to the spec the moment a real deploy surfaced it.

  • 25,345spec fixes, and counting
  • 20 of 85providers have needed at least one — including 10 of the 12 that Alchemy runs on
  • 1 in 4operations needed a fix on the median provider Alchemy runs on The full tally →

You could patch the generated code by hand — and lose it on the next regeneration. Or you could fix the description once, and every build after it inherits the correction.

How it works

Generated from the spec. Proven by Alchemy.

Distilled turns each provider's API description into an SDK. Alchemy builds real infrastructure on those SDKs — resources, providers, tests against the live API. Every place the description turns out to be wrong comes back as a patch, and the next generation starts from the corrected description.

  1. Alchemy

    Infrastructure as code.

    • Resources: Bucket, Table, Machine
    • Tests against the actual API
  2. Distilled

    Effect-native SDKs.

    • Schemas, operations, typed errors
    • Retry, pagination, streaming
  3. Spec

    The provider's own API description.

Providers

85 providers, one shape.

Each provider is one package, generated from that provider's own API description and published to npm as @distilled.cloud/<provider>. Pair it with effect. Some descriptions needed more fixing than others — see the Wall of Shame →

Clouds9 providers

Platforms13 providers

  • 1.0.0-rc.12
    106 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    65 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    275 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    274 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    106 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    343 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    146 operations231 spec fixes

    No. 3 of 20 on the Wall of Shame →

    Used in Alchemy

  • 1.0.0-rc.12
  • 1.0.0-rc.12
    251 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    419 operations108 spec fixes

    No. 12 of 20 on the Wall of Shame →

    Used in Alchemy

  • 1.0.0-rc.12
    279 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    200 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    408 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Data16 providers

Identity4 providers

Secrets & certificates6 providers

  • 1.0.0-rc.8zero fixes
    12 operationsno spec fixes

    Honour roll — generated as published, and Alchemy runs on it.

  • 1.0.0-rc.12
    128 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    2,298 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    15 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    85 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.8zero fixes
    1 operationsno spec fixes

    Honour roll — generated as published, and Alchemy runs on it.

Payments9 providers

  • 1.0.0-rc.12
    28 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    146 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    133 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    72 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    115 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    305 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    186 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    566 operations4 spec fixes

    No. 19 of 20 on the Wall of Shame →

    Used in Alchemy

  • 1.0.0-rc.12
    388 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Observability9 providers

  • 1.0.0-rc.12
    80 operations92 spec fixes

    No. 5 of 20 on the Wall of Shame →

    Used in Alchemy

  • 1.0.0-rc.12
    149 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    1,690 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    250 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    363 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    389 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
  • 1.0.0-rc.12
    221 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    339 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Messaging & support6 providers

  • 1.0.0-rc.12
    172 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
  • 1.0.0-rc.12
    231 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    79 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    317 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    636 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Developer tools7 providers

  • 1.0.0-rc.12
  • 1.0.0-rc.12
    1,187 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    324 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    50 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    187 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    104 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    61 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Business & analytics5 providers

  • 1.0.0-rc.12
    276 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    708 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    653 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    84 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.12
    53 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

More1 provider

  • 1.0.0-rc.12
    76 operationsno spec fixes

    No spec fixes yet — nothing depends on it.