Realtime (socket.io)
Frappe ships with an API for realtime events based on socket.io. Since socket.io needs a Node server to run, we run a Node process in parallel to the main web server.
Client APIs (JavaScript)
frappe.realtime.on
To listen to realtime events on the client (browser), you can use the frappe.realtime.on
method:
frappe.realtime.on('event\_name', (data) => {
console.log(data)
})
frappe.realtime.off
Stop listening to an event you have subscribed to:
frappe.realtime.off('event\_name')
Server APIs (Python)
frappe.publish_realtime
To publish a realtime event from the server, you can use the frappe.publish_realtime
method:
frappe.publish_realtime('event_name', data={'key': 'value'})
frappe.publish_progress
You can use this method to show a progress bar in a dialog:
frappe.publish_progress(25, title='Some title', description='Some description')
Custom Event Handlers
Note: This feature is only available in nightly (v16) version. This feature is considered experimental.
You can implement custom real-time event handlers by creating a handlers.js
file in real-time folder of your app.
You need to specify a single export from this file - a function that will setup event handlers on the socket instance. E.g. if you are developing an app called "chat", here's how the file should look like
// bench/apps/chat/realtime/handlers.js
function chat\_app\_handlers(socket) {
socket.on("hello\_chat", () => {
console.log("hello world!");
});
}
module.exports = chat_app_handlers;
You can trigger this event using client side code frappe.realtime.emit("hello_chat")
. Note that you might have to restart socketio server to see effect of code changes. Refer SocketIO documentation to learn more about writing custom event handlers: https://socket.io/docs/v4/
Custom Client
You can write a custom client to connect to socket.io server if you're developing a SPA or a mobile app that doesn't use Desk interface. You can refer to official socket.io documentation for building your custom client: https://socket.io/docs/v4/client-initialization/
Here are some examples of custom clients:
- https://github.com/frappe/gameplan/blob/9f9332cf29496afe5e912e4f1734fbf1142cb18c/frontend/src/socket.js#L13
- https://github.com/frappe/frappe/blob/8093a1d0a54900fe4b43b01ae6ffc0adf855da43/frappe/public/js/frappe/socketio_client.js#L49
Authorization in custom clients
There are two ways to authenticate a connection with socket.io server:
- Cookies - if your custom client is in a browser-like environment then your connection will automatically send cookies and socketio server will be able to authenticate using the cookies.
- Authorization header: if cookies are not available in your environment like mobile apps then you can use Authorization headers just like API requests. Refer REST API authentication documentation to understand this: https://frappeframework.com/docs/user/en/api/rest#authentication and https://socket.io/docs/v4/client-options/#extraheaders
Implementation Notes
- Realtime server uses socket.io server. The server is written in node.js and can be found in
/realtime
directory. - Realtime client is wrapper around socket.io client library and can be found in
public/js/frappe/socketio_client.js
. - Python processes publish events to node server using Redis pub-sub channel. Realtime server subscribes to the Redis channel and publishes it to all subscribed clients.
- Realtime server is multi-tenant, all site-related traffic is namespaced by sitename. Namespaces are dynamically created using
/{sitename}
format where sitename is name of site's folder in sites directory orfrappe.local.site
. - Realtime server uses main Frappe web server to authenticate connections. SID cookie or authorization header are passed to client and realtime server uses it to ensure that the connection is from a valid user and can subscribe to certain DocType or documents based on permissions.
Realtime implementation allows the following rooms:
all
- Room accessible and connected by default to all System Users.website
- Room accessible and connected by any user, even Guests.user:{username}
- Dynamic room created for each user. Allowed without any permission checks.doctype:{doctype}
- Dynamic room created for each DocType. Only user with permission to the DocType can join said room. User is automatically subscribed to this room when they open list view or form view of the document.doc:{doctype}/{name}
- Dynamic room created for each document. Only user with permission to the document room can join said room. User is automatically subscribed to this room when they form view of the document.