{"id":20131789,"url":"https://github.com/toverux/expresse","last_synced_at":"2026-03-12T15:03:57.761Z","repository":{"id":57167366,"uuid":"91589404","full_name":"toverux/expresse","owner":"toverux","description":"ExpreSSE: A better module for working with Server-Sent Events in Express","archived":false,"fork":false,"pushed_at":"2024-06-15T23:35:25.000Z","size":128,"stargazers_count":50,"open_issues_count":2,"forks_count":7,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-02-21T19:36:56.630Z","etag":null,"topics":["eventsource","express","express-middleware","nodejs","server-sent-events","sse","stream"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/toverux.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-17T15:02:45.000Z","updated_at":"2025-04-11T04:47:06.000Z","dependencies_parsed_at":"2024-06-18T20:12:07.218Z","dependency_job_id":null,"html_url":"https://github.com/toverux/expresse","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/toverux/expresse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toverux%2Fexpresse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toverux%2Fexpresse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toverux%2Fexpresse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toverux%2Fexpresse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/toverux","download_url":"https://codeload.github.com/toverux/expresse/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toverux%2Fexpresse/sbom","scorecard":{"id":895717,"data":{"date":"2025-08-11","repo":{"name":"github.com/toverux/expresse","commit":"4d8bc9e23cf55c5571a7ef2fa03c9a7b7c487432"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"95 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-8w4h-3cm3-2pm2","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-rq8g-5pc5-wrhr","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-hr2v-3952-633q","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-ff7x-qrg7-qggm","Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc","Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx","Warn: Project is vulnerable to: GHSA-qrmc-fj45-qfc2","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-xf7w-r453-m56c","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-q42p-pg8m-cqh6","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-44pw-h2cw-w3vq","Warn: Project is vulnerable to: GHSA-jp4x-w63m-7wgm","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-fvqr-27wr-82fm","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-p9wx-2529-fp83","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-4xcv-9jjx-gfj3","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-fhjf-83wg-r2j9","Warn: Project is vulnerable to: GHSA-4p35-cfcx-8653","Warn: Project is vulnerable to: GHSA-7f3x-x4pr-wqhj","Warn: Project is vulnerable to: GHSA-jpp7-7chh-cf67","Warn: Project is vulnerable to: GHSA-q6wq-5p59-983w","Warn: Project is vulnerable to: GHSA-j9fq-vwqv-2fm2","Warn: Project is vulnerable to: GHSA-pqw5-jmp5-px4v","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-g6ww-v8xp-vmwg","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-r2j6-p67h-q639","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-jv35-xqg7-f92r","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-2m39-62fm-q8r3","Warn: Project is vulnerable to: GHSA-325j-24f4-qv5x","Warn: Project is vulnerable to: GHSA-mf6x-7mm4-x2g7","Warn: Project is vulnerable to: GHSA-j44m-qm6p-hp7m","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7","Warn: Project is vulnerable to: GHSA-332q-7ff2-57h2","Warn: Project is vulnerable to: GHSA-v2p6-4mp7-3r9v","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T13:37:58.373Z","repository_id":57167366,"created_at":"2025-08-24T13:37:58.373Z","updated_at":"2025-08-24T13:37:58.373Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30429316,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T14:34:45.044Z","status":"ssl_error","status_checked_at":"2026-03-12T14:09:33.793Z","response_time":114,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["eventsource","express","express-middleware","nodejs","server-sent-events","sse","stream"],"created_at":"2024-11-13T20:50:17.875Z","updated_at":"2026-03-12T15:03:57.722Z","avatar_url":"https://github.com/toverux.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e **Deprecation Notice**\n\u003e \n\u003e Although this module should still work very well, more than 6 years have passed since the last release and you may find better software out there now.\n\u003e \n\u003e Ideally, I'd like to take on this project again to add features, support more than Express, new ways to consume and produce events (observables, async enumerators...), expose internals and a make a client-side implementation as-well in order to provide a full-fledged SSE toolkit, but until I find the time for that, you *should* probably use other libraries.\n\u003e \n\u003e If you'd like to be notified in case this happens, subscribe to releases.\n\n# \u003cimg src=\"https://raw.githubusercontent.com/toverux/expresse/master/expresse.png\" alt=\"AssetBundleCompiler logo\" align=\"right\"\u003eExpreSSE [![npm version](https://img.shields.io/npm/v/@toverux/expresse.svg?style=flat-square)](https://www.npmjs.com/package/@toverux/expresse) ![license](https://img.shields.io/github/license/mitmadness/UnityInvoker.svg?style=flat-square) ![npm total downloads](https://img.shields.io/npm/dt/@toverux/expresse.svg?style=flat-square)\n\nExpreSSE is a set of middlewares - with a simple and elegant API - for working with [Server-Sent Events (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) in [Express](http://expressjs.com/fr/). SSE is a simple unidirectional protocol that lets an HTTP server push messages to a client that uses `window.EventSource`. It's HTTP long-polling, without polling!\n\nFrom the MDN:\n\n\u003e Traditionally, a web page has to send a request to the server to receive new data; that is, the page requests data from the server. With server-sent events, it's possible for a server to send new data to a web page at any time, by pushing messages to the web page. \n\n----------------\n\n - [Installation \u0026 Usage](#package-installation--usage)\n - [`sse()` middleware](#sse-middleware) — one to one (server to 1 client)\n - [`sseHub()` middleware](#ssehub-middleware) — one to many (server to _n_ clients)\n - [`RedisHub`](#redishub--redis-support-for-ssehub) — Redis support for `sseHub()` — (_n_ servers to _n_ clients)\n - Notes:\n   [About browser support](#about-browser-support) / [Using a serializer for messages' `data` field](#using-a-serializer-for-messages-data-fields) / [Using compression? Read this.](#using-compression)\n\n----------------\n\n## :package: Installation \u0026 Usage\n\n**Requirements:**\n\n - Node.js 5+ because ExpreSSE is transpiled down to ES 6 ;\n - Express 4\n\nInstall it via the npm registry:\n\n```\nyarn add @toverux/expresse\n```\n\n*TypeScript users:* the library as distributed on npm already contains type definitions for TypeScript. :sparkles:\n\n## `sse()` middleware\n\n\u003cdetails\u003e\n\u003csummary\u003eImport the middleware\u003c/summary\u003e\n\n - Using ES 2015 imports:\n \n   `ISseResponse` is a TypeScript interface. Don't try to import it when using JavaScript.\n\n   ```typescript\n   import { ISseResponse, sse } from '@toverux/expresse';\n   \n   // named export { sse } is also exported as { default }:\n   import sse from '@toverux/expresse';\n   ```\n\n - Using CommonJS:\n\n   ```javascript\n   const { sse } = require('@toverux/expresse');\n   ```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eAvailable configuration options\u003c/summary\u003e\n\n```typescript\ninterface ISseMiddlewareOptions {\n    /**\n     * Serializer function applied on all messages' data field (except when you direclty pass a Buffer).\n     * SSE comments are not serialized using this function.\n     *\n     * @default JSON.stringify\n     */\n    serializer?: (value: any) =\u003e string | Buffer;\n\n    /**\n     * Whether to flush headers immediately or wait for the first res.write().\n     *  - Setting it to false can allow you or 3rd-party middlewares to set more headers on the response.\n     *  - Setting it to true is useful for debug and tesing the connection, ie. CORS restrictions fail only when headers\n     *    are flushed, which may not happen immediately when using SSE (it happens after the first res.write call).\n     *\n     * @default true\n     */\n    flushHeaders?: boolean;\n\n    /**\n     * Determines the interval, in milliseconds, between keep-alive packets (neutral SSE comments).\n     * Pass false to disable heartbeats (ie. you only support modern browsers/native EventSource implementation and\n     * therefore don't need heartbeats to avoid the browser closing an inactive socket).\n     *\n     * @default 5000\n     */\n    keepAliveInterval?: false | number;\n    \n    /**\n     * If you are using expressjs/compression, you MUST set this option to true.\n     * It will call res.flush() after each SSE messages so the partial content is compressed and reaches the client.\n     * Read {@link https://github.com/expressjs/compression#server-sent-events} for more.\n     *\n     * @default false\n     */\n    flushAfterWrite?: boolean;\n}\n```\n\n:arrow_right: [Read more about `serializer`](#using-a-serializer-for-messages-data-fields)\n\u003c/details\u003e\n\u003cbr\u003e\n\nUsage example *(remove `ISseResponse` when not using TypeScript)*:\n\n```typescript\n// somewhere in your module\nrouter.get('/events', sse(/* options */), (req, res: ISseResponse) =\u003e {\n    let messageId = parseInt(req.header('Last-Event-ID'), 10) || 0;\n    \n    someModule.on('someEvent', (event) =\u003e {\n        //=\u003e Data messages (no event name, but defaults to 'message' in the browser).\n        res.sse.data(event);\n        //=\u003e Named event + data (data is mandatory)\n        res.sse.event('someEvent', event);\n        //=\u003e Comment, not interpreted by EventSource on the browser - useful for debugging/self-documenting purposes.\n        res.sse.comment('debug: someModule emitted someEvent!');\n        //=\u003e In data() and event() you can also pass an ID - useful for replay with Last-Event-ID header.\n        res.sse.data(event, (messageId++).toString());\n    });\n    \n    // (not recommended) to force the end of the connection, you can still use res.end()\n    // beware that the specification does not support server-side close, so this will result in an error in EventSource.\n    // prefer sending a normal event that asks the client to call EventSource#close() itself to gracefully terminate.\n    someModule.on('someFinishEvent', () =\u003e res.end());\n});\n```\n\n## `sseHub()` middleware\n\nThis one is very useful for pushing the same messages to multiples users at a time, so they share the same \"stream\".\n\nIt is based on the `sse()` middleware, meaning that you can still use `res.sse.*` functions, their behavior don't change.\nFor broadcasting to the users that have subscribed to the stream (meaning that they've made the request to the endpoint), use the `req.sse.broadcast.*` functions, that are exactly the same as their 1-to-1 variant.\n\n\u003cdetails\u003e\n\u003csummary\u003eImport the middleware\u003c/summary\u003e\n\n - Using ES 2015 imports:\n \n   `ISseHubResponse` is a TypeScript interface. Don't try to import it when using JavaScript.\n\n   ```typescript\n   import { Hub, ISseHubResponse, sseHub } from '@toverux/expresse';\n   ```\n\n - Using CommonJS:\n\n   ```javascript\n   const { Hub, sseHub } = require('@toverux/expresse');\n   ```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eAvailable configuration options\u003c/summary\u003e\n\nThe options are the same from the `sse()` middleware ([see above](#sse-middleware)), plus another, `hub`:\n\n```typescript\ninterface ISseHubMiddlewareOptions extends ISseMiddlewareOptions {\n    /**\n     * You can pass a Hub instance for controlling the stream outside of the middleware.\n     * Otherwise, a Hub is automatically created.\n     * \n     * @default Hub\n     */\n    hub: Hub;\n}\n```\n\u003c/details\u003e\n\u003cbr\u003e\n\nFirst usage example - where the client has control on the hub *(remove `ISseHubResponse` when not using TypeScript)*:\n\n```typescript\n// somewhere in your module\nrouter.get('/events', sseHub(/* options */), (req, res: ISseHubResponse) =\u003e {\n    //=\u003e The 1-to-1 functions are still there\n    res.sse.event('welcome', 'Welcome!');\n    \n    //=\u003e But we also get a `broadcast` property with the same functions inside.\n    //   Everyone that have hit /events will get this message - including the sender!\n    res.sse.broadcast.event('new-user', `User ${req.query.name} just hit the /channel endpoint`);\n});\n```\n\nMore common usage example - where the Hub is deported outside of the middleware:\n\n```typescript\nconst hub = new Hub();\n\nsomeModule.on('someEvent', (event) =\u003e {\n    //=\u003e All the functions you're now used to are still there, data(), event() and comment().\n    hub.event('someEvent', event);\n});\n\nrouter.get('/events', sseHub({ hub }), (req, res: ISseHubResponse) =\u003e {\n    //=\u003e The 1-to-1 functions are still there\n    res.sse.event('welcome', 'Welcome! You\\'ll now receive realtime events from someModule like everyone else');\n});\n```\n\n### `RedisHub` – Redis support for `sseHub()`\n\nIn the previous example you can notice that we've created the Hub object ourselves. This also means that you can replace that object with another that has a compatible interface (implement `IHub` in [src/hub.ts](src/hub.ts) to make your own :coffee:).\n\n_expresse_ provides an alternative subclass of `Hub`, `RedisHub` that uses Redis' pub/sub capabilities, which is very practical if you have multiple servers, and you want `res.sse.broadcast.*` to actually broadcast SSE messages between all the nodes.\n\n```typescript\n// connects to localhost:6379 (default Redis port)\nconst hub = new RedisHub('channel-name');\n// ...or you can pass you own two ioredis clients to bind on a custom network address\nconst hub = new RedisHub('channel-name', new Redis(myRedisNodeUrl), new Redis(myRedisNodeUrl));\n\nrouter.get('/channel', sseHub({ hub }), (req, res: ISseHubResponse) =\u003e {\n    res.sse.event('welcome', 'Welcome!'); // 1-to-1\n    res.sse.broadcast.event('new-user', `User ${req.query.name} just hit the /channel endpoint`);\n});\n```\n\n## :bulb: Notes\n\n### About browser support\n\nThe W3C standard client for Server-Sent events is [EventSource](https://developer.mozilla.org/fr/docs/Web/API/EventSource). Unfortunately, it is not yet implemented in Internet Explorer or Microsoft Edge.\n\nYou may want to use a polyfill on the client side if your application targets those browsers (see [eventsource](https://www.npmjs.com/package/eventsource) package on npm for Node and older browsers support).\n\n[See complete support report on _Can I use_](http://caniuse.com/#feat=eventsource)\n\n|                     | Chrome | IE / Edge | Firefox | Opera | Safari |\n|---------------------|--------|-----------|---------|-------|--------|\n| EventSource Support | 6      | No        | 6       | 11    | 5      |\n\n### Using a serializer for messages' `data` fields\n\nWhen sending a message, the `data` field is serialized using `JSON.stringify`. You can override that default serializer to use your own format.\n\nThe serializer must be compatible with the signature `(value: any) =\u003e string|Buffer;`.\n\nFor example, to format data using the `toString()` format of the value, you can use the `String()` constructor:\n\n```typescript\napp.get('/events', sse({ serializer: String }), yourMiddleware);\n\n// or, less optimized:\napp.get('/events', sse({ serializer: data =\u003e data.toString() }), yourMiddleware);\n```\n\n### Using Compression\n\nIf you are using a dynamic HTTP compression middleware, like [expressjs/compression](https://github.com/expressjs/compression), _expresse_ won't likely work out of the box.\n\nThis is due to the nature of compression and how compression middlewares work. For example, express' compression middleware will patch res.write and hold the content written in it until `res.end()` or an equivalent is called. Then the body compression can happen and the compressed content can be sent.\n\nTherefore, `res.write()` must not be buffered with SSEs. That's why ExpreSSE offers expressjs/compression support through the `flushAfterWrite` option. It **must** be set when using the compression middleware:\n\n```typescript\napp.use(compression());\n\napp.get('/events', sse({ flushAfterWrite: true }), (req, res: ISseResponse) =\u003e {\n    res.sse.comment('Welcome! This is a compressed SSE stream.');\n});\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoverux%2Fexpresse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftoverux%2Fexpresse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoverux%2Fexpresse/lists"}