diff --git a/apps/www/_blog/2026-10-26-getting-started-with-the-supabase-dotnet-sdk.mdx b/apps/www/_blog/2026-10-26-getting-started-with-the-supabase-dotnet-sdk.mdx deleted file mode 100644 index b7ad00c8363..00000000000 --- a/apps/www/_blog/2026-10-26-getting-started-with-the-supabase-dotnet-sdk.mdx +++ /dev/null @@ -1,253 +0,0 @@ ---- -title: 'Getting started with the Supabase .NET SDK' -description: 'How to set up and use the Supabase C# client in .NET, for ASP.NET Core, Console Apps, Desktop, Unity and Mobile.' -author: guillaume_faas -date: '2026-10-26' -categories: - - developers -tags: - - dotnet - - csharp - - sdk -imgSocial: '2026-10-26-getting-started-with-the-supabase-dotnet-sdk/og.png' -imgThumb: '2026-10-26-getting-started-with-the-supabase-dotnet-sdk/thumb.png' -toc_depth: 3 ---- - -The Supabase .NET SDK is a C# client for your Supabase project. -It gives you Auth, Database, Storage, Realtime, and Edge Functions behind one API and handles the communication with the Supabase Platform. - -Version `8.0.0` has been released, along with a new `Supabase.Extensions.DependencyInjection` package. -This post covers how to set the client up, and then walk through basic operations. - -## What changed in `v8.0.0` - -If you are coming from a previous major version, a few things moved. -You do not need all the details to follow this tutorial, but they are worth knowing: - -- Serialization moved from Newtonsoft.Json to `System.Text.Json`. Custom converters and `[JsonProperty]` attributes need their `System.Text.Json` equivalents. -- Every service client now accepts an injectable `HttpClient` and supports opt-in retry with backoff. -- There is a new dependency injection package for ASP.NET Core, covered below. -- Packages target .NET Standard 2.1. That drops .NET Framework, which tops out at .NET Standard 2.0, so you need .NET Core 3.0+ or .NET 5+. - -If you want to see the full list of changes, check the [release notes](https://github.com/supabase/supabase-csharp/releases/tag/v8.0.0). -The [migration guide](https://github.com/supabase/supabase-csharp/blob/master/docs/migrations/v8.0.0.md) lists every breaking change you may encounter and how to handle them. - -## Install the package - -Add the meta-package to your project. -It brings in all five service packages (Auth, Postgrest, Storage, Realtime, and Functions): - -```sh -dotnet add package Supabase -``` - -If you only need one service, reference it on its own instead, for example `Supabase.Gotrue` for Auth or `Supabase.Postgrest` for the database. - -## Define a model - -The SDK maps a table to a C# class. -A model derives from `BaseModel` and uses attributes to link its properties to columns. - -Here is a `todos` table that we will use in upcoming examples, with `id`, `task`, and `is_complete`: - -```c# -using Supabase.Postgrest.Attributes; -using Supabase.Postgrest.Models; - -[Table("todos")] -public class Todo : BaseModel -{ - [PrimaryKey("id")] - public int Id { get; set; } - - [Column("task")] - public string Task { get; set; } - - [Column("is_complete")] - public bool IsComplete { get; set; } -} -``` - -## Set up the client - -### ASP.NET Core - -Install the dependency injection package: - -```sh -dotnet add package Supabase.Extensions.DependencyInjection -``` - -Register the client in `Program.cs`: - -```c# -using Supabase.Core.Http; -using Supabase.Extensions.DependencyInjection; - -var builder = WebApplication.CreateBuilder(args); -builder.Services.AddSupabase( - supabaseUrl: builder.Configuration["Supabase:Url"]!, - supabaseKey: builder.Configuration["Supabase:Key"]!, - configureOptions: options => - { - options.AutoConnectRealtime = true; - options.PostgrestRetry = new RetryOptions { MaxRetries = 3 }; - }); - -var app = builder.Build(); -``` - -`AddSupabase` registers the `Supabase.Client` as a scoped service, so you can inject it wherever you need: a minimal API endpoint, a controller, or your own service. - -```c# -using Supabase; - -public class TodoService(Client supabase) -{ - public async Task> GetOpen() - { - var result = await supabase - .From() - .Where(t => t.IsComplete == false) - .Get(); - return result.Models; - } -} -``` - -It also registers each sub-client on its own (`IPostgrestClient`, `IGotrueClient`, `IStorageClient`, `IFunctionsClient`, `IRealtimeClient`), so a class that touches one service can depend on that alone. - -We built this extension to make it easy for you to setup the client, rather than having to wire it up yourself. -A `Supabase.Client` builds its own `HttpClient` when you construct it. If you register as scoped or transient, every resolution creates a fresh `HttpClient` (and a fresh socket), which will exhausts the connection pool under load. -`AddSupabase` wires the client through `IHttpClientFactory` so the handler is pooled and reused. -To keep this consistent, it replaces any `HttpClient` you pass through `configureOptions`; your proxy, retry, Realtime, and other options still apply. - -### Other apps - -Outside ASP.NET Core, in Console apps, Desktop, Mobile, or Unity, construct the client yourself and initialize it once: - -```c# -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(); -``` - -`InitializeAsync` restores a saved session and, with `AutoConnectRealtime` on, opens the Realtime connection. -The default session handler does not store anything, so to restore a sign-in after a restart, set `SupabaseOptions.SessionHandler` to your own implementation that saves and loads the session. -Create the client once and reuse it in the app. -A new client per operation means a new `HttpClient` each time, which is the same problem the DI package solves on the server. - -## Run the common operations - -The examples below use a `supabase` variable, which is a `Supabase.Client` instance. - -### Query rows - -`From()` opens a query on the table mapped by `T`. -You can then filter using `Where` and then call `Get` to execute the query. - -`Get` returns a `ModeledResponse`, and you can find the rows in `Models`: - -```c# -ModeledResponse result = await supabase - .From() - .Where(t => t.IsComplete == false) - .Get(); -List todos = result.Models; -``` - -### Insert - -`Insert` writes a single model or a list: - -```c# -await supabase.From().Insert(new Todo { Task = "Write the launch notes", IsComplete = false }); -``` - -### Update and delete - -There are two ways to update an entity, and both are valid. -It simply depends on whether you already have the row in hand. - -If you already retrieved a model, change its properties and call `Update` on it. -A row returned from `Get` carries its own identity, so it knows which record to write: - -```c# -var result = await supabase.From().Where(t => t.Id == 1).Get(); -var todo = result.Models.First(); - -todo.IsComplete = true; -await todo.Update(); - -await todo.Delete(); -``` - -If you don't have a model, you can use the query builder which saves you a `Get` query. -Set the new values, filter the rows, and run it: - -```c# -await supabase - .From() - .Set(t => t.IsComplete, true) - .Where(t => t.IsComplete == false) - .Update(); - -await supabase - .From() - .Where(t => t.IsComplete == true) - .Delete(); -``` - -> While this looks very familiar to how Entity Framework is built, it does not behave the same. -> There is no `DbContext`, no change tracker, and no `SaveChanges`. -> Changing a property on a model does nothing until you call `Update`, and each call is its own HTTP request. - -### Authenticate a user - -Authentication lives on `supabase.Auth`. -Sign a user up and in with email and password: - -```c# -await supabase.Auth.SignUp("maria.garcia@example.com", "a-secure-password"); - -var session = await supabase.Auth.SignIn("maria.garcia@example.com", "a-secure-password"); -``` - -With `AutoRefreshToken` enabled, the client refreshes the access token before it expires and attaches it to your database and Storage calls, so Row-Level-Security applies to the signed-in user. -The same `supabase.Auth` surface covers OAuth, magic links, phone, and SSO. - -### Upload a file - -```c# -await supabase.Storage - .From("avatars") - .Upload("./local/photo.png", "maria/photo.png"); -``` - -## A note on keys - -Version `8.0.0` added the new publishable and secret API keys, alongside the existing anon and service keys. - -Use a publishable or anon key in anything that ships to users. -Keep secret and service keys on the server. -A service key bypasses Row Level Security, so it does not belong in a client app. -When you need that level of access, use a separate client for it. - -## Next steps - -You now have a client set up and can run queries, auth, and Storage from .NET. From here: - -- Browse the [C# reference](https://supabase.com/docs/reference/csharp/introduction) for the full API. -- Read the [DependencyInjection package README](https://github.com/supabase/supabase-csharp/tree/master/packages/extensions/DependencyInjection) for the ASP.NET Core details. -- Check the [SDK repository](https://github.com/supabase/supabase-csharp) for examples and the changelog. diff --git a/apps/www/public/images/blog/2026-10-26-getting-started-with-the-supabase-dotnet-sdk/og.png b/apps/www/public/images/blog/2026-10-26-getting-started-with-the-supabase-dotnet-sdk/og.png deleted file mode 100644 index fecdc9bbddd..00000000000 Binary files a/apps/www/public/images/blog/2026-10-26-getting-started-with-the-supabase-dotnet-sdk/og.png and /dev/null differ diff --git a/apps/www/public/images/blog/2026-10-26-getting-started-with-the-supabase-dotnet-sdk/thumb.png b/apps/www/public/images/blog/2026-10-26-getting-started-with-the-supabase-dotnet-sdk/thumb.png deleted file mode 100644 index ab19cc108a0..00000000000 Binary files a/apps/www/public/images/blog/2026-10-26-getting-started-with-the-supabase-dotnet-sdk/thumb.png and /dev/null differ