Overview of Discord-REST-Listener
mainMonitoRSS/Discord-REST.repository·main·Indexed 22 days ago
https://github.com/synzen/monitorssMonitoRSS (formerly Discord.RSS) is a service that delivers highly-customized news feeds to Discord servers. The documentation covers self-hosting via Docker and Docker Compose, environment variable configuration for Discord, Reddit, and SMTP, and developer guides for E2E testing with Playwright and RabbitMQ messaging using the @monitorss/contracts package.
MonitoRSS/Discord-REST.The services/backend-api/client/docs/adr/ directory contains Architecture Decision Records specifically for the React application served by the backend-api. These records document the architectural constraints, patterns, and trade-offs used in the frontend.
Key Architectural Patterns documented in ADRs:
/me route and slug-based /workspaces/:workspaceSlug/... routes.FeedConnectionType shell by using destination sub-features.MonitoRSS uses slug-based URLs for workspace scoping instead of opaque IDs. This allows for readable and shareable URLs such as /workspaces/acme-marketing/feeds.
When interacting with the API or building client-side routes, use the workspaceSlug rather than a workspaceId. The routing structure follows the pattern /workspaces/:workspaceSlug.
Key technical details:
/workspaces/:workspaceSlug/...RouteScope includes { workspaceSlug?: string }.WORKSPACE_SLUG_TAKEN error code.SLUG_PATTERN and SLUG_MAX constraints defined in the system's shared validation logic.MonitoRSS uses a hybrid URL scoping model to support both personal and workspace-owned resources. This design allows users to manage feeds individually or within shared workspaces (similar to Slack or Notion).
/me/ routes./feeds (List of personal feeds)/feeds/:feedId (Specific feed)/settings/workspaces/:workspaceSlug/ prefix./workspaces/:workspaceSlug/feeds/workspaces/:workspaceSlug/settingsThe backend-api uses request.container as a service locator pattern. Every Fastify request carries the full DI container, allowing handlers to access required services.
Usage Pattern: Handlers access repositories or services directly from the request object:
// Example of accessing a repository via the container in a handler
const userRepo = request.container.userFeedRepository;
const user = await userRepo.findById(id);Note on DI Patterns: While request.container is the standard, new routes may implement a 'dependency-injected route-plugin' pattern where the registration function receives a typed deps object. When modifying existing code, match the existing pattern used in that specific handler.
src/features/, services that wrap external APIs (e.g., Discord, Paddle, Reddit, or feed-fetcher-api) should continue to reside in src/services/<name>/. These are considered legitimate candidates for Dependency Injection (DI) to facilitate easier mocking during testing.Feeds can be personal or owned by a workspace. A feed belongs to a workspace if its workspaceId is set to a valid ObjectId; otherwise, it is personal (workspaceId: null).
Key Behaviors:
getWorkspaceBenefits(workspaceId). They are isolated from personal supporter perks (like refresh rates or personal feed counts).BACKEND_API_DEFAULT_MAX_WORKSPACE_FEEDS.The MonitoRSS frontend (located in services/backend-api/client/) uses a semantic role system rather than hardcoded color values. This system is designed to ensure accessibility (contrast compliance), maintainability (single-file reskinning), and extensibility (easy theme changes).
Instead of referencing raw palette colors (e.g., gray.800), developers must use semantic roles. This prevents the 'incoherent surface' problem where different parts of the UI use slightly different shades of the same color, and ensures that controls remain visible and accessible across different themes.
When contributing to or extending the user-feeds-next service, adhere to these structural rules to maintain pipeline integrity:
articles/: The articles/ module must remain platform-agnostic. Never import Discord-specific types (like DiscordMessageApiPayload) into articles/ or formatting/.formatArticleForDiscord) should be called within the per-medium delivery loop in delivery-routing.ts. This allows each medium to have unique formatter options and custom placeholders.pipeline/ module should import from both articles/ and delivery/. Other modules must not reach across this boundary.stores/.To maintain architectural integrity and prevent circular dependencies, the src/shared/ layer must adhere to the following rules:
src/shared/** are strictly forbidden from importing from @/features/* or any **/features/* paths. This is enforced via no-restricted-imports linting rules.@/shared/concernName) rather than reaching into internal files (e.g., @/shared/concernName/file.ts).src/shared/ speculatively. A module should only be moved to this layer once it is actually required by two or more unrelated features.The MonitoRSS backend-api uses a three-layer folder model designed to enforce feature ownership and minimize a bloated shared base. When deciding where to place a new file, use this 3-question lookup:
pages/.features/<feature>/.Key Principles:
pages/ should only contain thin shells (composition) for routes. Complex logic and JSX trees must move into a feature.components/ for now" pattern.When developing new services within the MonitoRSS ecosystem, you should follow the standard stack to ensure maintainability and architectural consistency. The canonical reference implementation for this stack is found in services/user-feeds-next/.
pg for PostgreSQL. Use Mongoose only if the service specifically requires MongoDB. Do not use MikroORM for new services.rabbitmq-client (the Node-native library). Do not use @golevelup/nestjs-rabbitmq or amqplib/amqp-connection-manager.workerpool for handling CPU-bound tasks.