MCPcopy Index your code
hub / github.com/MateoKruk/effect-slack

github.com/MateoKruk/effect-slack @v0.3.2

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.3.2 ↗ · + Follow
487 symbols 815 edges 69 files 3 documented · 1%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

effect-slack

An Effect-native Slack SDK ✨

Features

  • 100% Type-safe — Full TypeScript types for all 272 methods, arguments, and responses
  • Typed errors — Discriminated unions with catchTag/catchTags for precise error handling
  • Observability built-in — OpenTelemetry spans with rich attributes (method, channel, user, timestamps)
  • Smart retries — Rate limit aware with exponential backoff and jitter
  • Testable by design — Dependency injection via Effect layers, easily mockable
  • Always up-to-date — Auto-generated from official @slack/web-api types

Installation

bun add effect-slack effect
# or
npm install effect-slack effect

Quick Start

import { Effect } from "effect"
import { SlackService } from "effect-slack"

// Using environment variables (SLACK_TOKEN)
const program = Effect.gen(function* () {
  const slack = yield* SlackService

  const result = yield* slack.postMessage({
    channel: "C1234567890",
    text: "Hello from Effect!"
  })

  console.log("Message sent:", result.ts)
}).pipe(Effect.provide(SlackService.Live))

Effect.runPromise(program)

Examples

Check out the examples folder for complete, runnable examples:

Example Description
with-effect-platform Full Effect stack using @effect/platform-bun with Events API, slash commands, and Swagger docs
with-express Express + Effect integration showing gradual Effect adoption for Slack API calls

Custom Configuration

import { Effect, Layer, Redacted } from "effect"
import { SlackService, SlackConfig, SlackClient } from "effect-slack"

const customConfig = SlackConfig.make({
  token: Redacted.make(process.env.MY_SLACK_TOKEN!),
  options: {
    retryConfig: { retries: 5 }
  }
})

const CustomSlackLayer = SlackService.Default.pipe(
  Layer.provide(SlackClient.Default),
  Layer.provide(customConfig)
)

const program = Effect.gen(function* () {
  const slack = yield* SlackService
  // ... use slack methods
}).pipe(Effect.provide(CustomSlackLayer))

Error Handling

All Slack API errors are mapped to typed Effect errors that can be handled with catchTag or catchTags:

import { Effect } from "effect"
import { SlackService } from "effect-slack"

const program = Effect.gen(function* () {
  const slack = yield* SlackService
  return yield* slack.postMessage({ channel: "C123", text: "Hi" })
}).pipe(
  Effect.catchTags({
    SlackRateLimitedError: (e) => Effect.log(`Rate limited, retry in ${e.retryAfter}s`),
    SlackPlatformError: (e) =>
      e.isAuthError
        ? Effect.logError(`Auth failed: ${e.error}`)
        : Effect.logError(`API error: ${e.error}`),
    SlackHttpError: (e) => Effect.logError(`HTTP ${e.statusCode}: ${e.statusMessage}`)
  }),
  Effect.provide(SlackService.Live)
)

Error Types

Error Type Description
SlackRequestError Network failures, DNS errors
SlackHttpError Non-200 HTTP responses with statusCode and body
SlackPlatformError Slack API errors with error code (e.g., channel_not_found)
SlackRateLimitedError Rate limit exceeded, includes retryAfter (seconds)
SlackFileUploadInvalidArgumentsError Invalid file upload arguments
SlackFileUploadReadError Failed to read file data
SlackUnknownError Unexpected errors

SlackPlatformError includes an isAuthError getter that returns true for auth-related errors (invalid_auth, not_authed, token_revoked, token_expired, account_inactive).

Retry Support

The library provides two approaches to retry handling:

SDK-Level Retries (Default)

The underlying @slack/web-api SDK handles retries automatically. You can configure it via SlackConfig:

import { Redacted } from "effect"
import { SlackConfig } from "effect-slack"

const config = SlackConfig.make({
  token: Redacted.make("xoxb-..."),
  options: {
    retryConfig: { retries: 5 }
  }
})

Disabling SDK Retries

To use Effect-level retries exclusively, disable SDK retries:

import { Redacted } from "effect"
import { SlackConfig } from "effect-slack"

const config = SlackConfig.make({
  token: Redacted.make("xoxb-..."),
  options: {
    retryConfig: { retries: 0 }, // Disable SDK retries
    rejectRateLimitedCalls: true // Don't auto-handle rate limits
  }
})

Effect-Level Retries

For more control, use the Effect-native retry utilities:

import { Effect, pipe } from "effect"
import {
  SlackService,
  withDefaultRetry,
  withRateLimitRetry,
  rapidRetryPolicy,
  isRetryableError
} from "effect-slack"

// Apply default retry policy (10 retries with exponential backoff)
const program = pipe(
  Effect.flatMap(SlackService, (slack) =>
    slack.postMessage({ channel: "#general", text: "Hello!" })
  ),
  withDefaultRetry
)

// Or use a custom schedule
const programWithCustomRetry = pipe(
  Effect.flatMap(SlackService, (slack) =>
    slack.postMessage({ channel: "#general", text: "Hello!" })
  ),
  Effect.retry(rapidRetryPolicy)
)

