supabase-csharp: the official C# client, split so you can take one service
A C# Client library for Supabase
At a glance
- What is it?
- A monorepo holding the Supabase meta package and one package per service, so a Unity or MAUI app can take only Auth or only Storage, with LINQ over PostgREST and OpenTelemetry instrumentation that stays silent until you subscribe. The service key warning in the readme is the part to read before anything else.
- Who is it for?
- Adopt supabase-csharp if you are shipping a .NET, MAUI, Xamarin or Unity application against Supabase and want the official client with per-service packages so you are not shipping PostgREST and Storage to a client that needs neither. Skip it if you are on the official JavaScript or Dart clients, since the Supabase platform does not care which client you use.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One repository, seven NuGet packages, and a choice of scope
The Supabase C# SDK is the official C# client for Supabase, and the repository is a monorepo: the top-level Supabase client and every service package it builds on live under packages.
The point of that structure is the second sentence of the readme. Use the Supabase meta-package for the full client, or reference an individual service package, Auth, Postgrest, Storage, Realtime, Functions, on its own.
That is a real decision rather than a packaging detail. A Unity game or a MAUI app that only needs authentication and a user profile does not want the PostgREST client, the storage client and the realtime websocket client in its build output. Taking the individual package means those assemblies are simply not referenced.
The package table gives the purposes. Supabase is the unified client that wires the services together, and the readme says start here. Supabase.Gotrue is authentication, covering email and password, OAuth, SSO and magic links. Supabase.Postgrest queries your database through the auto-generated REST API, with LINQ. Supabase.Storage is file storage, upload, download, signed URLs and buckets. Supabase.Realtime is Postgres changes, Broadcast and Presence over websockets. Supabase.Functions invokes Edge Functions. And Supabase.Core holds shared primitives used by the packages above, which the readme says is rarely referenced directly.
So the shape is one umbrella plus five services plus a shared base, and the umbrella is optional.
One client object reaches every service
The API is unified through a single client instance, and the quickstart shows the whole sequence in one block.
using Supabase;
var url = Environment.GetEnvironmentVariable("SUPABASE_URL");
var key = Environment.GetEnvironmentVariable("SUPABASE_KEY");
var options = new SupabaseOptions
{
AutoRefreshToken = true,
AutoConnectRealtime = true
};
var supabase = new Client(url, key, options);
await supabase.InitializeAsync();The two options in the constructor are the ones worth understanding. AutoRefreshToken means the client keeps a valid session for you rather than forcing you to re-authenticate on expiry, and AutoConnectRealtime means the realtime websocket is established during initialization rather than when you first subscribe.
After InitializeAsync, each service is reached from the client: supabase.Auth, supabase.From<T>() together with supabase.Rpc(...), supabase.Storage, supabase.Realtime and supabase.Functions.
The From<T>() generic is the interesting piece for database work, because it gives you a typed query rather than an untyped JSON payload. A call like supabase.From<Movie>().Get() selects from a table into a C# type, and the same call shape covers the rest of the query builder. The readme states the purpose plainly: query your database through the auto-generated REST API, with LINQ.
For using a single service on its own, the readme points you to that package's own README.
The service key warning, and why you need two client instances
There is a note about keys in the readme that deserves to be quoted in full, because it is the one piece of this SDK that can cause a security incident rather than a compile error.
Some APIs, user administration and bypassing RLS among them, require the service_key rather than the public or anon key. Never expose a service_key in client-side code, and use a separate client instance for service and user contexts.
The mechanism behind that advice is worth understanding, because it explains why the SDK is shaped the way it is. PostgREST is the auto-generated REST API in front of your database, and row level security is enforced by the database according to the role of the requesting key. The public or anon key carries the anonymous role, and the service key carries a privileged one. The whole security model of a Supabase database rests on the key determining what the request is allowed to do.
So a service key in a mobile app is the same as publishing your database credentials. The advice to use a separate client instance for service and user contexts is the practical implementation of that, and it is a hint about how the client is designed: one client, one key, one role. If you need both, you construct two clients and hold them separately, and the readme is telling you to do that deliberately rather than swapping a key on a shared instance.
It is also the reason the SDK does not have a single built-in admin mode, which would have been the more convenient design if the key were not the entire trust boundary.
OpenTelemetry without taking a dependency on OpenTelemetry
The observability design is the most interesting engineering decision in the SDK, and it is a deliberate constraint.
The clients emit traces and metrics through System.Diagnostics, so you can wire them into OpenTelemetry, or any ActivityListener or MeterListener, without taking a dependency on the OpenTelemetry packages. Emission is zero-cost while nothing is listening, so it is always on and stays silent until you subscribe.
That is a well-known .NET pattern and it is the right one for a library. A client library that references an observability SDK forces that SDK on every consumer, and in practice consumers end up with version conflicts. Using System.Diagnostics directly means the .NET base class library provides the instrumentation points and the consumer decides what to plug in.
The registration is symmetrical because each client shares one name between its ActivitySource and its Meter:
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource(SupabaseDiagnostics.SourceNames.ToArray())
.AddOtlpExporter())
.WithMetrics(metrics => metrics
.AddMeter(SupabaseDiagnostics.SourceNames.ToArray())
.AddOtlpExporter());The coverage has a gap worth knowing. This covers the Auth, Postgrest, Functions and Storage clients, and Realtime is not yet instrumented.
There is also a deliberate limit on what goes into a span. URLs are recorded without their query string, and no token, credential or payload is placed in a tag. For a library that talks to an authenticated API, that is the difference between useful tracing and a credential leak in your tracing backend, and each package's README documents the specific spans and metrics it produces.
Targeting .NET Standard 2.1, and what that buys you
Every package targets .NET Standard 2.1, so it runs on .NET Core 3.0 and later, .NET 5 and later, recent MAUI, Xamarin, and Unity.
That single target framework choice is what makes the package list possible. .NET Standard 2.1 is the last .NET Standard version, and it is the one that carries the .NET Core 3 era runtime surface. Targeting it rather than a specific .NET version is what lets one binary work in a server application, a MAUI app, a Xamarin app and a Unity game, which is an unusual spread for a client SDK and explains why the wiki carries platform-specific guides for Unity, desktop and mobile, and server-side.
The trade of .NET Standard 2.1 is that it is a fixed surface, so newer runtime APIs are not available to the library, and it is the end of the line, so there is no further standard to move to. For a client library talking HTTP and websockets that is not a constraint in practice.
The wiki link is worth noting as the place to look for platform specifics. The readme points at it for platform-specific guides covering Unity, desktop and mobile, and server-side, and there are separate wiki pages for Getting Started, Examples and Troubleshooting, plus a Supabase C# reference on the Supabase documentation site and generated API docs.
Building the SDK and the test stack it expects
The build section is short and worth following exactly if you intend to work on the SDK rather than consume it.
The SDK builds with the .NET SDK version pinned in global.json, and the three commands are:
dotnet restore
dotnet build Supabase.sln --configuration Release
dotnet test Supabase.sln --configuration ReleaseThe layout explains the central package management. Each packages/ directory holds the library project and its test project, so the repository contains seven libraries and seven test projects. Package metadata common to every project, covering licence, authors and repository, lives in Directory.Build.props, and dependency versions are managed centrally in Directory.Packages.props.
The test setup has a wrinkle that will cost you an hour if you do not know it in advance. Some test suites are end-to-end and need a local Supabase stack via the Supabase CLI, using supabase start, which requires Docker.
So a clone of this repository does not build and test on a bare machine. You need Docker running for the end-to-end suites, and the Supabase CLI to bring up the local stack they point at. That is a reasonable arrangement for an SDK whose whole point is talking to a Supabase instance, but it does mean a contributor needs a working local Supabase before the test run is green.
The rest of the tree is standard for a modern .NET repository: a .globalconfig for the analyser settings, an .editorconfig, a .claude directory, a .documentation directory, release-please-config.json with a .release-please-manifest.json, a CHANGELOG.md, and an sdk-compliance.yaml.
The licence is MIT, with the LICENSE file at the root.
Where this client is the wrong choice
There are cases where the C# client will not be what you need, and the readme hints at most of them.
The first is the language. Supabase has official clients for JavaScript and Dart, and the platform itself is indifferent to which one you use, so a team whose frontend and backend both live in TypeScript has no reason to introduce a C# client for the same project. A C# client makes sense when the application is genuinely .NET, and specifically when it is a platform Supabase's other clients do not cover well: MAUI, Xamarin, or Unity.
The second is feature parity. The readme describes what this client covers, and the phrase official client is about the client, not about every Supabase feature. Where a capability is not implemented in C#, the answer is that you are calling the HTTP API directly or waiting, and the version numbers move fast enough that the gap list changes between releases.
The third is the Realtime instrumentation gap. If your interest in this SDK is realtime and you run a service with OpenTelemetry on it, the client you care about least is the one that is not instrumented. That is a narrow point, but it is a real one for a team that treats tracing coverage as a selection criterion.
The fourth is that the service key boundary has to be respected in your architecture, not just in your code. If your application needs administrative operations and the only place to run them is a mobile client, no SDK design fixes that. The readme's instruction to use a separate client instance is a code-level answer to an architecture-level problem, and the architecture has to be there first.
The fifth is the build and test cost if you are a contributor. Every package has its own test project, some of them end-to-end, and the end-to-end ones need Docker and a local Supabase stack. Contributing to this SDK is not a clone and build exercise.
The release process, and what the version numbers tell you
Releases are automated with release-please, commits follow Conventional Commits, and the changelog is generated. The repository also has a release-please-config.json and a .release-please-manifest.json, which is the pattern that lets a monorepo version each package independently while still producing one coherent changelog.
The recent releases show that independence in action. v8.3.0 was published on 2026-10-05, v8.3.1 on 2026-10-09, and v8.4.0 on 2026-10-16, so the patch line and a minor line moved within two weeks of each other. The last push was on 2026-10-16, the same day as v8.4.0.
The NuGet badge in the readme is a prerelease version badge, which is a small detail with a consequence: the packages you find on nuget.org by default may be a prerelease, and the readme asks you to check the wiki and the Supabase documentation site for the current state of the reference rather than assuming the highest version is the stable one.
For an adopter, the practical picture is a client that ships often, follows a conventional commit discipline, and generates its changelog from the history. That is a healthy shape for a client SDK, and it is also a shape that means you should pin a version rather than track a floating one, because a minor bump can arrive every week.
Editorial conclusion
Adopt supabase-csharp if you are shipping a .NET, MAUI, Xamarin or Unity application against Supabase and want the official client with per-service packages so you are not shipping PostgREST and Storage to a client that needs neither. Skip it if you are on the official JavaScript or Dart clients, since the Supabase platform does not care which client you use. Verify first that the platform features you rely on exist in the C# client, that you separate the service key client from the user client as the readme instructs, and that the Realtime instrumentation gap does not affect you, since Realtime is not yet instrumented.
Frequently asked questions
How do I install the Supabase C# SDK?
Add the meta package with dotnet add package Supabase, or reference an individual service package such as Supabase.Gotrue, Supabase.Postgrest, Supabase.Storage, Supabase.Realtime or Supabase.Functions on its own. Every package targets .NET Standard 2.1, so it runs on .NET Core 3.0+, .NET 5+, recent MAUI, Xamarin and Unity.
How do I create and use a Supabase client in C#?
Construct a new Client with the URL and key, optionally passing SupabaseOptions with AutoRefreshToken and AutoConnectRealtime, then await supabase.InitializeAsync(). After that you reach each service from the client: supabase.Auth, supabase.From<T>() with supabase.Rpc(...), supabase.Storage, supabase.Realtime and supabase.Functions.
What is the difference between the service key and the public key in the Supabase C# client?
Some APIs, such as user administration and bypassing RLS, require the service_key rather than the public or anon key. The readme warns never to expose a service_key in client-side code and to use a separate client instance for service and user contexts.
Does supabase-csharp support OpenTelemetry?
The clients emit traces and metrics through System.Diagnostics, so you can wire them into OpenTelemetry or any ActivityListener or MeterListener without taking a dependency on the OpenTelemetry packages. Auth, Postgrest, Functions and Storage are covered, while Realtime is not yet instrumented, and no token, credential or payload is placed in a tag.
How is the Supabase C# SDK built and tested?
It builds with the .NET SDK version pinned in global.json, using dotnet restore, dotnet build Supabase.sln --configuration Release and dotnet test Supabase.sln --configuration Release. Some test suites are end-to-end and need a local Supabase stack started with the Supabase CLI, which requires Docker.
How often are the Supabase C# SDK packages released?
Frequently, and each package is versioned independently through release-please with Conventional Commits. The recent releases are v8.3.0 on 2026-10-05, v8.3.1 on 2026-10-09 and v8.4.0 on 2026-10-16, and the last push was on 2026-10-16. The licence is MIT.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/supabase-supabase-csharp)Was this useful?