Supabase API - HelgaZhizhka/yes-code-merch GitHub Wiki

Supabase Documentation

[Supabase Docs](https://supabase.com/dashboard/project/tlnboeuoaezawexbrblg/api?page=users-management)

Supabase automatically documents all the data added to it. You can explore all tables, fields, and also see example queries.

All API interactions are handled in the shared layer.


1. Domain Layer – API Functions and Interfaces

shared/api/supabase-client.ts Initializes the Supabase client and sets up basic configuration.

shared/api/database.types.ts Contains types auto-generated based on the Supabase schema.

shared/api/auth Contains interfaces related to authentication and user data:

  • LoginDTO – Login data
  • SignUpDTO – Basic signup data
  • RegisterDTO – Extended registration data

Provides functions for handling auth:

  • login – Sign in via Supabase Auth
  • logout – Sign out
  • signUp – Create an account
  • createViewer – Create an extended user profile via an RPC (Remote Procedure Call). This allows the client to ask the server to run a predefined function like complete_registration, passing arguments as if calling a regular procedure.
  • register – Combines signup + extended profile creation

Utility functions:

  • mapViewerDataToRpcArgs – Converts frontend user data into the format required for the RPC call

2. Viewer Segment

shared/viewer/model.ts – Stores current session data and provides selectors to check auth status

shared/session/hooks.ts – Hooks for session management. Subscribes to auth state on app init, cleans up on unmount, and provides hooks like useViewerState


3. Page-specific Components

pages/login/hooks.ts Hook for login form:

  • useLoginForm – Integrates React Hook Form with login mutation

pages/registration/hooks.ts Hook for registration form:

  • useRegistrationForm – Manages form data and triggers the API call

Data Flow Overview

Session Initialization: On app load, the auth state is checked and stored in a global Zustand store.

User Login:

  • Login form collects data
  • useLogin calls the login function
  • The result is saved into the session store

User Registration:

  • Registration form collects profile info
  • useRegister calls register
  • register performs two sequential actions: signUp (create account) + createViewer (save extended profile via RPC)

User Logout:

  • useLogout calls the logout function
  • Clears session store and resets query cache

User Registration Flow

Registration happens in two steps:

  1. signUp via Supabase Auth
  2. createViewer via Supabase RPC function

What complete_registration RPC does:

  1. Inserts or updates the profile in the customers table.

  2. Creates one or two addresses in the addresses table:

    • always creates a shipping address
    • adds a billing address if useShippingAsBilling = false

Example RegisterDTO sent from the form:

{
  email: '[email protected]',
  password: 'password',
  firstName: 'Olga',
  lastName: 'Zh',
  phone: '123456789',
  title: 'Ms.',
  dateOfBirth: '1977-09-19',
  company: 'Yes Code',
  shippingAddresses: [
    {
      country: 'ES',
      city: 'Madrid',
      streetName: 'Calle Nueva',
      streetNumber: '12',
      postalCode: '100000',
      isDefault: true
    }
  ],
  useShippingAsBilling: true
}

Form Step Breakdown:

  1. Email + Password
  2. First name, last name, phone, (optional: title, DOB, company)
  3. Shipping address
  4. useShippingAsBilling – default is true. If false, user provides billing address

All this is submitted once when clicking Submit.


RPC Details

  1. Inserts/updates entry in public.customers

    • first_name, last_name, title, phone, email
    • date_of_birth, company
    • user_id from auth.uid()
  2. Inserts entries into public.addresses

Shipping address:

  • Always created
  • is_shipping_address = true
  • is_default_shipping set from input
  • If useShippingAsBilling = true, it also acts as billing

Billing address:

  • Only created if useShippingAsBilling = false
  • is_billing_address = true
  • is_default_billing set from input

Client-side uses mapDataRegistrationFormToRpcArgs(viewer) to format form data into snake_case arguments for the RPC call.


API Architecture Pattern

DTO → Mapper → Model

[ DTO ]
     ↓
[ Mapper (model/mappers.ts) ]
     ↓
[ Model (model/types.ts) ]

Example DTO:

{
  "product_id": 123,
  "product_name": "Cool T-Shirt",
  "product_price": 29.99,
  "created_at": "2024-04-22T10:00:00Z"
}

Mapped Model:

interface Product {
  id: number;
  title: string;
  price: number;
  createdAt: Date;
}

Mapper Function:

export const mapDtoToProduct = (dto: ProductDto): Product => ({
  id: dto.product_id,
  title: dto.product_name,
  price: dto.product_price,
  createdAt: new Date(dto.created_at),
});

countries Table

Stores a list of supported countries. The app currently uses a single region hardcoded in shared/config: REGION = 'EU'.

Each country includes:

  • iso_code – two-letter code (e.g. 'ES')
  • name – full country name (e.g. 'Spain')
  • region – default is 'EU'

How to fetch countries from the client:

const { data: countries } = await supabase
  .from('countries')
  .select('iso_code, name')
  .eq('region', EU_REGION);

Postal Code Validation

Use RegExp per country for postal code validation. See: https://en.wikipedia.org/wiki/List_of_postal_codes

You can also explore libraries or npm packages that offer postal code validation per country.