Pre-built Schedules

Schedule Description
tenRetriesInAboutThirtyMinutes 10 retries with exponential backoff + jitter
fiveRetriesInFiveMinutes 5 retries with exponential backoff + jitter
rapidRetryPolicy 3 retries with 100ms delay (for testing)
rateLimitAwareSchedule(opts) Configurable retries with exponential backoff

Retryable Errors

The isRetryableError function determines which errors are safe to retry:

Error Type Retryable Reason
SlackRateLimitedError Yes Transient, has retryAfter
SlackRequestError Yes Network failures
SlackHttpError (5xx) Yes Server errors
SlackHttpError (4xx) No Client errors
SlackPlatformError Partial Only service_unavailable
Auth errors No Won't resolve with retry

Observability

All SlackService methods are instrumented with OpenTelemetry-compatible spans.

Span Attributes

Attribute Description
slack.method Slack API method (e.g., chat.postMessage)
slack.channel Channel ID (where applicable)
slack.user User ID (for user operations)
slack.ts Message timestamp (for updates/deletes)
slack.reaction Reaction name (for reaction operations)
error.type Error tag on failures (e.g., SlackPlatformError)
slack.error Platform error code (e.g., channel_not_found)
http.status_code HTTP status code on HTTP errors
slack.retry_after Retry-After seconds on rate limit errors

Exporting Traces

Use @effect/opentelemetry to export traces to your observability backend:

import { Effect } from "effect"
import { NodeSdk } from "@effect/opentelemetry"
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base"
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"
import { SlackService } from "effect-slack"

const TracingLive = NodeSdk.layer(() => ({
  resource: { serviceName: "my-slack-app" },
  spanProcessor: new BatchSpanProcessor(new OTLPTraceExporter())
}))

const program = Effect.gen(function* () {
  const slack = yield* SlackService
  yield* slack.postMessage({ channel: "#general", text: "Hello!" })
})

Effect.runPromise(program.pipe(Effect.provide(SlackService.Live), Effect.provide(TracingLive)))

Available Methods

The library provides 272 methods across 33 services, auto-generated from the official @slack/web-api types. Services include:

  • ChatService - Messages, threads, scheduled messages
  • ConversationsService - Channels, DMs, group conversations
  • UsersService - User profiles, presence, identity
  • ReactionsService - Emoji reactions
  • FilesService - File uploads and management
  • AdminService - Workspace administration (100+ methods)
  • AppsService, AuthService, BookmarksService, CallsService, ViewsService, and more...

Each service is available as an Effect service with full TypeScript types. See src/generated/ for the complete API.

Testing

The library is designed to be easily testable by providing mock implementations:

import { Effect, Layer } from "effect"
import { SlackService, SlackClient } from "effect-slack"
import type { WebClient } from "@slack/web-api"

// Create a mock WebClient
const mockClient = {
  chat: {
    postMessage: async () => ({ ok: true, ts: "1234.5678" })
  }
} as unknown as WebClient

// Create test layer
const TestLayer = SlackService.Default.pipe(Layer.provide(SlackClient.make(mockClient)))

// Use in tests
const testProgram = Effect.gen(function* () {
  const slack = yield* SlackService
  const result = yield* slack.postMessage({
    channel: "C123",
    text: "Test message"
  })
  return result
}).pipe(Effect.provide(TestLayer))

Architecture

Services are auto-generated from the @slack/web-api TypeScript definitions:

  1. Parse — Extract method signatures from @slack/web-api/dist/methods.d.ts
  2. Generate — Create Effect-wrapped services with typed arguments and responses
  3. Instrument — Add OpenTelemetry spans and error mapping to each method
@slack/web-api types → Parser → Code Generator → Effect Services

Generated services follow a consistent pattern:

// Each method is wrapped with Effect.tryPromise, error mapping, and tracing
const postMessage = (
  args: ChatPostMessageArguments
): Effect.Effect<ChatPostMessageResponse, SlackError> =>
  Effect.tryPromise({
    try: () => client.chat.postMessage(args),
    catch: mapSlackError
  }).pipe(
    Effect.tapError(annotateSpanWithError),
    Effect.withSpan("ChatService.postMessage", {
      attributes: { "slack.method": "chat.postMessage" }
    })
  )

Run bun run generate to regenerate services when updating @slack/web-api.

License

MIT

Extension points exported contracts — how you extend this code

Core symbols most depended-on inside this repo

Shape

Method 285
Class 132
Function 55
Interface 15

Languages

TypeScript100%

Modules by API surface

src/generated/AdminService.ts97 symbols
src/generated/ConversationsService.ts30 symbols
examples/with-effect-platform/src/Schemas.ts22 symbols
scripts/lib/errors.ts20 symbols
src/generated/FilesService.ts18 symbols
src/generated/ChatService.ts15 symbols
src/Errors.ts15 symbols
src/generated/UsersService.ts14 symbols
src/generated/SlackListsService.ts14 symbols
src/SlackService.ts13 symbols
test/SlackService.test.ts12 symbols
src/generated/TeamService.ts11 symbols

For agents

$ claude mcp add effect-slack \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page