
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
effect-slack
Advanced tools
An Effect-native Slack SDK ✨
catchTag/catchTags for precise error handling@slack/web-api typesbun add effect-slack effect
# or
npm install effect-slack effect
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)
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 |
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))
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 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).
The library provides two approaches to retry handling:
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 }
}
})
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
}
})
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)
)
| 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 |
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 |
All SlackService methods are instrumented with OpenTelemetry-compatible spans.
| 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 |
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)))
The library provides 272 methods across 33 services, auto-generated from the official @slack/web-api types. Services include:
Each service is available as an Effect service with full TypeScript types. See src/generated/ for the complete API.
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))
Services are auto-generated from the @slack/web-api TypeScript definitions:
@slack/web-api/dist/methods.d.ts@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.
MIT
FAQs
An Effect-native Slack SDK
The npm package effect-slack receives a total of 4 weekly downloads. As such, effect-slack popularity was classified as not popular.
We found that effect-slack demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.