Skip to main content

Inbloom API Documentation

Welcome to the Inbloom API docs. Inbloom is an event-based social/dating platform: users create events, invite friends as co-authors, and approve groups of interested participants. Dating happens through joining events together, not through 1:1 swiping.

Core concept​

Users create events → Friends invited as authors → Users form like groups →
Authors approve groups → Approved group gets private chat → Dating happens through events

Architecture​

The API is a thin Hono server on Bun that talks to a Supabase project for auth and storage. We deliberately reuse Supabase's production-grade building blocks (GoTrue, PostgREST, Realtime, Storage) instead of reimplementing them.

LayerTechnology
RuntimeBun
HTTP frameworkHono
ValidationZod
AuthSupabase Auth (GoTrue) — JWT verified server-side
DatabaseSupabase Postgres (accessed via Drizzle ORM)
RealtimeSupabase Realtime channels
StorageSupabase Storage private media bucket
Loggingpino
Lint/formatBiome

Why this stack​

  • Simplicity over micro-services. One Hono app, one Postgres, one Supabase project. No Redis, no BullMQ, no separate WebSocket server, no S3/Minio cluster.
  • Reuse battle-tested pieces. Auth, file storage, realtime fan-out and row-level security all come from Supabase. We don't write them.
  • Type safety end-to-end. Zod for request validation, Drizzle for query types, Supabase JS for auth response types.

Repository layout​

api/
├── src/
│ ├── main.ts # Bun.serve entrypoint
│ ├── core/ # config, logger, server, middleware
│ ├── feats/ # feature modules (e.g. profiles/)
│ ├── infra/ # db (drizzle) + supabase clients
│ ├── libs/ # shared utilities (errors, etc.)
│ └── ts/ # ambient TS declarations
└── drizzle/ # generated migrations

Each feature under src/feats/<name>/ is self-contained: <name>.router.ts, <name>.service.ts, <name>.schemas.ts, <name>.constants.ts.

Status​

The HTTP surface is shipped behind OpenAPI at /openapi.json and Swagger UI at /docs. This wiki describes the current implementation; planned items are marked explicitly.

ModuleStatus
Auth (via Supabase)✅ Shipped
Profile (/profiles)✅ Shipped
Profile educations✅ Shipped
Profile languages✅ Shipped
Profile photos, onboarding, usernames✅ Shipped
Reference data (lookups + languages)✅ Shipped
People discovery, interactions, matches✅ Shipped
Friends✅ Shipped
Events✅ Shipped
Like groups + approval✅ Shipped
Chat, direct chat, attachments✅ Shipped
Notifications✅ Shipped
Push devices and delivery✅ Shipped
Reports✅ Shipped
Moderation actions🚧 Not exposed as public API

User roles​

Roles are application-level, encoded as JWT claims (not Postgres roles).

RolePermissions
UserCreate events, like events, chat, send friend requests
ModeratorReserved application role; no moderation HTTP routes are exposed
AdminReserved application role; no admin HTTP routes are exposed

Authentication contract​

All non-auth endpoints require a Supabase-issued JWT:

Authorization: Bearer <supabase-access-token>

The API verifies the token via supabase.auth.getUser(token) and attaches the resolved user to the request context (see src/core/middleware/auth.middleware.ts). See the Auth doc for the full client flow.

Pagination​

Cursor-based pagination is the standard for list endpoints:

?cursor=xxxxx&limit=20
{
"data": [...],
"nextCursor": "encoded-cursor-or-null"
}

Realtime​

Real-time updates use Supabase Realtime channels rather than a custom WebSocket server. Clients subscribe via @supabase/supabase-js:

ChannelEvents
chat:{chatId}message.created, message.updated, message.deleted
group:{groupId}group.approved, group.updated
event:{eventId}event.updated

The two patterns we'll mix:

  • Postgres changes — automatic events from row INSERT/UPDATE/DELETE, filtered by RLS so users only get rows they can read.
  • Broadcast — server-emitted ephemeral events for things that aren't rows (typing indicators, presence-style ticks).

Rate limits​

Endpoint groupLimit
Auth (Supabase-side)Per Supabase plan
Event creationNot enforced by this API
General APINot enforced by this API

Support​

Public routes​

GET /health returns { "status": "ok" } without authentication. Mobile platform verification files are served at /.well-known/assetlinks.json and /.well-known/apple-app-site-association; public Bloomers deep links are served at /bloomers/:userId and /bloomers/og.png.