Slack
Post notifications to Slack via an incoming webhook or the Web API. A fluent SlackMessage builder with text, Block Kit blocks, and attachments.
The Slack channel posts a notification to Slack — either through an incoming webhook or the Web API
(chat.postMessage) with a bot token. You build the message with a fluent builder that supports
plain text, Block Kit blocks, and legacy attachments.
Install
pnpm add @dudousxd/nestjs-notifications-slacknpm install @dudousxd/nestjs-notifications-slackRegister the channel
import { SlackChannelModule } from '@dudousxd/nestjs-notifications-slack';
@Module({
imports: [
// incoming webhook:
SlackChannelModule.forRoot({ webhookUrl: process.env.SLACK_WEBHOOK_URL }),
// or, with the Web API:
// SlackChannelModule.forRoot({ token: process.env.SLACK_BOT_TOKEN, defaultChannel: '#general' }),
],
})
export class AppModule {}SlackChannelModule.forRoot() takes:
| Option | Type | Default | Description |
|---|---|---|---|
webhookUrl | string | — | Default incoming-webhook URL, used when the route doesn't supply one. |
token | string | — | Bot/user token for the Web API. When set, delivery uses chat.postMessage. |
defaultChannel | string | — | Default channel id for Web API delivery when the route isn't a webhook. |
resolveOptions | (tenant: string) => SlackChannelOptions | — | Per-tenant options resolver. See Per-tenant config. |
global | boolean | true | Register globally so the channel is discoverable app-wide. |
The notification side
Annotate the payload method with the @Slack() handle and return a SlackMessage:
import { type Notifiable, Notification } from '@dudousxd/nestjs-notifications-core';
import { Slack, SlackMessage } from '@dudousxd/nestjs-notifications-slack';
@Notification()
export class DeployFinished {
constructor(private service: string) {}
@Slack()
toSlack({ notifiable }: ChannelContext): SlackMessage {
return new SlackMessage()
.text(`Deploy finished: ${this.service}`)
.block({ type: 'section', text: { type: 'mrkdwn', text: `*${this.service}* is live :rocket:` } });
}
}The Slack handle also works as a via() token (via() { return [Slack]; }) for
explicit routing; implement
SlackNotification alongside the decorator for compile-time checks on toSlack().
SlackMessage builder
SlackMessage is a fluent builder; each method returns this:
| Method | Effect |
|---|---|
.text(s) | Set the fallback/notification text. |
.block(b) | Append a Block Kit block. Call repeatedly to add several. |
.attachment(a) | Append a legacy attachment. |
.toPayload() | Serialize to the JSON body Slack expects, omitting empty collections. |
toPayload() returns a SlackPayload ({ text?, blocks?, attachments? }) — the channel calls it
for you, but it's there if you need to inspect the body.
Webhook vs. bot token
The channel picks its delivery mode from the options:
- Bot token — if
tokenis set, every message goes through the Web API (chat.postMessage) with anAuthorization: Bearerheader. The channel id comes from the route (a non-URL string) or falls back todefaultChannel. - Incoming webhook — without a token, the channel POSTs to a webhook URL. It uses the route if it
returned an
https://URL, otherwise the module'swebhookUrl.
If neither a webhook URL nor a token is available, the channel throws asking you to provide one.
Routing
routeNotificationFor('slack') controls the target per notifiable. What you return depends on the
mode:
export class Team implements Notifiable {
constructor(public slackWebhook: string, public slackChannelId: string) {}
routeNotificationFor(channel: string) {
if (channel !== 'slack') return undefined;
// Webhook mode: return an https:// URL to override the default webhook.
return this.slackWebhook;
// Web API mode (token set): return a channel id like '#alerts' or 'C0123456789'.
// return this.slackChannelId;
}
}In webhook mode an https:// route overrides webhookUrl; in Web API mode a non-URL route is used
as the channel id, overriding defaultChannel.
Block Kit blocks and attachments take raw Slack JSON, so you have the full Slack message format
available — buttons, fields, images, context blocks. Build them in Block Kit
Builder and pass each block to .block().
Per-tenant config
In a multi-tenant app each tenant can post to its own workspace. Pass resolveOptions — when a send
is scoped to a tenant (via forTenant(id) or a @Tenant()
property), the channel uses the options you return (webhook URL, token, default channel) instead of
the module defaults:
SlackChannelModule.forRoot({
webhookUrl: process.env.SLACK_WEBHOOK_URL,
resolveOptions: (tenant) => ({ webhookUrl: tenantWebhooks.get(tenant) }),
});Sends with no tenant scope keep the module defaults. See Multi-tenancy for the full picture.
Broadcast
Push notifications to the browser in real time over socket.io. Each notifiable gets its own room; pair it with the database channel for a live in-app feed.
Discord
Post notifications to Discord via an incoming webhook. A fluent DiscordMessage builder with plain content and rich embeds, routed per notifiable.