
Product
Introducing Repository Access Permissions and Custom Roles
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.
@fluojs/discord
Advanced tools
Webhook-first, transport-agnostic Discord delivery core for Fluo with notifications integration.
English 한국어
Webhook-first, transport-agnostic Discord delivery core for fluo. It provides a Nest-like module API, an injectable DiscordService for standalone usage, and a first-party DiscordChannel for @fluojs/notifications integration without assuming a Node-only Discord SDK.
Migration boundary: the module API is intentionally Nest-like but not a NestJS dynamic-module clone. DiscordModule is global by default through global: options.global ?? true, forRootAsync(...) supports only inject plus useFactory, and internal provider helpers/tokens stay private so applications compose Discord through the module facade and exported service/channel tokens.
npm install @fluojs/discord @fluojs/notifications
This package follows the repo-wide Node.js 20+ install baseline reflected in published package metadata, while keeping its delivery contract transport-agnostic at runtime through explicit fetch-compatible boundaries.
@fluojs/notifications.process.env reads inside the package.import { Module } from '@fluojs/core';
import { DiscordModule, createDiscordWebhookTransport } from '@fluojs/discord';
@Module({
imports: [
DiscordModule.forRoot({
defaultThreadId: 'release-thread-id',
transport: createDiscordWebhookTransport({
fetch: globalThis.fetch.bind(globalThis),
webhookUrl: 'https://discord.com/api/webhooks/123/abc',
}),
}),
],
})
export class AppModule {}
import { Inject } from '@fluojs/core';
import { DiscordService } from '@fluojs/discord';
@Inject(DiscordService)
export class DeployNotifier {
constructor(private readonly discord: DiscordService) {}
async announce(version: string) {
await this.discord.send({
content: `Deploy ${version} finished successfully.`,
});
}
}
DiscordServiceUse DiscordService when your application wants direct Discord delivery without routing through the notifications foundation.
DiscordModule.forRootAsync({
inject: [ConfigService],
useFactory: (config) => ({
defaultThreadId: config.discord.defaultThreadId,
transport: createDiscordWebhookTransport({
fetch: config.runtime.fetch,
webhookUrl: config.discord.webhookUrl,
}),
}),
});
forRootAsync(...) accepts the fluo async shape only: register dependencies elsewhere in the application graph, list their tokens in inject, and return final DiscordModuleOptions from useFactory. It does not consume NestJS imports, useClass, or useExisting variants, so migrate those patterns to application-owned providers before passing resolved options to Discord.
Behavioral contract notes:
DiscordModule.forRoot(...) and DiscordModule.forRootAsync(...) export DiscordService, DiscordChannel, DISCORD, and DISCORD_CHANNEL globally by default. Use the fluo global?: boolean option and set global: false only when migrated code must keep Discord providers local to importing modules; NestJS isGlobal is not supported.DiscordService.send(...) resolves defaultThreadId before delivery.DiscordService.sendMany(...) is a direct DiscordMessage[] batch API that sends messages sequentially and supports continueOnError; it is not a multi-recipient @fluojs/notifications dispatch shortcut.ready; attempts before bootstrap, during startup, after failed bootstrap, while shutting down, or after shutdown are rejected before delivery.DiscordService.createPlatformStatusSnapshot() exposes the same status contract as createDiscordPlatformStatusSnapshot(...): lifecycle/readiness, health, transport kind and ownership, default thread configuration, bootstrap verification state, and notifications channel dependency details, so callers can observe Discord wiring without reaching into internal options.defaultThreadId and notifications.channel values are trimmed and ignored; the notifications channel defaults to discord.process.env directly. All configuration must enter through explicit options or DI.@fluojs/notificationsInject DISCORD_CHANNEL into NotificationsModule.forRootAsync(...) so the Discord package remains the only place that understands Discord-specific payload fields and recipient-to-thread translation.
import { Module } from '@fluojs/core';
import { NotificationsModule } from '@fluojs/notifications';
import {
DISCORD_CHANNEL,
DiscordModule,
createDiscordWebhookTransport,
} from '@fluojs/discord';
@Module({
imports: [
DiscordModule.forRoot({
transport: createDiscordWebhookTransport({
fetch: globalThis.fetch.bind(globalThis),
webhookUrl: 'https://discord.com/api/webhooks/123/abc',
}),
}),
NotificationsModule.forRootAsync({
inject: [DISCORD_CHANNEL],
useFactory: (channel) => ({
channels: [channel],
}),
}),
],
})
export class AppModule {}
Supported notification payload fields:
content, embeds, components, attachmentsallowedMentions, username, avatarUrl, ttsthreadId, threadName, flags, poll, metadataBehavioral contract notes:
payload.threadId or a single entry in recipients.payload.threadId is omitted, DiscordService.sendNotification(...) uses the first recipients entry or falls back to defaultThreadId.template is rendered only when a renderer is configured.DiscordService.sendMany(...) or issue separate notification dispatches; a single notification dispatch never expands multi-recipient fan-out implicitly.Use createDiscordWebhookTransport(...) when you want a portable first-party transport that only depends on a fetch-compatible HTTP boundary.
const transport = createDiscordWebhookTransport({
fetch: runtime.fetch,
webhookUrl: discordWebhookUrl,
});
await discord.send({
content: 'Deploy finished',
embeds: [{ description: 'Build 124 succeeded.' }],
});
For richer API integrations such as bot-backed REST delivery, implement the exported DiscordTransport contract and inject it through DiscordModule.forRoot(...) or forRootAsync(...).
Behavioral contract notes:
408, 429, and 5xx responses, and also retries transport-level exceptions, using bounded exponential backoff before surfacing an error. Permanent upstream responses are not retried.DiscordSendResult.response; caller-visible DiscordTransportError messages still omit raw upstream response bodies by default, including after rate-limit retries fail.webhookUrl values are rejected immediately as DiscordConfigurationError instead of being retried as delivery failures.The Discord package intentionally does not:
process.envThese limitations are part of the package contract so runtime choice, provider capability, and rollout strategy stay explicit at the application boundary.
DiscordDiscordModule.forRoot(options) / DiscordModule.forRootAsync(options)DiscordModuleOptionsDiscordAsyncModuleOptionsDiscordServiceDiscordService.send(message, options)DiscordService.sendMany(messages, options)DiscordService.sendNotification(notification, options)DiscordService.createPlatformStatusSnapshot()DiscordChannelDISCORDDISCORD_CHANNELCompose applications through DiscordModule and integrate notifications through DISCORD_CHANNEL plus the exported transport contracts.
The package intentionally keeps createDiscordProviders(...), DISCORD_OPTIONS, and NormalizedDiscordModuleOptions out of the public root barrel. If a migration previously customized NestJS internals or provider tokens, wrap DiscordModule.forRoot(...) / forRootAsync(...) in an app-owned module instead of importing private helpers.
DiscordMessageNormalizedDiscordMessageDiscordWebhookTransportOptionsDiscordFetchLikeDiscordFetchResponseDiscordSendResultDiscordSendOptionsDiscordSendManyOptionsDiscordSendBatchResultDiscordSendFailureDiscordNotificationPayloadDiscordNotificationDispatchRequestDiscordAllowedMentionsDiscordAttachmentDiscordComponentDiscordEmbedDiscordPollDiscordTransportDiscordTransportContextDiscordTransportFactoryDiscordTransportReceiptDiscordTemplateRenderInputDiscordTemplateRenderResultDiscordTemplateRenderercreateDiscordWebhookTransport(options)DiscordService.createPlatformStatusSnapshot()createDiscordPlatformStatusSnapshot(...)DiscordLifecycleStateDiscordPlatformStatusSnapshotDiscordStatusAdapterInputDiscordConfigurationErrorDiscordMessageValidationErrorDiscordTransportError@fluojs/notifications: Shared orchestration layer that consumes DISCORD_CHANNEL.@fluojs/config: Recommended for resolving webhook URLs or thread ids without direct environment access.@fluojs/event-bus: Useful when Discord notifications are one side effect among several event-driven workflows.packages/discord/src/module.test.ts: Module registration, async wiring, webhook transport, and notifications integration examples.packages/discord/src/public-surface.test.ts: Public export and TypeScript contract verification.packages/discord/src/status.test.ts: Health/readiness contract examples.FAQs
Webhook-first, transport-agnostic Discord delivery core for Fluo with notifications integration.
We found that @fluojs/discord 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.

Product
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.

Product
Socket MCP now lets AI assistants review org alerts, investigate threats using the Socket threat feed, and inspect package files in addition to dependency scoring.

Product
Socket Firewall blocks malicious VS Code and Open VSX extensions before install, protecting developers from compromised editor marketplaces.