Skip to content

@distilled.cloud  ·  87 providers  ·  Apache-2.0

Cloud APIs, distilled into Effect.

Effect-native TypeScript SDKs for 87 cloud providers. Enjoy typed schemas, declarative retry policies, streaming pagination and optimal tree-shaking for 81,352 APIs.

add @distilled.cloud/aws effect
fetch-object.ts
import { Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/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),
    NoSuchBucket: (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)

Built entirely on Effect, not tryPromise

Every operation returns an Effect, requests go through Effect's HttpClient, and inputs and outputs are Effect Schemas. Distilled's core gives you everything you want from an Effect-first SDK: typed error categories, retry policies, pagination as Effect Streams, and a span per request. Every SDK is built on the same core with the same naming patterns, so once you've used one, the rest feel familiar.

Typed errors

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

Match an exact error or a whole category with the Effect functions you already know, like Effect.catchTags and Effect.catchIf. Each SDK is patched with the errors and details its API spec never documents, so nothing comes back as unknown and your types stay accurate.

Retries and backoff

const write = Effect.all([
  // throttled → waits as long as S3 asks, forever
  S3.putObject({ Bucket, Key, Body }),
  // no retry of its own; only the outer loop can bring it back
  S3.deleteObject({ Bucket, Key }).pipe(AWS.Retry.none),
]).pipe(AWS.Retry.throttling)

// around both: 5xx, timeouts, dropped sockets — 4 tries, then fail
write.pipe(Effect.retry({
  while: isTransientError,
  times: 3,
  schedule: Schedule.exponential("250 millis"),
}))

The API's own Retry-After is respected, and you can define your own retry policies and stack them.

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(), so you pull items from an Effect Stream and pages load as you go.Large binary bodies 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
)

// the region comes from the layer…
const head = S3.headObject({ Bucket, Key })

// ...unless one call says otherwise
const eu = head.pipe(Effect.provide(Region.of("eu-central-1")))

Effect.all([head, eu]).pipe(Effect.provide(AwsLive))

Your code just calls S3.headObject. Credentials and regions come from layers: provide them once at the root of your program and override them wherever you need to.

Lazy GraphQL

// at runtime `yield* load()` is a single GraphQL query
const load = Query.fn(() => {
  const me = Railway.me()
  const page = Railway.projects({ first: 20 })
  return {
    email: me.email,
    names: page.pipe(
      Query.map((project) => project.name),
    ),
  }
})

GraphQL queries are abstracted away, just reference the fields you need and a query is generated at runtime.

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",
  })),
)

Operations are ordinary Effects, so they get Effect's built-in OpenTelemetry support.

Strict responses

import { ResponseValidation } from "@distilled.cloud/core"

// every SDK checks each response against its schema
// (additionalProperties: allow fields the spec does not list)
program.pipe(Effect.provide(ResponseValidation.strict))

// a response that drifted from the spec is a typed error
Neon.getProject({ project_id }).pipe(
  Effect.catchTag("NeonParseError", (e) => ...),
)

Responses are lenient by default and come back as the API sent them. Provide one layer and every SDK in your program checks each response against its schema: strict also rejects fields the spec does not list, additionalProperties lets them through. When an API changes shape, the call fails with a typed ParseError instead of handing you the wrong type.

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

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 ever as good as the api spec its built from. Most of these specs aren't generated from the source and regularly drift from the actual implementation (or in cloudflare's case are just blatantly wrong). The spec says a response value is required, your types compile, your runtime fails because the server doesn't return it. We patch the api spec to ensure all inputs, outputs, and errors are correct with live behavior of the providers.

What Fly's spec declaresPOST /v1/apps/{app_name}/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.

  • 33,995spec fixes, and counting
  • 26 of 87providers have needed at least one — including all 9 that Alchemy runs on
  • 1 in 4operations needed a fix on the median provider Alchemy runs on The full tally →

How it works

Generated from the spec. Patched by Alchemy.

How do we patch specs for so many provider and keep stuff accurate? We have a feedback loop with Alchemy; Alchemy uses Distilled to generate resources and run its test suite against the real cloud provider apis, when the tests fail they are investigated and the patches are added back to Distilled for everyone to benefit!

  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 spec.

Providers

87 providers, one shape.

Each provider sdk is one package, produced from the spec and refined by our feedback look and published to npm as @distilled.cloud/<provider>. Pair it with effect. Some specs need a lot of patches see the Wall of Shame →

Clouds9 providers

Platforms13 providers

  • 1.0.0-rc.17
    106 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    104 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    22 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    291 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    275 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    106 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    349 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    177 operations278 spec fixes

    No. 5 of 26 on the Wall of Shame →

    Used in Alchemy

  • 1.0.0-rc.17
  • 1.0.0-rc.17
    262 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    296 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    219 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    437 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Data17 providers

Identity4 providers

Secrets & certificates6 providers

Payments9 providers

  • 1.0.0-rc.17
    28 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    154 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    321 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    73 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    115 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    304 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    190 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    589 operations5 spec fixes

    No. 26 of 26 on the Wall of Shame →

    Used in Alchemy

  • 1.0.0-rc.17
    451 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Observability9 providers

  • 1.0.0-rc.17
    82 operations136 spec fixes

    No. 3 of 26 on the Wall of Shame →

    Used in Alchemy

  • 1.0.0-rc.17
    149 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    1,791 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    250 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    380 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    389 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
  • 1.0.0-rc.17
    247 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    347 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Messaging & support6 providers

  • 1.0.0-rc.17
    188 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
  • 1.0.0-rc.17
    235 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    79 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    340 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    653 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Developer tools7 providers

  • 1.0.0-rc.17
  • 1.0.0-rc.17
    1,194 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    363 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    50 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    187 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    104 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    66 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

Business & analytics6 providers

  • 1.0.0-rc.17
    304 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    714 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    653 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    61 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    127 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

  • 1.0.0-rc.17
    53 operationsno spec fixes

    No spec fixes yet — nothing depends on it.

More1 provider

  • 1.0.0-rc.17
    76 operationsno spec fixes

    No spec fixes yet — nothing depends on it.