Skip to main content
The F1 PitLane Predict application uses a PostgreSQL database managed through Prisma ORM. The schema is defined in prisma/schema.prisma and consists of six main models that handle users, F1 data, and betting functionality.

Overview

The database schema includes:
  • Users - User accounts and authentication
  • Drivers - F1 driver information
  • Teams - F1 constructor/team data
  • Races - Race schedule and circuit information
  • Results - Race results and driver positions
  • Bets - User betting records and status

Models

Users

Stores user account information, authentication credentials, and balance data.
model Users {
  userId    Int       @id @default(autoincrement())
  username  String    @unique @db.VarChar(255)
  email     String    @unique @db.VarChar(255)
  password  String    @db.VarChar(255)
  balance   Decimal?  @default(0.00) @db.Decimal(10, 2)
  createdAt DateTime? @default(now()) @db.Timestamp(6)
  lastLogin DateTime? @db.Timestamptz(6)
  country   String?   @db.VarChar(50)
  userType  String?   @default("regular") @db.VarChar(10)
  Bets      Bets[]
}
FieldTypeDescription
userIdIntPrimary key, auto-incrementing user identifier
usernameStringUnique username, max 255 characters
emailStringUnique email address, max 255 characters
passwordStringHashed password, max 255 characters
balanceDecimalUser’s account balance for betting, defaults to 0.00
createdAtDateTimeAccount creation timestamp
lastLoginDateTimeLast login timestamp with timezone
countryStringUser’s country, max 50 characters
userTypeStringUser type (e.g., “regular”), defaults to “regular”
This table contains check constraints and requires additional setup for migrations. See the Prisma documentation for more information.
Relationships:
  • One-to-many with Bets (a user can have multiple bets)

Drivers

Contains comprehensive information about F1 drivers including personal details and team affiliation.
model Drivers {
  driverId        String    @id @db.VarChar(50)
  code            String?   @db.VarChar(10)
  permanentNumber Int?
  givenName       String?   @db.VarChar(100)
  familyName      String?   @db.VarChar(100)
  dateOfBirth     DateTime? @db.Date
  nationality     String?   @db.VarChar(50)
  url             String?   @db.VarChar(255)
  driverImage     String?   @db.VarChar(255)
  teamId          String?   @db.VarChar(50)
  Bets            Bets[]
  Teams           Teams?    @relation(fields: [teamId], references: [teamId], onDelete: NoAction, onUpdate: NoAction)
  Results         Results[]
}
FieldTypeDescription
driverIdStringPrimary key, unique driver identifier (max 50 chars)
codeStringDriver’s three-letter code (e.g., “VER”, “HAM”)
permanentNumberIntDriver’s permanent racing number
givenNameStringDriver’s first name
familyNameStringDriver’s last name
dateOfBirthDateTimeDriver’s date of birth
nationalityStringDriver’s nationality
urlStringReference URL for driver information
driverImageStringURL or path to driver’s image
teamIdStringForeign key to Teams table
Relationships:
  • Many-to-one with Teams (a driver belongs to one team)
  • One-to-many with Bets (a driver can have multiple bets placed on them)
  • One-to-many with Results (a driver can have multiple race results)

Teams

Stores F1 constructor/team information.
model Teams {
  teamId      String    @id @db.VarChar(50)
  name        String?   @db.VarChar(100)
  nationality String?   @db.VarChar(50)
  url         String?   @db.VarChar(255)
  teamLogo    String?   @db.VarChar(255)
  Drivers     Drivers[]
}
FieldTypeDescription
teamIdStringPrimary key, unique team identifier (max 50 chars)
nameStringTeam/constructor name (e.g., “Red Bull Racing”)
nationalityStringTeam’s nationality
urlStringReference URL for team information
teamLogoStringURL or path to team’s logo image
Relationships:
  • One-to-many with Drivers (a team can have multiple drivers)

Races

