The Aero backend uses PostgreSQL as its database and Prisma as the ORM. The schema includes models for users, airports, airlines, aircraft, flights, and flight tracking data.
Database setup
Connection
Configure the PostgreSQL connection in your .env file:
DATABASE_URL = "postgresql://user:password@localhost:5432/aero?schema=public"
Migrations
Run migrations to set up the database schema:
Development
Production
Reset
# Create and apply migration
npx prisma migrate dev
Core models
User
The User model stores authentication and profile information:
model User {
id String @id @unique
name String
email String @unique
password String
createdAt DateTime @default ( now ()) @map ( "created_at" )
updatedAt DateTime @updatedAt @map ( "updated_at" )
flights Flight []
bookings FlightBooking []
@@map ( "users" )
}
User IDs are generated using CUID2 with the prefix user_. Passwords are hashed using Argon2.
Airport
The Airport model contains comprehensive airport data:
enum AirportType {
small_airport
seaplane_base
medium_airport
large_airport
heliport
closed
balloonport
}
model Airport {
id String @id @unique @default ( cuid ())
ident String
type AirportType
elevation Decimal ?
continent String
isoCountry String @map ( "iso_country" )
isoRegion String @map ( "iso_region" )
municipality String ?
gpsCode String ? @map ( "gps_code" )
iataCode String ? @map ( "iata_code" )
name String
lat Decimal
long Decimal
createdAt DateTime @default ( now ()) @map ( "created_at" )
updatedAt DateTime @updatedAt @map ( "updated_at" )
@@index ( [ ident ] )
@@index ( [ municipality ] )
@@index ( [ gpsCode ] )
@@index ( [ iataCode ] )
@@index ( [ name ] )
@@map ( "airports" )
}
Indexes are created on frequently searched fields for optimal query performance.
Airline
The Airline model stores airline information:
model Airline {
id String @id @unique @default ( cuid ())
name String
icao String
iata String
image String ?
createdAt DateTime @default ( now ()) @map ( "created_at" )
updatedAt DateTime @updatedAt @map ( "updated_at" )
@@index ( [ name ] )
@@index ( [ icao ] )
@@index ( [ iata ] )
@@map ( "airlines" )
}
Aircraft
The Aircraft model tracks aircraft details:
model Aircraft {
id String @id @unique @default ( cuid ())
aircraft_id String
reg String
hexIcao String ? @map ( "hex_icao" )
modelCode String ? @map ( "model_code" )
age Decimal ?
firstFlightDate DateTime ? @map ( "first_flight_date" )
deliveryDate DateTime ? @map ( "delivery_date" )
typeName String ? @map ( "type_name" )
isFreighter Boolean @map ( "is_freighter" )
image String ?
attribution String ?
registrations AircraftRegistration []
// Store the whole json payload from api
payload Json ?
createdAt DateTime @default ( now ()) @map ( "created_at" )
updatedAt DateTime @updatedAt @map ( "updated_at" )
@@map ( "aircrafts" )
}
Flight tracking
Flight
The main Flight model for user-tracked flights:
model Flight {
id String @id @unique @default ( cuid ())
greatCircleDistance GreatCircleDistance ?
flightAwareData FlightAwareData ?
flightNo String @map ( "flight_no" )
aircraft Json
airline Json
arrival Json
departure Json
cargo Boolean @default ( false )
date DateTime
callSign String @map ( "call_sign" )
createdAt DateTime @default ( now ()) @map ( "created_at" )
updatedAt DateTime @updatedAt @map ( "updated_at" )
FlightPositions FlightPositions []
allFlightPositionsFetched Boolean @default ( false ) @map ( "all_flight_positions_fetched" )
bookings FlightBooking []
user User @relation ( fields : [ userId ], references : [ id ] )
userId String
@@map ( "flight" )
}
FlightPositions
Tracks real-time and historical flight positions:
enum AltitudeChange {
Climb
Descend
None
}
enum UpdateType {
Projected
Oceanic
Radar
ADSB
Multilateration
Datalink
Surface_And_Near_Surface
Spaced_Based
Virtual_Event
}
model FlightPositions {
id String @id @unique @default ( cuid ())
altitude Int
altitudeChange AltitudeChange @default ( None ) @map ( "altitude_change" )
groundSpeed Int @map ( "ground_speed" )
heading Int ?
latitude Decimal
longitude Decimal
timestamp DateTime
updatedType UpdateType @map ( "update_type" )
createdAt DateTime @default ( now ()) @map ( "created_at" )
updatedAt DateTime @updatedAt @map ( "updated_at" )
flight Flight ? @relation ( fields : [ flightId ], references : [ id ] )
flightId String ? @map ( "flight_id" )
@@index ( [ timestamp ] )
@@index ( [ latitude , longitude ] )
@@index ( [ altitude ] )
@@index ( [ altitudeChange ] )
@@index ( [ groundSpeed ] )
@@index ( [ heading ] )
@@index ( [ updatedType ] )
@@map ( "flight_positions" )
}
FlightBooking
Stores booking details for flights:
enum SeatType {
window
middle
aisle
jumpseat
captain
pilot
copilot
flight_engineer
flight_attendant
observer
other
}
enum SeatingClass {
economy
premium_economy
business
first
private
other
}
enum BookingReason {
personal
business
crew
training
repositioning
other
}
model FlightBooking {
id String @id @unique @default ( cuid ())
/// Booking code/confirmation code (e.g., "ABC123")
bookingCode String ? @map ( "booking_code" )
/// Seat number (e.g., "12A", "1F")
seatNumber String ? @map ( "seat_number" )
/// Type of seat position
seatType SeatType ? @map ( "seat_type" )
/// Class of service
seatingClass SeatingClass ? @map ( "seating_class" )
/// Reason for the flight
reason BookingReason ?
/// Notes about the booking
notes String ?
flight Flight @relation ( fields : [ flightId ], references : [ id ], onDelete : Cascade )
flightId String @map ( "flight_id" )
user User @relation ( fields : [ userId ], references : [ id ] )
userId String @map ( "user_id" )
createdAt DateTime @default ( now ()) @map ( "created_at" )
updatedAt DateTime @updatedAt @map ( "updated_at" )
@@index ( [ flightId ] )
@@index ( [ userId ] )
@@index ( [ bookingCode ] )
@@map ( "flight_bookings" )
}
FlightAwareData
Detailed flight information from FlightAware API:
model FlightAwareData {
id String @id @unique @default ( cuid ())
/// FlightAware flight ID - unique identifier
faFlightId String @map ( "fa_flight_id" )
/// Flight identifier
ident String
/// ICAO operator code + flight number
identIcao String ? @map ( "ident_icao" )
/// IATA operator code + flight number
identIata String ? @map ( "ident_iata" )
/// Actual departure runway
actualRunwayOff String ? @map ( "actual_runway_off" )
/// Actual arrival runway
actualRunwayOn String ? @map ( "actual_runway_on" )
/// Aircraft registration (tail number)
registration String ?
/// Departure delay in seconds (negative = early)
departureDelay Int ? @map ( "departure_delay" )
/// Arrival delay in seconds (negative = early)
arrivalDelay Int ? @map ( "arrival_delay" )
/// Flight status description
status String
/// Planned flight distance in statute miles
routeDistance Int ? @map ( "route_distance" )
/// Flight route description
route String ?
/// Gate and terminal information
gateOrigin String ? @map ( "gate_origin" )
gateDestination String ? @map ( "gate_destination" )
terminalOrigin String ? @map ( "terminal_origin" )
terminalDestination String ? @map ( "terminal_destination" )
/// Timing information
scheduledOut DateTime ? @map ( "scheduled_out" )
actualOut DateTime ? @map ( "actual_out" )
scheduledOff DateTime ? @map ( "scheduled_off" )
actualOff DateTime ? @map ( "actual_off" )
scheduledOn DateTime ? @map ( "scheduled_on" )
actualOn DateTime ? @map ( "actual_on" )
scheduledIn DateTime ? @map ( "scheduled_in" )
actualIn DateTime ? @map ( "actual_in" )
/// Status flags
blocked Boolean @default ( false )
diverted Boolean @default ( false )
cancelled Boolean @default ( false )
/// Airport information
originCode String ? @map ( "origin_code" )
originName String ? @map ( "origin_name" )
destinationCode String ? @map ( "destination_code" )
destinationName String ? @map ( "destination_name" )
flight Flight @relation ( fields : [ flightId ], references : [ id ], onDelete : Cascade )
flightId String @unique @map ( "flight_id" )
createdAt DateTime @default ( now ()) @map ( "created_at" )
updatedAt DateTime @updatedAt @map ( "updated_at" )
@@index ( [ ident ] )
@@index ( [ faFlightId ] )
@@index ( [ registration ] )
@@map ( "flight_aware_data" )
}
See the full schema in prisma/schema.prisma for all fields and relationships.
GreatCircleDistance
Stores calculated flight distances in multiple units:
model GreatCircleDistance {
id String @id @unique @default ( cuid ())
meter Decimal
km Decimal
mile Decimal
nm Decimal
feet Decimal
flight Flight @relation ( fields : [ flightId ], references : [ id ], onDelete : Cascade )
flightId String @unique @map ( "flight_id" )
createdAt DateTime @default ( now ()) @map ( "created_at" )
updatedAt DateTime @updatedAt @map ( "updated_at" )
@@map ( "great_circle_distance" )
}
Prisma client usage
The Prisma client is exported from src/db/index.ts:
import { prisma } from 'src/db' ;
// Query users
const user = await prisma . user . findFirst ({
where: { email: '[email protected] ' },
});
// Create a flight
const flight = await prisma . flight . create ({
data: {
flightNo: 'AA100' ,
userId: user . id ,
date: new Date (),
callSign: 'AAL100' ,
aircraft: {},
airline: {},
arrival: {},
departure: {},
},
});
Database operations
Generate Prisma client
After modifying the schema, regenerate the client:
View database in Prisma Studio
Open the Prisma Studio GUI to view and edit data:
Create migrations
Create a new migration after schema changes:
npx prisma migrate dev --name description_of_changes
Always test migrations in development before applying to production.
The schema includes strategic indexes for optimal query performance:
Airport : Indexed on ident, municipality, gpsCode, iataCode, and name
Airline : Indexed on name, icao, and iata
FlightPositions : Indexed on timestamp, latitude/longitude (composite), altitude, groundSpeed, heading, and updatedType
FlightBooking : Indexed on flightId, userId, and bookingCode
FlightAwareData : Indexed on ident, faFlightId, and registration
These indexes ensure fast lookups for common queries like searching airports by IATA code or tracking flight positions by timestamp.