Skip to main content

Profile API

A profile is the dating-side view of a user: name, bio, demographics, preferences. Authentication identity (email, password, etc.) lives in Supabase Auth and is not part of the profile.

Status

EndpointStatus
POST /profiles✅ Shipped
GET /profiles/me🚧 Planned
PATCH /profiles/me🚧 Planned
Profile photos / media🚧 Planned (Supabase Storage)
Profile languages, education🚧 Planned

Source: src/feats/profiles/.

Create profile

Creates the dating profile for the currently authenticated Supabase user. The user's id (UUID) is taken from the verified JWT, so clients do not send it.

Request

POST /profiles
Authorization: Bearer <supabase-access-token>
Content-Type: application/json

{
"firstName": "Nurbek",
"bio": "I am very cool",
"birthdate": "2004-12-05",
"height": 193,
"hometown": "Almaty",

"datingPurpose": "friendship",
"showDatingPurpose": true,

"religion": "atheism",
"familyPlan": "no-maybe-in-future",
"financialState": "wealthy",
"bodyType": "fit",

"alcoholAttitude": "negative",
"smokingAttitude": "negative"
}

Fields

FieldTypeRequiredDescription
firstNamestringDisplay name
biostring | nullShort bio
birthdateISO dateCoerced via z.coerce.date()
heightnumbercm, range 55..272
hometownstringFree-text city for now
datingPurposeenumSee enums
showDatingPurposebooleanWhether to show the purpose publicly
religionenumSee enums
familyPlanenumSee enums
financialStateenumSee enums
bodyTypeenumSee enums
alcoholAttitudeenumpositive / neutral / negative
smokingAttitudeenumpositive / neutral / negative

The Zod schema is profiles.schemas.ts; the corresponding Drizzle table is profile.schema.ts.

Server-derived fields

These are computed by the API, not sent by the client:

FieldSource
userIdSupabase user from the JWT
zodiacDerived from birthdate (getZodiacSign(date))

Response

200 OK with the created profile. (The shipped handler currently returns the inserted row; richer shape coming as the module grows.)

Errors

Validation errors are returned by the global error handler in src/core/server.ts:

{
"error": "Validation failed",
"fields": [
{ "field": "height", "message": "Number must be greater than or equal to 55" }
]
}
StatusCause
400Validation failed (see body)
401Missing / invalid bearer token
500Internal error

Enumerations

These mirror the constants in profiles.constants.ts. Some are also seeded into reference tables for joins.

datingPurpose

dating, relationship, friendship, chatting

bodyType

slim, normal, fit, sportive, muscular, stocky, chubby

familyPlan

no-dont-want, no-not-soon, no-maybe-in-future, no-but-want-to, already-have

financialState

unemployed, studying, irregular-income, low-income, middle-income, wealthy

religion

catholicism, orthodoxy, islam, protestantism, buddhism, hinduism, atheism, agnosticism, judaism

attitude (alcoholAttitude, smokingAttitude)

positive, neutral, negative — backed by the Postgres enum attitude.

zodiac (server-derived)

aquarius, pisces, aries, taurus, gemini, cancer, leo, virgo, libra, scorpio, sagittarius, capricorn — backed by the Postgres enum zodiac.

educationLevel (planned)

basic_general, average_general, secondary_special, bachelor, specialty, master, postgraduate — backed by the Postgres enum education_level. Used by the planned profile_educations table.

Example with curl

ACCESS=$(bun run ./devtools/authenticate.ts)

curl -X POST http://localhost:8080/profiles \
-H "Authorization: Bearer $ACCESS" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Nurbek",
"bio": "I am very cool",
"birthdate": "2004-12-05",
"height": 193,
"hometown": "Almaty",
"datingPurpose": "friendship",
"showDatingPurpose": true,
"religion": "atheism",
"familyPlan": "no-maybe-in-future",
"financialState": "wealthy",
"bodyType": "fit",
"alcoholAttitude": "negative",
"smokingAttitude": "negative"
}'

A worked example is also kept in api/e2e/profile.http.

Roadmap

  • GET /profiles/me — fetch own profile.
  • PATCH /profiles/me — partial update (same field rules).
  • POST /profiles/me/photos — Supabase Storage-backed avatars.
  • profile_languages, profile_educations join tables.
  • City-as-FK (Geonames) instead of free-text hometown, with PostGIS-backed location for radius queries.