PageSourceSearch

https://socket.io/assets/js/14954928.4942be1e.js

js socket.io collected 2026-09-24 07:26:14 UTC 55,160 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunksample_website=self.webpackChunksample_website||[]).push([[6411],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){o(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,o=function(e,t){if(null==e)return{};var n,a,o={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},m="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},k=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=c(n),k=o,u=m["".concat(s,".").concat(k)]||m[k]||d[k]||r;return n?a.createElement(u,i(i({ref:t},p),{},{components:n})):a.createElement(u,i({ref:t},p))}));function u(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,i=new Array(r);i[0]=k;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[m]="string"==typeof e?e:o,i[1]=l;for(var c=2;c<r;c++)i[c]=n[c];return a.createElement.apply(null,i)}return a.createElement.apply(null,n)}k.displayName="MDXCreateElement"},1683:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var a=n(3117),o=(n(7294),n(3905));const r={title:"Migrating from 2.x to 3.0",sidebar_position:1,slug:"/migrating-from-2-x-to-3-0/",toc_max_heading_level:4},i=void 0,l={unversionedId:"categories/Migrations/migrating-from-2-to-3",id:"categories/Migrations/migrating-from-2-to-3",title:"Migrating from 2.x to 3.0",description:"This release should fix most of the inconsistencies of the Socket.IO library and provide a more intuitive behavior for",source:"@site/docs/categories/07-Migrations/migrating-from-2-to-3.md",sourceDirName:"categories/07-Migrations",slug:"/migrating-from-2-x-to-3-0/",permalink:"/docs/v4/migrating-from-2-x-to-3-0/",draft:!1,editUrl:"https://github.com/socketio/socket.io-website/edit/main/docs/categories/07-Migrations/migrating-from-2-to-3.md",tags:[],version:"current",lastUpdatedAt:1784117104,formattedLastUpdatedAt:"Jul 15, 2026",sidebarPosition:1,frontMatter:{title:"Migrating from 2.x to 3.0",sidebar_position:1,slug:"/migrating-from-2-x-to-3-0/",toc_max_heading_level:4},sidebar:"sidebar",previous:{title:"Performance tuning",permalink:"/docs/v4/performance-tuning/"},next:{title:"Migrating from 3.x to 4.0",permalink:"/docs/v4/migrating-from-3-x-to-4-0/"}},s={},c=[{value:"Configuration",id:"configuration",level:3},{value:"Saner default values",id:"saner-default-values",level:4},{value:"CORS handling",id:"cors-handling",level:4},{value:"No more cookie by default",id:"no-more-cookie-by-default",level:4},{value:"API change",id:"api-change",level:3},{value:"io.set() is removed",id:"ioset-is-removed",level:4},{value:"No more implicit connection to the default namespace",id:"no-more-implicit-connection-to-the-default-namespace",level:4},{value:"Namespace.connected is renamed to Namespace.sockets and is now a Map",id:"namespaceconnected-is-renamed-to-namespacesockets-and-is-now-a-map",level:4},{value:"Socket.rooms is now a Set",id:"socketrooms-is-now-a-set",level:4}
1,{value:"Socket.binary() is removed",id:"socketbinary-is-removed",level:4},{value:"Socket.join() and Socket.leave() are now synchronous",id:"socketjoin-and-socketleave-are-now-synchronous",level:4},{value:"<del>Socket.use() is removed</del>",id:"socketuse-is-removed",level:4},{value:"A middleware error will now emit an Error object",id:"a-middleware-error-will-now-emit-an-error-object",level:4},{value:"Add a clear distinction between the Manager query option and the Socket query option",id:"add-a-clear-distinction-between-the-manager-query-option-and-the-socket-query-option",level:4},{value:"The Socket instance will no longer forward the events emitted by its Manager",id:"the-socket-instance-will-no-longer-forward-the-events-emitted-by-its-manager",level:4},{value:"Namespace.clients() is renamed to Namespace.allSockets() and now returns a Promise",id:"namespaceclients-is-renamed-to-namespaceallsockets-and-now-returns-a-promise",level:4},{value:"Client bundles",id:"client-bundles",level:4},{value:"No more &quot;pong&quot; event for retrieving latency",id:"no-more-pong-event-for-retrieving-latency",level:4},{value:"ES modules syntax",id:"es-modules-syntax",level:4},{value:"<code>emit()</code> chains are not possible anymore",id:"emit-chains-are-not-possible-anymore",level:4},{value:"Room names are not coerced to string anymore",id:"room-names-are-not-coerced-to-string-anymore",level:4},{value:"New features",id:"new-features",level:3},{value:"Catch-all listeners",id:"catch-all-listeners",level:4},{value:"Volatile events (client)",id:"volatile-events-client",level:4},{value:"Official bundle with the msgpack parser",id:"official-bundle-with-the-msgpack-parser",level:4},{value:"Miscellaneous",id:"miscellaneous",level:3},{value:"The Socket.IO codebase has been rewritten to TypeScript",id:"the-socketio-codebase-has-been-rewritten-to-typescript",level:4},{value:"Support for IE8 and Node.js 8 is officially dropped",id:"support-for-ie8-and-nodejs-8-is-officially-dropped",level:4},{value:"How to upgrade an existing production deployment",id:"how-to-upgrade-an-existing-production-deployment",level:3},{value:"Known migration issues",id:"known-migration-issues",level:3}],p={toc:c},m="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(m,(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"This release should fix most of the inconsistencies of the Socket.IO library and provide a more intuitive behavior for\nthe end users. It is the result of the feedback of the community over the years. A big thanks to everyone involved!"),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"TL;DR:")," ",(0,o.kt)("del",{parentName:"p"},"due to several breaking changes, a v2 client will not be able to connect to a v3 server (and vice versa)")),(0,o.kt)("p",null,"Update: As of ",(0,o.kt)("a",{parentName:"p",href:"/blog/socket-io-3-1-0/"},"Socket.IO 3.1.0"),", the v3 server is now able to communicate with v2 clients. More information ",(0,o.kt)("a",{parentName:"p",href:"#how-to-upgrade-an-existing-production-deployment"},"below"),". A v3 client is still not be able to connect to a v2 server though."),(0,o.kt)("p",null,"For the low-level details, please see:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://github.com/socketio/engine.io-protocol#difference-between-v3-and-v4"},"Engine.IO protocol v4")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://github.com/socketio/socket.io-protocol#difference-between-v5-and-v4"},"Socket.IO protocol v5"))),(0,o.kt)("p",null,"Here is the complete list of changes:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("a",{parentName:"p",href:"#configuration"},"Configuration")),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#saner-default-values"},"Saner default values")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#cors-handling"},"CORS handling")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#no-more-cookie-by-default"},"No more cookie by default")))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("a",{parentName:"p",href:"#aPI-change"},"API change")),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#ioset-is-removed"},"io.set() is removed")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#no-more-implicit-connection-to-the-default-namespace"},"No more implicit connection to the default namespace")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#namespaceconnected-is-renamed-to-namespacesockets-and-is-now-a-map"}
1,"Namespace.connected is renamed to Namespace.sockets and is now a Map")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#socketrooms-is-now-a-set"},"Socket.rooms is now a Set")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#socketbinary-is-removed"},"Socket.binary() is removed")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#socketjoin-and-socketleave-are-now-synchronous"},"Socket.join() and Socket.leave() are now synchronous")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#socketuse-is-removed"},"Socket.use() is removed")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#a-middleware-error-will-now-emit-an-error-object"},"A middleware error will now emit an Error object")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#add-a-clear-distinction-between-the-manager-query-option-and-the-socket-query-option"},"Add a clear distinction between the Manager query option and the Socket query option")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#the-socket-instance-will-no-longer-forward-the-events-emitted-by-its-manager"},"The Socket instance will no longer forward the events emitted by its Manager")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#namespaceclients-is-renamed-to-namespaceallsockets-and-now-returns-a-promise"},"Namespace.clients() is renamed to Namespace.allSockets() and now returns a Promise")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#client-bundles"},"Client bundles")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#no-more-pong-event-for-retrieving-latency"},'No more "pong" event for retrieving latency')),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#eS-modules-syntax"},"ES modules syntax")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#emit-chains-are-not-possible-anymore"},(0,o.kt)("inlineCode",{parentName:"a"},"emit()")," chains are not possible anymore")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#room-names-are-not-coerced-to-string-anymore"},"Room names are not coerced to string anymore")))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("a",{parentName:"p",href:"#new-features"},"New features")),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#catch-all-listeners"},"Catch-all listeners")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#volatile-events-client"},"Volatile events (client)")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#official-bundle-with-the-msgpack-parser"},"Official bundle with the msgpack parser")))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("a",{parentName:"p",href:"#miscellaneous"},"Miscellaneous")),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#the-socketio-codebase-has-been-rewritten-to-typescript"},"The Socket.IO codebase has been rewritten to TypeScript")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"#support-for-ie8-and-nodejs-8-is-officially-dropped"},"Support for IE8 and Node.js 8 is officially dropped")))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("a",{parentName:"p",href:"#how-to-upgrade-an-existing-production-deployment"},"How to upgrade an existing production deployment"))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("a",{parentName:"p",href:"#known-migration-issues"},"Known migration issues")))),(0,o.kt)("h3",{id:"configuration"},"Configuration"),(0,o.kt)("h4",{id:"saner-default-values"},"Saner default values"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"the default value of ",(0,o.kt)("inlineCode",{parentName:"li"},"maxHttpBufferSize")," was decreased from ",(0,o.kt)("inlineCode",{parentName:"li"},"100MB")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"1MB"),"."),(0,o.kt)("li",{parentName:"ul"},"the WebSocket ",(0,o.kt)("a",{parentName:"li",href:"https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-19"},"permessage-deflate extension")," is now disabled by default"),(0,o.kt)("li",{parentName:"ul"},"you must now explicitly list the domains that are allowed (for CORS, see ",(0,o.kt)("a",{parentName:"li",href:"#cORS-handling"},"below"),")"),(0,o.kt)("li",{parentName:"ul"},"the ",(0,o.kt)("inlineCode",{parentName:"li"},"withCredentials")," option now defaults to ",(0,o.kt)("inlineCode",{parentName:"li"},"false")," on the client side")),(0,o.kt)("h4",{id:"cors-handling"},"CORS handling"),(0,o.kt)("p",null,"In v2, the Socket.IO server automatically added the necessary headers to allow ",(0,o.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"},"Cross-Origin Resource Sharing")," (CORS)."),(0,o.kt)("p",null,"This behavior, while convenient, was not great in terms of security, because it meant that all domains were allowed to reach your Socket.IO server, unless otherwise specified with the ",(0,o.kt)("inlineCode",{parentName:"p"},"origins")," option."),(0,o.kt)("p",null,"That's why, as of Socket.IO v3:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"CORS is now disabled by default"),(0,o.kt)("li",{parentName:"ul"},"the ",(0,o.kt)("inlineCode",{parentName:"li"},"origins")," option (used to provide a list of authorized domains) and the ",(0,o.kt)("inlineCode",{parentName:"li"},"handlePreflightRequest")," option (used to edit the ",(0,o.kt)("inlineCode",{parentName:"li"},"Access-Control-Allow-xxx")," headers) are replaced by the ",(0,o.kt)("inlineCode",{parentName:"li"},"cors")," option, which will be forwarded to the ",(0,o.kt)("a",{parentName:"li",href:"https://www.npmjs.com/package/cors"},"cors")," package.")),(0,o.kt)("p",null,"The complete list of options can be found ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/expressjs/cors#configuration-options"},"here"),"."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const io = require("socket.io")(httpServer, {\n  origins: ["https://example.com"],\n\n  // optional, useful for custom headers\n  handlePreflightRequest: (req, res) => {\n    res.writeHead(200, {\n      "Access-Control-Allow-Origin": "https://example.com",\n      "Access-Control-Allow-Methods": "GET,POST",\n      "Access-Control-Allow-Headers": "my-custom-header",\n      "Access-Control-Allow-Credentials": true\n    });\n    res.end();\n  }\n});\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const io = require("socket.io")(httpServer, {\n  cors: {\n    origin: "https://example.com",\n    methods: ["GET", "POST"],\n    allowedHeaders: ["my-custom-header"],\n    credentials: true\n  }\n});\n')),(0,o.kt)("h4",{id:"no-more-cookie-by-default"},"No more cookie by default"),(0,o.kt)("p",null,"In previous versions, an ",(0,o.kt)("inlineCode",{parentName:"p"},"io")," cookie was sent by default. This cookie can be used to enable sticky-session, which is still required when you have several servers and HTTP long-polling enabled (more information ",(0,o.kt)("a",{parentName:"p",href:"/docs/v4/using-multiple-nodes/"},"here"),")."),(0,o.kt)("p",null,"However, this cookie is not needed in some cases (i.e. single server deployment, sticky-session based on IP) so it must now be explicitly enabled."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const io = require("socket.io")(httpServer, {\n  cookieName: "io",\n  cookieHttpOnly: false,\n  cookiePath: "/custom"\n});\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"}
1,'const io = require("socket.io")(httpServer, {\n  cookie: {\n    name: "test",\n    httpOnly: false,\n    path: "/custom"\n  }\n});\n')),(0,o.kt)("p",null,"All other options (domain, maxAge, sameSite, ...) are now supported. Please see ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jshttp/cookie/"},"here")," for the complete list of options."),(0,o.kt)("h3",{id:"api-change"},"API change"),(0,o.kt)("p",null,"Below are listed the non backward-compatible changes. "),(0,o.kt)("h4",{id:"ioset-is-removed"},"io.set() is removed"),(0,o.kt)("p",null,"This method was deprecated in the 1.0 release and kept for backward-compatibility. It is now removed."),(0,o.kt)("p",null,"It was replaced by middlewares."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'io.set("authorization", (handshakeData, callback) => {\n  // make sure the handshake data looks good\n  callback(null, true); // error first, "authorized" boolean second \n});\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'io.use((socket, next) => {\n  var handshakeData = socket.request;\n  // make sure the handshake data looks good as before\n  // if error do this:\n    // next(new Error("not authorized"));\n  // else just call next\n  next();\n});\n')),(0,o.kt)("h4",{id:"no-more-implicit-connection-to-the-default-namespace"},"No more implicit connection to the default namespace"),(0,o.kt)("p",null,"This change impacts the users of the multiplexing feature (what we call Namespace in Socket.IO)."),(0,o.kt)("p",null,"In previous versions, a client would always connect to the default namespace (",(0,o.kt)("inlineCode",{parentName:"p"},"/"),"), even if it requested access to another namespace. This meant that the middlewares registered for the default namespace were triggered, which may be quite surprising."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// client-side\nconst socket = io("/admin");\n\n// server-side\nio.use((socket, next) => {\n  // not triggered anymore\n});\n\nio.on("connection", socket => {\n  // not triggered anymore\n})\n\nio.of("/admin").use((socket, next) => {\n  // triggered\n});\n')),(0,o.kt)("p",null,'Besides, we will now refer to the "main" namespace instead of the "default" namespace.'),(0,o.kt)("h4",{id:"namespaceconnected-is-renamed-to-namespacesockets-and-is-now-a-map"},"Namespace.connected is renamed to Namespace.sockets and is now a Map"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"connected")," object (used to store all the Socket connected to the given Namespace) could be used to retrieve a Socket object from its id. It is now an ES6 ",(0,o.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map"},"Map"),"."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// get a socket by ID in the main namespace\nconst socket = io.of("/").connected[socketId];\n\n// get a socket by ID in the "admin" namespace\nconst socket = io.of("/admin").connected[socketId];\n\n// loop through all sockets\nconst sockets = io.of("/").connected;\nfor (const id in sockets) {\n  if (sockets.hasOwnProperty(id)) {\n    const socket = sockets[id];\n    // ...\n  }\n}\n\n// get the number of connected sockets\nconst count = Object.keys(io.of("/").connected).length;\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// get a socket by ID in the main namespace\nconst socket = io.of("/").sockets.get(socketId);\n\n// get a socket by ID in the "admin" namespace\nconst socket = io.of("/admin").sockets.get(socketId);\n\n// loop through all sockets\nfor (const [_, socket] of io.of("/").sockets) {\n  // ...\n}\n\n// get the number of connected sockets\nconst count = io.of("/").sockets.size;\n')),(0,o.kt)("h4",{id:"socketrooms-is-now-a-set"},"Socket.rooms is now a Set"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"rooms")," property contains the list of rooms the Socket is currently in. It was an object, it is now an ES6 ",(0,o.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set"},"Set"),"."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'io.on("connection", (socket) => {\n\n  console.log(Object.keys(socket.rooms)); // [ <socket.id> ]\n\n  socket.join("room1");\n\n  console.log(Object.keys(socket.rooms)); // [ <socket.id>, "room1" ]\n\n});\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'io.on("connection", (socket) => {\n\n  console.log(socket.rooms); // Set { <socket.id> }\n\n  socket.join("room1");\n\n  console.log(socket.rooms); // Set { <socket.id>, "room1" }\n\n});\n')),(0,o.kt)("h4",{id:"socketbinary-is-removed"}
1,"Socket.binary() is removed"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"binary"),"  method could be used to indicate that a given event did not contain any binary data (in order to skip the lookup done by the library and improve performance in certain conditions)."),(0,o.kt)("p",null,"It was replaced by the ability to provide your own parser, which was added in Socket.IO 2.0."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'socket.binary(false).emit("hello", "no binary");\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const io = require("socket.io")(httpServer, {\n  parser: myCustomParser\n});\n')),(0,o.kt)("p",null,"Please see ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/socketio/socket.io-msgpack-parser"},"socket.io-msgpack-parser")," for example."),(0,o.kt)("h4",{id:"socketjoin-and-socketleave-are-now-synchronous"},"Socket.join() and Socket.leave() are now synchronous"),(0,o.kt)("p",null,"The asynchronicity was needed for the first versions of the Redis adapter, but this is not the case anymore."),(0,o.kt)("p",null,"For reference, an Adapter is an object that stores the relationships between Sockets and ",(0,o.kt)("a",{parentName:"p",href:"/docs/v4/rooms/"},"Rooms"),". There are two official adapters: the in-memory adapter (built-in) and the ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/socketio/socket.io-redis"},"Redis adapter")," based on Redis ",(0,o.kt)("a",{parentName:"p",href:"https://redis.io/topics/pubsub"},"pub-sub mechanism"),"."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'socket.join("room1", () => {\n io.to("room1").emit("hello");\n});\n\nsocket.leave("room2", () => {\n  io.to("room2").emit("bye");\n});\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'socket.join("room1");\nio.to("room1").emit("hello");\n\nsocket.leave("room2");\nio.to("room2").emit("bye");\n')),(0,o.kt)("p",null,"Note: custom adapters may return a Promise, so the previous example becomes:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'await socket.join("room1");\nio.to("room1").emit("hello");\n')),(0,o.kt)("h4",{id:"socketuse-is-removed"},(0,o.kt)("del",{parentName:"h4"},"Socket.use() is removed")),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"socket.use()")," could be used as a catch-all listener. But its API was not really intuitive. It is replaced by ",(0,o.kt)("a",{parentName:"p",href:"#catch-all-listeners"},"socket.onAny()"),"."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"UPDATE"),": the ",(0,o.kt)("inlineCode",{parentName:"p"},"Socket.use()")," method was restored in ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/socketio/socket.io/releases/3.0.5"},(0,o.kt)("inlineCode",{parentName:"a"},"[email protected]")),"."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"socket.use((packet, next) => {\n  console.log(packet.data);\n  next();\n});\n")),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"socket.onAny((event, ...args) => {\n  console.log(event);\n});\n")),(0,o.kt)("h4",{id:"a-middleware-error-will-now-emit-an-error-object"},"A middleware error will now emit an Error object"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"error")," event is renamed to ",(0,o.kt)("inlineCode",{parentName:"p"},"connect_error")," and the object emitted is now an actual Error:"),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// server-side\nio.use((socket, next) => {\n  next(new Error("not authorized"));\n});\n\n// client-side\nsocket.on("error", err => {\n  console.log(err); // not authorized\n});\n\n// or with an object\n// server-side\nio.use((socket, next) => {\n  const err = new Error("not authorized");\n  err.data = { content: "Please retry later" }; // additional details\n  next(err);\n});\n\n// client-side\nsocket.on("error", err => {\n  console.log(err); // { content: "Please retry later" }\n});\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// server-side\nio.use((socket, next) => {\n  const err = new Error("not authorized");\n  err.data = { content: "Please retry later" }; // additional details\n  next(err);\n});\n\n// client-side\nsocket.on("connect_error", err => {\n  console.log(err instanceof Error); // true\n  console.log(err.message); // not authorized\n  console.log(err.data); // { content: "Please retry later" }\n});\n')),(0,o.kt)("h4",{id:"add-a-clear-distinction-between-the-manager-query-option-and-the-socket-query-option"},"Add a clear distinction between the Manager query option and the Socket query option"),(0,o.kt)("p",null,"In previous versions, the ",(0,o.kt)("inlineCode",{parentName:"p"},"query")," option was used in two distinct places:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"in the query parameters of the HTTP requests (",(0,o.kt)("inlineCode",{parentName:"li"},"GET /socket.io/?EIO=3&abc=def"),")"),(0,o.kt)("li",{parentName:"ul"},"in the ",(0,o.kt)("inlineCode",{parentName:"li"},"CONNECT")," packet")),(0,o.kt)("p",null,"Let's take the following example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const socket = io({\n  query: {\n    token: "abc"\n  }\n});\n')),(0,o.kt)("p",null,"Under the hood, here's what happened in the ",(0,o.kt)("inlineCode",{parentName:"p"},"io()")," method:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const { Manager } = require("socket.io-client");\n\n// a new Manager is created (which will manage the low-level connection)\nconst manager = new Manager({\n  query: { // sent in the query parameters\n    token: "abc"\n  }\n});\n\n// and then a Socket instance is created for the namespace (here, the main namespace, "/")\nconst socket = manager.socket("/", {\n  query: { // sent in the CONNECT packet\n    token: "abc"\n  }\n});\n')),(0,o.kt)("p",null,"This behavior could lead to weird behaviors, for example when the Manager was reused for another namespace (multiplexing):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// client-side\nconst socket1 = io({\n  query: {\n    token: "abc"\n  }\n});\n\nconst socket2 = io("/my-namespace", {\n  query: {\n    token: "def"\n  }\n});\n\n// server-side\nio.on("connection", (socket) => {\n  console.log(socket.handshake.query.token); // abc (ok!)\n});\n\nio.of("/my-namespace").on("connection", (socket) => {\n  console.log(socket.handshake.query.token); // abc (what?)\n});\n')),(0,o.kt)("p",null,"That's why the ",(0,o.kt)("inlineCode",{parentName:"p"},"query")," option of the Socket instance is renamed to ",(0,o.kt)("inlineCode",{parentName:"p"},"auth")," in Socket.IO v3:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// plain object\nconst socket = io({\n  auth: {\n    token: "abc"\n  }\n});\n\n// or with a function\nconst socket = io({\n  auth: (cb) => {\n    cb({\n      token: "abc"\n    });\n  }\n});\n\n// server-side\nio.on("connection", (socket) => {\n  console.log(socket.handshake.auth.token); // abc\n});\n')),(0,o.kt)("p",null,"Note: the ",(0,o.kt)("inlineCode",{parentName:"p"},"query")," option of the Manager can still be used in order to add a specific query parameter to the HTTP requests."),(0,o.kt)("h4",{id:"the-socket-instance-will-no-longer-forward-the-events-emitted-by-its-manager"},"The Socket instance will no longer forward the events emitted by its Manager"),(0,o.kt)("p",null,"In previous versions, the Socket instance emitted the events related to the state of the underlying connection. This will not be the case anymore."),(0,o.kt)("p",null,"You can still have access to those events on the Manager instance (the ",(0,o.kt)("inlineCode",{parentName:"p"},"io")," property of the socket) :"),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"}
1,'socket.on("reconnect_attempt", () => {});\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'socket.io.on("reconnect_attempt", () => {});\n')),(0,o.kt)("p",null,"Here is the updated list of events emitted by the Manager:"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Name"),(0,o.kt)("th",{parentName:"tr",align:null},"Description"),(0,o.kt)("th",{parentName:"tr",align:null},"Previously (if different)"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"open"),(0,o.kt)("td",{parentName:"tr",align:null},"successful (re)connection"),(0,o.kt)("td",{parentName:"tr",align:null},"-")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"error"),(0,o.kt)("td",{parentName:"tr",align:null},"(re)connection failure or error after a successful connection"),(0,o.kt)("td",{parentName:"tr",align:null},"connect_error")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"close"),(0,o.kt)("td",{parentName:"tr",align:null},"disconnection"),(0,o.kt)("td",{parentName:"tr",align:null},"-")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"ping"),(0,o.kt)("td",{parentName:"tr",align:null},"ping packet"),(0,o.kt)("td",{parentName:"tr",align:null},"-")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"packet"),(0,o.kt)("td",{parentName:"tr",align:null},"data packet"),(0,o.kt)("td",{parentName:"tr",align:null},"-")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"reconnect_attempt"),(0,o.kt)("td",{parentName:"tr",align:null},"reconnection attempt"),(0,o.kt)("td",{parentName:"tr",align:null},"reconnect_attempt & reconnecting")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"reconnect"),(0,o.kt)("td",{parentName:"tr",align:null},"successful reconnection"),(0,o.kt)("td",{parentName:"tr",align:null},"-")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"reconnect_error"),(0,o.kt)("td",{parentName:"tr",align:null},"reconnection failure"),(0,o.kt)("td",{parentName:"tr",align:null},"-")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"reconnect_failed"),(0,o.kt)("td",{parentName:"tr",align:null},"reconnection failure after all attempts"),(0,o.kt)("td",{parentName:"tr",align:null},"-")))),(0,o.kt)("p",null,"Here is the updated list of events emitted by the Socket:"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Name"),(0,o.kt)("th",{parentName:"tr",align:null},"Description"),(0,o.kt)("th",{parentName:"tr",align:null},"Previously (if different)"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"connect"),(0,o.kt)("td",{parentName:"tr",align:null},"successful connection to a Namespace"),(0,o.kt)("td",{parentName:"tr",align:null},"-")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"connect_error"),(0,o.kt)("td",{parentName:"tr",align:null},"connection failure"),(0,o.kt)("td",{parentName:"tr",align:null},"error")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"disconnect"),(0,o.kt)("td",{parentName:"tr",align:null},"disconnection"),(0,o.kt)("td",{parentName:"tr",align:null},"-")))),(0,o.kt)("p",null,"And finally, here's the updated list of reserved events that you cannot use in your application:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"connect")," (used on the client-side)"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"connect_error")," (used on the client-side)"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"disconnect")," (used on both sides)"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"disconnecting")," (used on the server-side)"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"newListener")," and ",(0,o.kt)("inlineCode",{parentName:"li"},"removeListener")," (EventEmitter ",(0,o.kt)("a",{parentName:"li",href:"https://nodejs.org/api/events.html#events_event_newlistener"},"reserved events"),")")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"}
1,'socket.emit("connect_error"); // will now throw an Error\n')),(0,o.kt)("h4",{id:"namespaceclients-is-renamed-to-namespaceallsockets-and-now-returns-a-promise"},"Namespace.clients() is renamed to Namespace.allSockets() and now returns a Promise"),(0,o.kt)("p",null,"This function returns the list of socket IDs that are connected to this namespace."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// all sockets in default namespace\nio.clients((error, clients) => {\n  console.log(clients); // => [6em3d4TJP8Et9EMNAAAA, G5p55dHhGgUnLUctAAAB]\n});\n\n// all sockets in the "chat" namespace\nio.of("/chat").clients((error, clients) => {\n  console.log(clients); // => [PZDoMHjiu8PYfRiKAAAF, Anw2LatarvGVVXEIAAAD]\n});\n\n// all sockets in the "chat" namespace and in the "general" room\nio.of("/chat").in("general").clients((error, clients) => {\n  console.log(clients); // => [Anw2LatarvGVVXEIAAAD]\n});\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// all sockets in default namespace\nconst ids = await io.allSockets();\n\n// all sockets in the "chat" namespace\nconst ids = await io.of("/chat").allSockets();\n\n// all sockets in the "chat" namespace and in the "general" room\nconst ids = await io.of("/chat").in("general").allSockets();\n')),(0,o.kt)("p",null,"Note: this function was (and still is) supported by the Redis adapter, which means that it will return the list of socket IDs across all the Socket.IO servers."),(0,o.kt)("h4",{id:"client-bundles"},"Client bundles"),(0,o.kt)("p",null,"There are now 3 distinct bundles:"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:"left"},"Name"),(0,o.kt)("th",{parentName:"tr",align:"left"},"Size"),(0,o.kt)("th",{parentName:"tr",align:"left"},"Description"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:"left"},"socket.io.js"),(0,o.kt)("td",{parentName:"tr",align:"left"},"34.7 kB gzip"),(0,o.kt)("td",{parentName:"tr",align:"left"},"Unminified version, with ",(0,o.kt)("a",{parentName:"td",href:"https://www.npmjs.com/package/debug"},"debug"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:"left"},"socket.io.min.js"),(0,o.kt)("td",{parentName:"tr",align:"left"},"14.7 kB min+gzip"),(0,o.kt)("td",{parentName:"tr",align:"left"},"Production version, without ",(0,o.kt)("a",{parentName:"td",href:"https://www.npmjs.com/package/debug"},"debug"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:"left"},"socket.io.msgpack.min.js"),(0,o.kt)("td",{parentName:"tr",align:"left"},"15.3 kB min+gzip"),(0,o.kt)("td",{parentName:"tr",align:"left"},"Production version, without ",(0,o.kt)("a",{parentName:"td",href:"https://www.npmjs.com/package/debug"},"debug")," and with the ",(0,o.kt)("a",{parentName:"td",href:"https://github.com/socketio/socket.io-msgpack-parser"},"msgpack parser"))))),(0,o.kt)("p",null,"By default, all of them are served by the server, at ",(0,o.kt)("inlineCode",{parentName:"p"},"/socket.io/<name>"),"."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-html"},'\x3c!-- note: this bundle was actually minified but included the debug package --\x3e\n<script src="/socket.io/socket.io.js"><\/script>\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-html"},'\x3c!-- during development --\x3e\n<script src="/socket.io/socket.io.js"><\/script>\n\x3c!-- for production --\x3e\n<script src="/socket.io/socket.io.min.js"><\/script>\n')),(0,o.kt)("h4",{id:"no-more-pong-event-for-retrieving-latency"},'No more "pong" event for retrieving latency'),(0,o.kt)("p",null,"In Socket.IO v2, you could listen to the ",(0,o.kt)("inlineCode",{parentName:"p"},"pong")," event on the client-side, which included the duration of the last health check round-trip."),(0,o.kt)("p",null,"Due to the reversal of the heartbeat mechanism (more information ",(0,o.kt)("a",{parentName:"p",href:"/blog/engine-io-4-release/#heartbeat-mechanism-reversal"},"here"),"), this event has been removed."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"}
1,'socket.on("pong", (latency) => {\n  console.log(latency);\n});\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// server-side\nio.on("connection", (socket) => {\n  socket.on("ping", (cb) => {\n    if (typeof cb === "function")\n      cb();\n  });\n});\n\n// client-side\nsetInterval(() => {\n  const start = Date.now();\n\n  // volatile, so the packet will be discarded if the socket is not connected\n  socket.volatile.emit("ping", () => {\n    const latency = Date.now() - start;\n    // ...\n  });\n}, 5000);\n')),(0,o.kt)("h4",{id:"es-modules-syntax"},"ES modules syntax"),(0,o.kt)("p",null,"The ECMAScript modules syntax is now similar to the Typescript one (see ",(0,o.kt)("a",{parentName:"p",href:"#the-socketio-codebase-has-been-rewritten-to-typescript"},"below"),")."),(0,o.kt)("p",null,"Before (using default import):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"// server-side\nimport Server from \"socket.io\";\n\nconst io = new Server(8080);\n\n// client-side\nimport io from 'socket.io-client';\n\nconst socket = io();\n")),(0,o.kt)("p",null,"After (with named import):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"// server-side\nimport { Server } from \"socket.io\";\n\nconst io = new Server(8080);\n\n// client-side\nimport { io } from 'socket.io-client';\n\nconst socket = io();\n")),(0,o.kt)("h4",{id:"emit-chains-are-not-possible-anymore"},(0,o.kt)("inlineCode",{parentName:"h4"},"emit()")," chains are not possible anymore"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"emit()")," method now matches the ",(0,o.kt)("a",{parentName:"p",href:"https://nodejs.org/dist/latest/docs/api/events.html#events_emitter_emit_eventname_args"},(0,o.kt)("inlineCode",{parentName:"a"},"EventEmitter.emit()"))," method signature, and returns ",(0,o.kt)("inlineCode",{parentName:"p"},"true")," instead of the current object."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'socket.emit("event1").emit("event2");\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'socket.emit("event1");\nsocket.emit("event2");\n')),(0,o.kt)("h4",{id:"room-names-are-not-coerced-to-string-anymore"},"Room names are not coerced to string anymore"),(0,o.kt)("p",null,"We are now using Maps and Sets internally instead of plain objects, so the room names are not implicitly coerced to string anymore."),(0,o.kt)("p",null,"Before:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// mixed types were possible\nsocket.join(42);\nio.to("42").emit("hello");\n// also worked\nsocket.join("42");\nio.to(42).emit("hello");\n')),(0,o.kt)("p",null,"After:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// one way\nsocket.join("42");\nio.to("42").emit("hello");\n// or another\nsocket.join(42);\nio.to(42).emit("hello");\n')),(0,o.kt)("h3",{id:"new-features"},"New features"),(0,o.kt)("p",null,"Some of those new features may be backported to the ",(0,o.kt)("inlineCode",{parentName:"p"},"2.4.x")," branch, depending on the feedback of the users."),(0,o.kt)("h4",{id:"catch-all-listeners"},"Catch-all listeners"),(0,o.kt)("p",null,"This feature is inspired from the ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/EventEmitter2/EventEmitter2"},"EventEmitter2")," library (which is not used directly in order not to increase the browser bundle size)."),(0,o.kt)("p",null,"It is available for both the server and the client sides:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// server\nio.on("connection", (socket) => {\n  socket.onAny((event, ...args) => {});\n  socket.prependAny((event, ...args) => {});\n  socket.offAny(); // remove all listeners\n  socket.offAny(listener);\n  const listeners = socket.listenersAny();\n});\n\n// client\nconst socket = io();\nsocket.onAny((event, ...args) => {});\nsocket.prependAny((event, ...args) => {});\nsocket.offAny(); // remove all listeners\nsocket.offAny(listener);\nconst listeners = socket.listenersAny();\n')),(0,o.kt)("h4",{id:"volatile-events-client"},"Volatile events (client)"),(0,o.kt)("p",null,"A volatile event is an event that is allowed to be dropped if the low-level transport is not ready yet (for example when an HTTP POST request is already pending)."),(0,o.kt)("p",null,"This feature was already available on the server-side. It might be useful on the client-side as well, for example when the socket is not connected (by default, packets are buffered until reconnection)."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"}
1,'socket.volatile.emit("volatile event", "might or might not be sent");\n')),(0,o.kt)("h4",{id:"official-bundle-with-the-msgpack-parser"},"Official bundle with the msgpack parser"),(0,o.kt)("p",null,"A bundle with the ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/socketio/socket.io-msgpack-parser"},"socket.io-msgpack-parser")," will now be provided (either on the CDN or served by the server at ",(0,o.kt)("inlineCode",{parentName:"p"},"/socket.io/socket.io.msgpack.min.js"),")."),(0,o.kt)("p",null,"Pros:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"events with binary content are sent as 1 WebSocket frame (instead of 2+ with the default parser)"),(0,o.kt)("li",{parentName:"ul"},"payloads with lots of numbers should be smaller")),(0,o.kt)("p",null,"Cons:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"no IE9 support (",(0,o.kt)("a",{parentName:"li",href:"https://caniuse.com/mdn-javascript_builtins_arraybuffer"},"https://caniuse.com/mdn-javascript_builtins_arraybuffer"),")"),(0,o.kt)("li",{parentName:"ul"},"a slightly bigger bundle size")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'// server-side\nconst io = require("socket.io")(httpServer, {\n  parser: require("socket.io-msgpack-parser")\n});\n')),(0,o.kt)("p",null,"No additional configuration is needed on the client-side."),(0,o.kt)("h3",{id:"miscellaneous"},"Miscellaneous"),(0,o.kt)("h4",{id:"the-socketio-codebase-has-been-rewritten-to-typescript"},"The Socket.IO codebase has been rewritten to TypeScript"),(0,o.kt)("p",null,"Which means ",(0,o.kt)("inlineCode",{parentName:"p"},"npm i -D @types/socket.io")," should not be needed anymore."),(0,o.kt)("p",null,"Server:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},'import { Server, Socket } from "socket.io";\n\nconst io = new Server(8080);\n\nio.on("connection", (socket: Socket) => {\n    console.log(`connect ${socket.id}`);\n\n    socket.on("disconnect", () => {\n        console.log(`disconnect ${socket.id}`);\n    });\n});\n')),(0,o.kt)("p",null,"Client:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},'import { io } from "socket.io-client";\n\nconst socket = io("/");\n\nsocket.on("connect", () => {\n    console.log(`connect ${socket.id}`);\n});\n')),(0,o.kt)("p",null,"Plain javascript is obviously still fully supported."),(0,o.kt)("h4",{id:"support-for-ie8-and-nodejs-8-is-officially-dropped"},"Support for IE8 and Node.js 8 is officially dropped"),(0,o.kt)("p",null,"IE8 is no longer testable on the Sauce Labs platform, and requires a lot of efforts for very few users (if any?), so we are dropping support for it."),(0,o.kt)("p",null,"Besides, Node.js 8 is now ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/nodejs/Release"},"EOL"),". Please upgrade as soon as possible!"),(0,o.kt)("h3",{id:"how-to-upgrade-an-existing-production-deployment"},"How to upgrade an existing production deployment"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"first, update the servers with ",(0,o.kt)("inlineCode",{parentName:"li"},"allowEIO3")," set to ",(0,o.kt)("inlineCode",{parentName:"li"},"true")," (added in ",(0,o.kt)("inlineCode",{parentName:"li"},"[email protected]"),")")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const io = require("socket.io")({\n  allowEIO3: true // false by default\n});\n')),(0,o.kt)("p",null,"Note: If you are using the Redis adapter to ",(0,o.kt)("a",{parentName:"p",href:"/docs/v4/broadcasting-events/#with-multiple-socketio-servers"},"broadcast packets between nodes"),", you must use ",(0,o.kt)("inlineCode",{parentName:"p"},"socket.io-redis@5")," with ",(0,o.kt)("inlineCode",{parentName:"p"},"socket.io@2")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"socket.io-redis@6")," with ",(0,o.kt)("inlineCode",{parentName:"p"},"socket.io@3"),". Please note that both versions are compatible, so you can update each server one by one (no big bang is needed)."),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"then, update the clients")),(0,o.kt)("p",null,"This step may actually take some time, as some clients may 
1still have a v2 client in cache."),(0,o.kt)("p",null,"You can check the version of the connection with:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'io.on("connection", (socket) => {\n  const version = socket.conn.protocol; // either 3 or 4\n});\n')),(0,o.kt)("p",null,"This matches the value of the ",(0,o.kt)("inlineCode",{parentName:"p"},"EIO")," query parameter in the HTTP requests."),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"and finally, once every client was updated, set ",(0,o.kt)("inlineCode",{parentName:"li"},"allowEIO3")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"false")," (which is the default value)")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const io = require("socket.io")({\n  allowEIO3: false\n});\n')),(0,o.kt)("p",null,"With ",(0,o.kt)("inlineCode",{parentName:"p"},"allowEIO3")," set to ",(0,o.kt)("inlineCode",{parentName:"p"},"false"),", v2 clients will now receive an HTTP 400 error (",(0,o.kt)("inlineCode",{parentName:"p"},"Unsupported protocol version"),") when connecting."),(0,o.kt)("h3",{id:"known-migration-issues"},"Known migration issues"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"stream_1.pipeline is not a function"))),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"TypeError: stream_1.pipeline is not a function\n    at Function.sendFile (.../node_modules/socket.io/dist/index.js:249:26)\n    at Server.serve (.../node_modules/socket.io/dist/index.js:225:16)\n    at Server.srv.on (.../node_modules/socket.io/dist/index.js:186:22)\n    at emitTwo (events.js:126:13)\n    at Server.emit (events.js:214:7)\n    at parserOnIncoming (_http_server.js:602:12)\n    at HTTPParser.parserOnHeadersComplete (_http_common.js:116:23)\n")),(0,o.kt)("p",null,"This error is probably due to your version of Node.js. The ",(0,o.kt)("a",{parentName:"p",href:"https://nodejs.org/api/stream.html#stream_stream_pipeline_source_transforms_destination_callback"},"pipeline")," method was introduced in Node.js 10.0.0."),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"error TS2416: Property 'emit' in type 'Namespace' is not assignable to the same property in base type 'EventEmitter'."))),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"node_modules/socket.io/dist/namespace.d.ts(89,5): error TS2416: Property 'emit' in type 'Namespace' is not assignable to the same property in base type 'EventEmitter'.\n  Type '(ev: string, ...args: any[]) => Namespace' is not assignable to type '(event: string | symbol, ...args: any[]) => boolean'.\n    Type 'Namespace' is not assignable to type 'boolean'.\nnode_modules/socket.io/dist/socket.d.ts(84,5): error TS2416: Property 'emit' in type 'Socket' is not assignable to the same property in base type 'EventEmitter'.\n  Type '(ev: string, ...args: any[]) => this' is not assignable to type '(event: string | symbol, ...args: any[]) => boolean'.\n    Type 'this' is not assignable to type 'boolean'.\n      Type 'Socket' is not assignable to type 'boolean'.\n")),(0,o.kt)("p",null,"The signature of the ",(0,o.kt)("inlineCode",{parentName:"p"},"emit()")," method was fixed in version ",(0,o.kt)("inlineCode",{parentName:"p"},"3.0.1")," (",(0,o.kt)("a",{parentName:"p",href:"https://github.com/socketio/socket.io/commit/50671d984a81535a6a15c704546ca7465e2ea295"},"commit"),")."),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"the client is disconnected when sending a big payload (> 1MB)")),(0,o.kt)("p",null,"This is probably due to the fact that the default value of ",(0,o.kt)("inlineCode",{parentName:"p"},"maxHttpBufferSize")," is now ",(0,o.kt)("inlineCode",{parentName:"p"},"1MB"),". When receiving a packet that is larger than this, the server disconnects the client, in order to prevent malicious clients from overloading the server."),(0,o.kt)("p",null,"You can adjust the value when creating the server:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'const io = require("socket.io")(httpServer, {\n  maxHttpBufferSize: 1e8\n});\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at xxx/socket.io/?EIO=4&transport=polling&t=NMnp2WI. (Reason: CORS header \u2018Access-Control-Allow-Origin\u2019 missing)."))),(0,o.kt)("p",null,"Since Socket.IO v3, you need to explicitly enable ",(0,o.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"},"Cross-Origin Resource Sharing")," (CORS). The documentation can be found ",(0,o.kt)("a",{parentName:"p",href:"/docs/v4/handling-cors/"},"here"),"."),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Uncaught TypeError: packet.data is undefined"))),(0,o.kt)("p",null,"It seems that you are using a v3 client to connect to a v2 server, which is not possible. Please see the ",(0,o.kt)("a",{parentName:"p",href:"#how-to-upgrade-an-existing-production-deployment"},"following section"),"."),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Object literal may only specify known properties, and 'extraHeaders' does not exist in type 'ConnectOpts'"))),(0,o.kt)("p",null,"Since the codebase has been rewritten to TypeScript (more information ",(0,o.kt)("a",{parentName:"p",href:"#the-socketio-codebase-has-been-rewritten-to-typescript"},"here"),"), ",(0,o.kt)("inlineCode",{parentName:"p"},"@types/socket.io-client")," is no longer needed and will actually conflict with the typings coming from the ",(0,o.kt)("inlineCode",{parentName:"p"},"socket.io-client")," package."),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"missing cookie in a cross-origin context")),(0,o.kt)("p",null,"You now need to explicitly enable cookies if the front is not served from the same domain as the backend:"),(0,o.kt)("p",null,(0,o.kt)("em",{parentName:"p"},"Server")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'import { Server } from "socket.io";\n\nconst io = new Server({\n  cors: {\n    origin: ["https://front.domain.com"],\n    credentials: true\n  }\n});\n')),(0,o.kt)("p",null,(0,o.kt)("em",{parentName:"p"},"Client")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},'import { io } from "socket.io-client";\n\nconst socket = io("https://backend.domain.com", {\n  withCredentials: true\n});\n')),(0,o.kt)("p",null,"Reference:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/docs/v4/handling-cors/"},"Handling CORS")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/docs/v4/server-api/#cors"},(0,o.kt)("inlineCode",{parentName:"a"},"cors"))," option"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/docs/v4/client-api/#withcredentials"},(0,o.kt)("inlineCode",{parentName:"a"},"withCredentials"))," option")))}d.isMDXComponent=!0}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.