Contains race schedule, circuit information, and event details.
model Races {
  raceId      Int       @id @default(autoincrement())
  season      String?   @db.VarChar(4)
  round       Int?
  raceName    String?   @db.VarChar(255)
  circuitId   String?   @db.VarChar(255)
  circuitName String?   @db.VarChar(255)
  locality    String?   @db.VarChar(255)
  country     String?   @db.VarChar(255)
  date        DateTime? @db.Date
  time        DateTime? @db.Time(6)
  url         String?   @db.VarChar(255)
  Bets        Bets[]
  Results     Results[]
}
FieldTypeDescription
raceIdIntPrimary key, auto-incrementing race identifier
seasonStringSeason year (e.g., “2024”)
roundIntRound number in the season
raceNameStringOfficial race name
circuitIdStringUnique circuit identifier
circuitNameStringCircuit name
localityStringCity or locality of the circuit
countryStringCountry where the race takes place
dateDateTimeRace date
timeDateTimeRace start time
urlStringReference URL for race information
Relationships:
  • One-to-many with Bets (a race can have multiple bets)
  • One-to-many with Results (a race has multiple driver results)

Results

Stores race results including driver positions and points earned.
model Results {
  resultId Int      @id @default(autoincrement())
  raceId   Int?
  driverId String?  @db.VarChar(50)
  position Int
  points   Int
  Drivers  Drivers? @relation(fields: [driverId], references: [driverId], onDelete: NoAction, onUpdate: NoAction)
  Races    Races?   @relation(fields: [raceId], references: [raceId], onDelete: NoAction, onUpdate: NoAction)
}
FieldTypeDescription
resultIdIntPrimary key, auto-incrementing result identifier
raceIdIntForeign key to Races table
driverIdStringForeign key to Drivers table
positionIntFinishing position in the race
pointsIntChampionship points earned
Relationships:
  • Many-to-one with Races (a result belongs to one race)
  • Many-to-one with Drivers (a result belongs to one driver)

Bets

Tracks user betting activity including bet amounts, odds, and status.
model Bets {
  betId     Int       @id @default(autoincrement())
  userId    Int?
  raceId    Int?
  driverId  String?   @db.VarChar(50)
  amount    Decimal   @db.Decimal(10, 2)
  odds      Decimal   @db.Decimal(5, 2)
  betStatus String?   @default("pending") @db.VarChar(10)
  createdAt DateTime? @default(now()) @db.Timestamp(6)
  Drivers   Drivers?  @relation(fields: [driverId], references: [driverId], onDelete: NoAction, onUpdate: NoAction)
  Races     Races?    @relation(fields: [raceId], references: [raceId], onDelete: NoAction, onUpdate: NoAction)
  Users     Users?    @relation(fields: [userId], references: [userId], onDelete: NoAction, onUpdate: NoAction)
}
FieldTypeDescription
betIdIntPrimary key, auto-incrementing bet identifier
userIdIntForeign key to Users table
raceIdIntForeign key to Races table
driverIdStringForeign key to Drivers table
amountDecimalBet amount with 2 decimal places
oddsDecimalBetting odds with 2 decimal places
betStatusStringStatus of bet (“pending”, “won”, “lost”), defaults to “pending”
createdAtDateTimeTimestamp when bet was created
This table contains check constraints and requires additional setup for migrations. See the Prisma documentation for more information.
Relationships:
  • Many-to-one with Users (a bet belongs to one user)
  • Many-to-one with Races (a bet is for one race)
  • Many-to-one with Drivers (a bet is on one driver)

Database configuration

The database uses PostgreSQL as the provider and connects using the DATABASE_URL environment variable:
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

Entity relationship diagram

Users (1) ────── (*) Bets (*) ────── (1) Races


                      (*)


                   Drivers (1) ────── (*) Results (*) ────── (1) Races

                      (*)


                   (1) Teams

Key features

All foreign key relationships use onDelete: NoAction and onUpdate: NoAction to prevent cascading deletes and updates. This ensures data integrity and prevents accidental data loss.
Most tables (Users, Races, Results, Bets) use auto-incrementing integer primary keys. Drivers and Teams use string-based identifiers for compatibility with external F1 data sources.
Financial fields (balance, amount, odds) use appropriate decimal precision:
  • Balance and amounts: Decimal(10, 2) for currency values
  • Odds: Decimal(5, 2) for betting odds
The schema uses various timestamp types:
  • @default(now()) for automatic creation timestamps
  • Timestamp(6) for standard timestamps
  • Timestamptz(6) for timezone-aware timestamps (lastLogin)

Build docs developers (and LLMs) love