ItinGen: The Travel Planner That Turns Gemini Into a Structured API
A small React and Express app shows the real challenge of AI products: not generating text, but forcing it into a schema a frontend can trust.
- ItinGen’s main contribution is not a better travel idea engine, but a stronger contract around LLM output.
- The prompt acts like infrastructure, with strict JSON expectations and budget-aware constraints baked in.
- The backend is built to turn malformed model behavior into usable product responses instead of exposing chatty raw text.
- Its value is as a pattern for production-minded AI wrappers, not as a broad travel platform.
A Travel Planner That Refuses to Ramble
ItinGen starts with a simple promise: describe a trip, get back an itinerary. The interesting part is that the app does not treat Gemini like a conversational partner. It treats it like a service that must return a shape the frontend can render without guessing.
That changes the whole product. The goal is not a clever paragraph about Paris. The goal is valid, structured output with fields the UI can trust, even when the user’s request is vague, budget-sensitive, or underspecified.
A simple itinerary generator.
The Secret Is in the Prompt, Not the Model
The core move lives in backend/utils/promptBuilder.js. Instead of asking the model to be creative and hoping for the best, ItinGen injects constraints, fields, and output rules up front. The prompt reads less like a suggestion and more like a contract.
export function buildPrompt({ destination, budget, travelType, transportPreference, currency }) {
return `You are a professional travel planner.
Return ONLY valid JSON.
Do not wrap the response in markdown.
User trip:
- Destination: ${destination}
- Budget: ${budget} ${currency}
- Travel style: ${travelType}
- Transport preference: ${transportPreference}
Required JSON schema:
{
"tripSummary": "string",
"hotelOptions": [
{ "tier": "Budget|Mid-range|Luxury", "name": "string", "why": "string" }
],
"days": [
{
"day": 1,
"morning": "string",
"afternoon": "string",
"evening": "string",
"food": "string"
}
]
}`;
}
The prompt also does the budget steering. Rather than asking for a generic plan, it demands hotel tiers, day parts, and expense-aware suggestions. That is the difference between a chatbot response and a product output.
How the Request Becomes an Itinerary
The flow is straightforward on paper. TripForm.jsx collects the inputs, MainControllers.js checks the request, and GeminiService.js calls the model. The value is in what happens between those steps: validation, parsing, and the insistence on a shape that can be rendered immediately.
// controller flow, simplified
const { destination, budget, travelType, transportPreference, currency } = req.body;
if (!destination || !budget || !travelType) {
return res.status(400).json({ error: 'Missing required trip details.' });
}
try {
const prompt = buildPrompt(req.body);
const raw = await generateItinerary(prompt);
const itinerary = JSON.parse(raw);
return res.json(itinerary);
} catch (error) {
return res.status(error.statusCode || 500).json({ error: error.message });
}
That is a familiar backend pattern, but it matters more here because the upstream system is probabilistic. The app is not assuming the model will behave. It is building a narrow lane and putting guardrails on both sides.
Why Budget-Aware Output Matters
Most itinerary generators stop at suggestions. ItinGen goes further by asking for budget-aware hotel tiers and practical choices that match the trip profile. That makes the output usable for a real planning session, not just fun to skim.
| Approach | Input style | Output structure | Reliability | Budget sensitivity | Frontend friendliness |
|---|---|---|---|---|---|
| ItinGen | Form fields plus backend validation | Strict JSON with itinerary sections | High for a small app because the prompt is constrained | Built in through prompt variables | Very high because the UI gets predictable data |
| Generic AI itinerary generator | Open-ended chat prompt | Often prose first, structure second | Variable, depending on prompt discipline | Usually shallow or inconsistent | Mixed, because the frontend may need cleanup |
| Traditional travel platform | Manual search and booking workflow | User assembled, not model generated | High, but not automated | Strong through filters and pricing | High for booking, lower for fast itinerary synthesis |
The contrast is simple. Traditional platforms optimize for search and booking. Generic AI tools optimize for surprise. ItinGen optimizes for shape.
Graceful Failure Is Part of the Product
The backend does not hide from model failure. In GeminiService.js and the controller layer, quota errors and service overload are translated into human-readable messages instead of a dead end. That is a small detail with a big product effect.
try {
return await callGemini(prompt);
} catch (error) {
if (error.status === 429) {
throw new Error('This demo has hit its daily limit. Please try again later.');
}
if (error.status === 503) {
throw new Error('Gemini is experiencing high demand. Please retry shortly.');
}
throw new Error('Something went wrong while generating the itinerary.');
}
That kind of handling is what separates a demo from a product-minded prototype. Users can forgive limits. They do not forgive silence or broken UI states.
What ItinGen Is Really Compared With
ItinGen is not trying to out-platform TripIt or out-market a commercial travel assistant. It belongs to a different class of tools: small AI wrappers that prove a pattern. The comparison is useful because it shows where the project is deliberately narrow.
| Category | What it optimizes for | What ItinGen does differently |
|---|---|---|
| Commercial travel planner | Booking, organization, and broad travel utility | Focuses only on generation and output discipline |
| Generic AI trip generator | Open-ended conversation and novelty | Forces schema-first responses that a frontend can trust |
| DIY prompt-in-chat workflow | Speed of experimentation | Moves the prompt into a backend contract with validation and parsing |
That narrowness is the point. ItinGen is not a destination app. It is a lesson in how to keep an LLM from leaking complexity into the rest of the stack.
Why This Tiny Stack Is Worth Studying
The stack is small, but the lesson is durable. Most useful AI products are not won by bigger models. They are won by better contracts around the model, clearer boundaries, and enough backend discipline to make probabilistic output behave like software.
That is why ItinGen is worth a look. It shows the current AI wrapper pattern in a clean, legible form: React for input and display, Express for control, Gemini for generation, and prompt design as the real product surface.