---
title: Database Design for AI-Native Travel Products
description: "AI agents create data conversationally, not through forms. Here is how we designed our schema for conversation-driven data and agent query patterns."
canonical: https://nowah.xyz/blog/database-design-ai-native-travel
lastModified: "2026-08-07T03:47:14.846Z"
---

# Database Design for AI-Native Travel Products

AI agents create data conversationally, not through forms. Here is how we designed our schema for conversation-driven data and agent query patterns.

Every database schema embeds assumptions about how data enters the system. Traditional web applications assume data arrives through forms: complete, validated, and submitted in one action. A user fills out all required fields, clicks submit, and a fully-formed record gets inserted into the database.

AI-native products don't work this way. Data arrives through conversation. It's partial, incremental, and out of order. A user mentions their destination on turn one, their dates on turn three, adds a traveler on turn seven, and changes the departure date on turn twelve. The trip record is built piece by piece over the course of a conversation that might span hours or days.

This fundamental difference in how data enters the system changes database design in ways that aren't obvious until you've hit the walls.

## Conversation-driven data creation

![Illustration for this section](https://pics.nowah.xyz/website-media/engineering-022-img-1.webp)

In a form-based application, a trip record is created when the user submits the booking form. All required fields are present and validated at creation time. The database schema can enforce NOT NULL constraints on everything that matters.

In our system, a trip record starts as a stub. The user said "I want to go to Barcelona." That's a destination but no dates, no travelers, no flights, no hotels. The record gets created with a destination and nothing else.

Over the next several conversation turns, the record fills in. Dates get added. Traveler count gets specified. Flights get attached after a search and selection. Hotels get added later. Documents get generated after booking.

This means our schema needs to handle partial states gracefully. Most fields that would be NOT NULL in a form-driven application are nullable in ours. Validation happens at the application layer, scoped to the current operation. You need dates to search for flights, but you don't need dates to create the initial trip record.

We use a state machine on the trip record to track how complete it is: planning, searching, booking, confirmed, traveling, completed. Each state has its own set of required fields. Transitioning from "searching" to "booking" requires dates and a selected flight. But transitioning from "planning" to "searching" only requires a destination.

Conversation-driven data creation requires flexible partial-state handling. That flexibility has to be in the schema, not bolted on after the fact.

## Why we chose a relational database

We use a relational database with an ORM for structured data. This was a deliberate choice over NoSQL alternatives, and the reasoning is relevant to the conversation about AI-native data design.

The argument for NoSQL in a conversational product is flexibility: documents can have arbitrary shape, partial data is natural, and schema changes are easy. The argument against it is that travel booking data is inherently relational. A trip has flights. Flights have passengers. Passengers have profiles. Bookings have payments. Payments have refunds. These relationships matter for data integrity and query efficiency.

When an AI agent asks "show me all the trips where this user had a flight delay," that's a join across trips, flights, and flight status data. When the agent needs to verify a booking, it traverses from the booking record to the payment record to the provider confirmation. These are relational queries.

NoSQL could handle this with denormalization, but denormalization creates consistency problems that are especially dangerous for financial data. If the payment amount is stored in both the payment record and the booking record, and they get out of sync, which one is correct? With a relational model and foreign keys, the data relationships are explicit and enforceable.

We also benefit from a relational database's JSON column support. For data that IS flexible and semi-structured (agent session state, conversation metadata, tool call results), we use JSON columns within our relational schema. This gives us the flexibility of document storage where we need it, without giving up relational integrity where it matters.

## Indexing for AI-driven query patterns

![Supporting diagram](https://pics.nowah.xyz/website-media/engineering-022-img-2.webp)

AI agents query differently than human-driven UIs. A UI page loads data for a specific view: "this user's trips" or "this booking's details." An agent queries based on conversational context: "the flight we discussed yesterday" or "hotels near the restaurant I recommended."

Traditional indexes optimize for page-driven access patterns: primary key lookups, filtered list queries, sorted result sets. These still matter for us, but we also need indexes that support the agent's reasoning patterns.

**Temporal indexes.** The agent frequently needs to find things by when they were discussed or created. "The flight you looked at yesterday" requires indexing conversations and search results by timestamp.

**Contextual indexes.** "The hotel near Sagrada Familia" requires spatial or text search capabilities. We use a relational database's full-text search and geographic extensions for location-based queries.

**Preference indexes.** The [agentic memory](/blog/agentic-memory-smarter-over-time) system needs fast access to user preferences by category and relevance. We index preferences by user, category, and last-updated timestamp.

AI-driven query patterns differ from form-driven query patterns. We build indexes based on actual agent query logs, not just UI requirements. After each product iteration, we analyze the agent's database access patterns and add or adjust indexes accordingly.

## Schema migrations for a fast-moving product

Our schema changes frequently. The AI agent's capabilities expand, which means new data types and relationships. A new tool might need a new table. A product improvement might restructure how we store search results. A performance optimization might require a new index.

Schema changes are frequent in fast-moving AI product development. We've developed practices to manage this without breaking production.

**Additive changes by default.** We prefer adding new columns and tables over modifying existing ones. New columns are nullable so existing records don't need migration. Old columns are deprecated and eventually removed, but only after all code paths have migrated.

**Zero-downtime migrations.** Our ORM supports migration files that are applied incrementally. Adding a column, creating an index, and inserting seed data happen in separate migration steps that can be rolled back independently.

**Migration testing.** Every migration runs against a copy of production data before deployment. This catches performance problems (a new index on a large table might lock it for minutes) and data issues (a NOT NULL constraint that doesn't account for legacy records with null values).

**Schema documentation as code.** The ORM schema file is the single source of truth for our data model. Any developer can read it and understand the full database structure. This matters for the AI agent's tool definitions, which reference schema fields and need to stay in sync.

## How legacy OTA databases constrain AI

[Traditional OTAs](/blog/ai-travel-booking-vs-traditional-otas) designed their databases around page-based user flows. There's a search results table, a booking table, a user profile table, and they're structured to serve specific pages: the search results page, the booking confirmation page, the user dashboard.

This works well for page-based products but constrains AI integration. The AI agent doesn't think in pages. It thinks in concepts: trips, preferences, options, decisions. These concepts span multiple tables and multiple pages in the traditional model.

Legacy OTAs trying to add AI features often struggle because their data model doesn't support the query patterns an agent needs. "What did this user search for last time?" requires joining [search history](/blog/end-of-travel-search-box-history), user sessions, and result data in ways the original schema wasn't designed for.

We started with the AI agent's needs and designed the schema to serve them. Conversations, agent sessions, tool results, and user preferences are first-class entities in our data model, not afterthoughts appended to a page-driven schema.

This is one of the advantages of building AI-native from the start. The data model was designed for how an AI agent works, not retrofitted to accommodate one.

---

Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. [Plan your next trip](https://app.nowah.xyz).
