{"id":13812215,"url":"https://github.com/supabase-community/realtime-csharp","last_synced_at":"2026-03-14T23:39:41.862Z","repository":{"id":54646802,"uuid":"323599604","full_name":"supabase-community/realtime-csharp","owner":"supabase-community","description":"A C# client library for supabase/realtime.","archived":false,"fork":false,"pushed_at":"2025-03-10T12:00:54.000Z","size":19495,"stargazers_count":74,"open_issues_count":11,"forks_count":15,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-07T02:25:27.217Z","etag":null,"topics":["realtime","realtime-csharp","socket","supabase"],"latest_commit_sha":null,"homepage":"https://supabase-community.github.io/realtime-csharp/api/Supabase.Realtime.Client.html","language":"C#","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/supabase-community.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2020-12-22T10:56:01.000Z","updated_at":"2025-04-04T04:42:27.000Z","dependencies_parsed_at":"2024-01-03T21:25:17.774Z","dependency_job_id":"9df39555-64f3-4b50-b9a9-6af1b17bdd6b","html_url":"https://github.com/supabase-community/realtime-csharp","commit_stats":null,"previous_names":["supabase/realtime-csharp"],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase-community%2Frealtime-csharp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase-community%2Frealtime-csharp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase-community%2Frealtime-csharp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase-community%2Frealtime-csharp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/supabase-community","download_url":"https://codeload.github.com/supabase-community/realtime-csharp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247601447,"owners_count":20964864,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["realtime","realtime-csharp","socket","supabase"],"created_at":"2024-08-04T04:00:49.298Z","updated_at":"2025-11-24T18:03:44.156Z","avatar_url":"https://github.com/supabase-community.png","language":"C#","funding_links":[],"categories":["Realtime"],"sub_categories":[],"readme":"# Supabase.Realtime\n\n[![Build and Test](https://github.com/supabase-community/realtime-csharp/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/supabase-community/realtime-csharp/actions/workflows/build-and-test.yml)\n[![NuGet](https://img.shields.io/nuget/vpre/Supabase.Realtime)](https://www.nuget.org/packages/Supabase.Realtime/)\n\n## [Notice]: v7.0.0 renames this package from `realtime-csharp` to `Supabase.Realtime`. The depreciation notice has been set in NuGet. The API remains the same.\n\n\n## BREAKING CHANGES MOVING FROM v5.x.x to v6.x.x\n\n- The realtime client now takes a \"fail-fast\" approach. On establishing an initial connection, client will throw\n  a `RealtimeException` in `ConnectAsync()` if the socket server is unreachable. After an initial connection has been\n  established, the **client will continue attempting reconnections indefinitely until disconnected.**\n- [Major, New] C# `EventHandlers` have been changed to `delegates`. This should allow for cleaner event data access over\n  the previous subclassed `EventArgs` setup. Events are scoped accordingly. For example, the `RealtimeSocket` error\n  handlers will receive events regarding socket connectivity; whereas the `RealtimeChannel` error handlers will receive\n  events according to `Channel` joining/leaving/etc. This is implemented with the following methods prefixed by (\n  Add/Remove/Clear):\n    - `RealtimeBroadcast.AddBroadcastEventHandler`\n    - `RealtimePresence.AddPresenceEventHandler`\n    - `RealtimeSocket.AddStateChangedHandler`\n    - `RealtimeSocket.AddMessageReceivedHandler`\n    - `RealtimeSocket.AddHeartbeatHandler`\n    - `RealtimeSocket.AddErrorHandler`\n    - `RealtimeClient.AddDebugHandler`\n    - `RealtimeClient.AddStateChangedHandler`\n    - `RealtimeChannel.AddPostgresChangeHandler`\n    - `RealtimeChannel.AddMessageReceivedHandler`\n    - `RealtimeChannel.AddErrorHandler`\n    - `Push.AddMessageReceivedHandler`\n- [Major, new] `ClientOptions.Logger` has been removed in favor of `Client.AddDebugHandler()` which allows for\n  implementing custom logging solutions if desired.\n  - A simple logger can be set up with the following:\n  ```c#\n  client.AddDebugHandler((sender, message, exception) =\u003e Debug.WriteLine(message));\n  ```\n- [Major] `Connect()` has been marked `Obsolete` in favor of `ConnectAsync()`\n- Custom reconnection logic has been removed in favor of using the built-in logic from `Websocket.Client@4.6.1`.\n- Exceptions that are handled within this library have been marked as `RealtimeException`s.\n- The local, docker-composed test suite has been brought back (as opposed to remotely testing on live supabase servers)\n  to test against.\n- Comments have been added throughout the entire codebase and an `XML` file is now generated on build.\n\n---\n\n**See realtime-csharp in action [here](https://multiplayer-csharp.azurewebsites.net/).**\n\n`realtime-csharp` is written as a client library for [supabase/realtime](https://github.com/supabase/realtime).\n\nDocumentation can be\nfound [here](https://supabase-community.github.io/realtime-csharp/api/Supabase.Realtime.Client.html).\n\nThe bulk of this library is a translation and c-sharp-ification of\nthe [supabase/realtime-js](https://github.com/supabase/realtime-js) library.\n\n**The Websocket-sharp implementation that Realtime-csharp is dependent on does _not_ support TLS1.3**\n\n## Getting Started\n\nCare was had to make this API as _easy\u003csup\u003etm\u003c/sup\u003e_ to interact with as possible. `Connect()` and `Subscribe()`\nhave `await`-able signatures\nwhich allow Users to be assured that a connection exists prior to interacting with it.\n\n```c#\nvar endpoint = \"ws://realtime-dev.localhost:4000/socket\";\nclient = new Client(endpoint);\n\nawait client.ConnectAsync();\n\n// Shorthand for registering a postgres_changes subscription\nvar channel = client.Channel(\"realtime\", \"public\", \"todos\");\n\n// Listen to Updates\nchannel.AddPostgresChangeHandler(ListenType.Updates, (_, change) =\u003e\n{\n    var model = change.Model\u003cTodo\u003e();\n    var oldModel = change.OldModel\u003cTodo\u003e();\n});\nawait channel.Subscribe();\n```\n\nLeveraging `Postgrest.BaseModel`s, one ought to be able to coerce SocketResponse Records into their associated models by\ncalling:\n\n```c#\n// ...\nvar channel = client.Channel(\"realtime\", \"public\", \"users\");\n\nchannel.AddPostgresChangeHandler(ListenType.Inserts, (_, change) =\u003e\n{\n    var model = change.Model\u003cTodo\u003e();\n});\n\nawait channel.Subscribe();\n```\n\n## Broadcast\n\n\"Broadcast follows the publish-subscribe pattern where a client publishes messages to a channel with a unique\nidentifier. For example, a user could send a message to a channel with id room-1.\n\nOther clients can elect to receive the message in real-time by subscribing to the channel with id room-1. If these\nclients are online and subscribed then they will receive the message.\n\nBroadcast works by connecting your client to the nearest Realtime server, which will communicate with other servers to\nrelay messages to other clients.\n\nA common use-case is sharing a user's cursor position with other clients in an online game.\"\n\n[Find more information here](https://supabase.com/docs/guides/realtime#broadcast)\n\n**Given the following model (`CursorBroadcast`):**\n\n```c#\nclass MouseBroadcast : BaseBroadcast\u003cMouseStatus\u003e { }\nclass MouseStatus\n{\n\t[JsonProperty(\"mouseX\")]\n\tpublic float MouseX { get; set; }\n\n\t[JsonProperty(\"mouseY\")]\n\tpublic float MouseY { get; set; }\n\n\t[JsonProperty(\"userId\")]\n\tpublic string UserId { get; set; }\n}\n```\n\n**Listen for typed broadcast events**:\n\n```c#\nvar channel = supabase.Realtime.Channel(\"cursor\");\n\nvar broadcast = channel.Register\u003cMouseBroadcast\u003e(false, true);\nbroadcast.AddBroadcastEventHandler((sender, _) =\u003e\n{\n    // Retrieved typed model.\n    var state = broadcast.Current();\n    \n    Debug.WriteLine($\"{state.Payload}: {state.Payload.MouseX}:{state.Payload.MouseY}\");\n});\nawait channel.Subscribe();\n```\n\n**Broadcast an event**:\n\n```c#\nvar channel = supabase.Realtime.Channel(\"cursor\");\nvar data = new CursorBroadcast { Event = \"cursor\", Payload = new MouseStatus { MouseX = 123, MouseY = 456 } };\nchannel.Send(ChannelType.Broadcast, data);\n```\n\n## Presence\n\n\"Presence utilizes an in-memory conflict-free replicated data type (CRDT) to track and synchronize shared state in an\neventually consistent manner. It computes the difference between existing state and new state changes and sends the\nnecessary updates to clients via Broadcast.\n\nWhen a new client subscribes to a channel, it will immediately receive the channel's latest state in a single message\ninstead of waiting for all other clients to send their individual states.\n\nClients are free to come-and-go as they please, and as long as they are all subscribed to the same channel then they\nwill all have the same Presence state as each other.\n\nThe neat thing about Presence is that if a client is suddenly disconnected (for example, they go offline), their state\nwill be automatically removed from the shared state. If you've ever tried to build an “I'm online” feature which handles\nunexpected disconnects, you'll appreciate how useful this is.\"\n\n[Find more information here](https://supabase.com/docs/guides/realtime#presence)\n\n**Given the following model: (`UserPresence`)**\n\n```c#\nclass UserPresence: BasePresence\n{\n    [JsonProperty(\"lastSeen\")]\n    public DateTime LastSeen { get; set; }\n}\n```\n\n**Listen for typed presence events**:\n\n```c#\nvar presenceId = Guid.NewGuid().ToString();\n\nvar channel = supabase.Realtime.Channel(\"last-seen\");\nvar presence = channel.Register\u003cUserPresence\u003e(presenceId);\n\npresence.AddPresenceEventHandler(EventType.Sync, (sender, type) =\u003e\n{\n    foreach (var state in presence.CurrentState)\n    {\n        var userId = state.Key;\n        var lastSeen = state.Value.First().LastSeen;\n        Debug.WriteLine($\"{userId}: {lastSeen}\");\n    }\n});\nawait channel.Subscribe();\n```\n\n**Track a user presence event**:\n\n```c#\nvar presenceId = Guid.NewGuid().ToString();\nvar channel = supabase.Realtime.Channel(\"last-seen\");\n\nvar presence = channel.Register\u003cUserPresence\u003e(presenceId);\npresence.Track(new UserPresence { LastSeen = DateTime.Now });\n```\n\n## Postgres Changes\n\n\"Postgres Changes enable you to listen to database changes and have them broadcast to authorized clients based\non [Row Level Security (RLS)](https://supabase.com/docs/guides/auth/row-level-security) policies.\n\nThis works by Realtime polling your database's logical replication slot for changes, passing those changes to\nthe [apply_rls](https://github.com/supabase/walrus#reading-wal) SQL function to determine which clients have permission,\nand then using Broadcast to send those changes to clients.\n\nRealtime requires a publication called `supabase_realtime` to determine which tables to poll. You must add tables to\nthis publication prior to clients subscribing to channels that want to listen for database changes.\n\nWe strongly encourage you to enable RLS on your database tables and have RLS policies in place to prevent unauthorized\nparties from accessing your data.\"\n\n[Find More Information here](https://supabase.com/docs/guides/realtime#postgres-changes)\n\n**Using the new `Register` method:**\n\n```c#\nvar channel = supabase.Realtime.Channel(\"public-users\");\nchannel.Register(new PostgresChangesOptions(\"public\", \"users\"));\nchannel.AddPostgresChangeHandler(ListenType.All, (sender, change) =\u003e\n{\n    switch (change.Event)\n    {\n        case EventType.Insert:\n            // User has been created\n            break;\n        case EventType.Update:\n            // User has been updated\n            break;\n        case EventType.Delete:\n            // User has been deleted\n            break;\n    }\n});\nawait channel.Subscribe();\n```\n\n## Status\n\n- [x] Client Connects to Websocket\n- [x] Socket Event Handlers\n    - [x] Open\n    - [x] Close - when channel is explicitly closed by server or by calling `Channel.Unsubscribe()`\n    - [x] Error\n- [x] Realtime Event Handlers\n    - [x] `INSERT`\n    - [x] `UPDATE`\n    - [x] `DELETE`\n    - [x] `*`\n- [x] Join channels of format:\n    - [x] `{database}`\n    - [x] `{database}:{schema}`\n    - [x] `{database}:{schema}:{table}`\n    - [x] `{database}:{schema}:{table}:{col}.eq.{val}`\n- [x] Responses supply a Generically Typed Model derived from `BaseModel`\n- [x] Ability to remove subscription to Realtime Events\n- [x] Ability to disconnect from socket.\n- [x] Socket reconnects when possible\n- [x] Unit Tests\n- [x] Documentation\n- [x] Nuget Release\n\n## Package made possible through the efforts of:\n\nJoin the ranks! See a problem? Help fix it!\n\n\u003ca href=\"https://github.com/supabase-community/realtime-csharp/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=supabase-community/realtime-csharp\" /\u003e\n\u003c/a\u003e\n\nMade with [contrib.rocks](https://contrib.rocks/preview?repo=supabase-community%2Frealtime-csharp).\n\n## Contributing\n\nWe are more than happy to have contributions! Please submit a PR.\n\n## Testing\n\nNote that the latest versions of `supabase/realtime` expect to be able to access a subdomain matching the tenant. For\nthe case of testing, this means that `realtime-dev.localhost:4000` should be available. To have tests run locally,\nplease add a hosts entry on your system for: `127.0.0.1  realtime-dev.localhost`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsupabase-community%2Frealtime-csharp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsupabase-community%2Frealtime-csharp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsupabase-community%2Frealtime-csharp/lists"}