Mes Recettes: Building a Modern Recipe Index with Blazor WebAssembly & Supabase

Abstract colorful shapes for Mes Recettes (Blazor + Supabase)

Mes Recettes: Building a Modern Recipe Index with Blazor WebAssembly & Supabase

Part 1 of 3 in the Mes Recettes series: Part 2 – Architecture · Part 3 – Testing & Delivery | 🇫🇷 Version

Why This Project Exists

I wanted a fast, portable way to index my cookbook recipes—linking authors, books, pages, ratings, and notes—with minimal backend overhead. Instead of standing up an API + database manually, I paired Blazor WebAssembly (C# in the browser) with Supabase (managed PostgreSQL, Auth, Realtime, REST).

Goals:

  • Single-page UX, instant navigation
  • Strongly-typed models end to end
  • Auth without reinventing flows
  • A foundation to later add search, offline support, and real-time collaboration

Technology Choices (and Why)

Concern Choice Reason
UI & SPA runtime Blazor WebAssembly (.NET 9) Full-stack C#, component model, ecosystem maturity
UI components MudBlazor Material look, dialogs, tables, theming
Backend Supabase PostgreSQL + Auth + Realtime + REST + row security
Data modeling PostgREST + C# attributes Direct mapping, fewer DTO layers initially
Validation DataAnnotations Simple, built-in, testable
Testing xUnit Standard .NET, parallel, attribute-driven
Deployment (target) Static hosting/CDN (e.g. Azure SWA / GitHub Pages + API) Cheap + global distribution

High-Level Architecture

+ B M ( l W u U a A d s z S B e o M l r r z r S u p a b a s e C l i e n t R A P e u o a t s l h t t g i r m e e S Q L

The browser downloads the Blazor app once; subsequent data calls go straight to Supabase’s REST endpoints (via the official client SDK).

Core Code Excerpt

Program startup wires Supabase cleanly:

var options = new SupabaseOptions
{
    AutoRefreshToken = true,
    AutoConnectRealtime = true
};

builder.Services.AddSingleton(sp =>
    new Client(
        builder.Configuration["supabase:Url"] ?? string.Empty,
        builder.Configuration["supabase:Key"] ?? string.Empty,
        options
    )
);

builder.Services.AddScoped<ISupabaseAuthWrapper, SupabaseAuthWrapper>();
builder.Services.AddScoped<AuthService>();

Auth wrapper keeps client concerns isolated:

public class SupabaseAuthWrapper : ISupabaseAuthWrapper
{
    private readonly Supabase.Client _client;

    public async Task<Session?> SignIn(string email, string password) =>
        await _client.Auth.SignIn(email, password);

    public async Task SignOut() => await _client.Auth.SignOut();

    public User? CurrentUser => _client.Auth.CurrentUser;
}

Models map directly to tables via attributes:

[Table("recettes")]
public class Recipe : BaseModel
{
    [PrimaryKey("id")] public int Id { get; set; }
    [Column("name"), Required] public string Name { get; set; } = string.Empty;
    [Column("rating"), Range(1,5)] public int Rating { get; set; }
    [Column("notes")] public string? Notes { get; set; }
    [Column("book_id")] public int? BookId { get; set; }
    [Reference(typeof(Book), joinType: ReferenceAttribute.JoinType.Left, true)]
    public Book? Book { get; set; }
}

What’s Implemented vs. Planned

Area Status
Auth (email/password) Implemented
Models (Recipe / Book / Author / Junction) Implemented
Basic CRUD UI Implemented (dialogs & pages)
Rating validation (1–5) Implemented + unit tests
Repository / Unit of Work abstraction Documented pattern (not yet wired)
Caching layer Planned (pattern drafted)
Realtime updates Enabled in config; targeted enhancement
Full-text search & indexes Planned (SQL examples drafted)
CI/CD pipeline Documented; workflow template pending

Lessons So Far

  1. Supabase + Blazor cuts time-to-first-feature dramatically.
  2. Keeping models close to storage early is fine; abstractions can grow later.
  3. DataAnnotations remain “good enough” for first-phase domain constraints.
  4. MudBlazor accelerates UI but theming decisions should be centralized early.

Next Enhancements

  • Introduce a RecipeService + caching decorator
  • RLS policies enforcing per-user ownership (SQL authoring phase)
  • Add integration tests hitting a staged Supabase instance
  • Real-time listener to reflect concurrent edits

Source

GitHub: https://github.com/mongeon/RecettesIndex



See also