25 author TEXT,
26 price REAL,
27 cover_image_url TEXT
28 )`,
29 `CREATE TABLE IF NOT EXISTS points_of_interest (
50 await sqlite.batch([
51 {
52 sql: `INSERT INTO tours (id, title, description, author, price, cover_image_url) VALUES (?, ?, ?, ?, ?, ?)`,
53 args: [
54 "tour_nyc_1",
57 "Geo-Narrate AI",
58 12.99,
59 "https://images.unsplash.com/photo-1534430480872-3498386e7856?q=80&w=2070&auto=format&fit=crop",
60 ],
61 },
132 author: row[3],
133 price: row[4],
134 cover_image_url: row[5],
135 points_of_interest: [],
136 };
407 card.className = 'tour-card';
408 card.innerHTML = \`
409 <img src="\${tour.cover_image_url}" alt="\${tour.title}">
410 <h3>\${tour.title}</h3>
411 <p>\${tour.description}</p>
429 <button onclick="window.location.reload()">Back to list</button>
430 <h2>\${tour.title}</h2>
431 <img src="\${tour.cover_image_url}" alt="\${tour.title}">
432 <p>\${tour.description}</p>
433 <button id="purchase-btn" \${isPurchased ? 'disabled' : ''} onclick="purchaseTour('\${tour.id}')">
28 margin: 1rem 0;
29 }
30 .bird-image {
31 max-width: 300px;
32 max-height: 200px;
357 </div>
358
359 ${bird.wikipedia?.image ? `
360 <div class="flex-shrink-0">
361 <img src="${bird.wikipedia.image}" alt="${bird.name}" class="bird-image">
362 </div>
363 ` : ''}
7- **Regional Bird Selection**: Uses eBird API to get locally relevant birds
8- **Fun Facts**: API Ninjas for engaging bird facts
9- **Rich Descriptions**: Wikipedia API for detailed information and images
10- **Smart Caching**: Reduces API calls and improves response times
11- **Graceful Fallbacks**: Multiple data sources ensure reliable responses
83 "source": "wikipedia",
84 "region": "US-CA",
85 "imageUrl": "https://upload.wikimedia.org/...",
86 "wikipediaUrl": "https://en.wikipedia.org/wiki/American_robin",
87 "collectedAt": "2025-06-29T06:00:00.000Z",
137 "filteredAt": "2025-06-29T06:00:00.000Z",
138 "region": "Arctic",
139 "imageUrl": "https://upload.wikimedia.org/...",
140 "wikipediaUrl": "https://en.wikipedia.org/wiki/Arctic_tern"
141 }
2291. **eBird API** - Regional bird observations
2302. **API Ninjas** - Fun facts and characteristics
2313. **Wikipedia** - Rich descriptions and images
232
233### Smart Caching Strategy
329| `fact` | string | Interesting fact about the bird |
330| `wikipedia.summary` | string | Wikipedia summary |
331| `wikipedia.image` | string | Image URL |
332| `wikipedia.url` | string | Wikipedia page URL |
333| `region` | string | Region or coordinates used |
11 source: "api_ninjas" | "wikipedia" | "curated";
12 region?: string;
13 imageUrl?: string;
14 wikipediaUrl?: string;
15 collectedAt: string;
243 source: this.determineSource(birdFact),
244 region: region || birdFact.region,
245 imageUrl: birdFact.wikipedia?.image,
246 wikipediaUrl: birdFact.wikipedia?.url,
247 collectedAt: new Date().toISOString(),
23 filteredAt: string;
24 region?: string;
25 imageUrl?: string;
26 wikipediaUrl?: string;
27}
84 filteredAt: new Date().toISOString(),
85 region: fact.region,
86 imageUrl: fact.imageUrl,
87 wikipediaUrl: fact.wikipediaUrl
88 };
140 filteredAt: new Date().toISOString(),
141 region: fact.region,
142 imageUrl: fact.imageUrl,
143 wikipediaUrl: fact.wikipediaUrl
144 } as SuperlativeFact;
1export interface WikipediaResult {
2 summary: string;
3 image?: string;
4 url: string;
5 scientificName?: string;
37 };
38
39 // Add image if available
40 if (data.thumbnail?.source) {
41 result.image = data.thumbnail.source;
42 } else if (data.originalimage?.source) {
43 result.image = data.originalimage.source;
44 }
45
113 try {
114 const pageTitle = this.formatPageTitle(birdName);
115 const url = `https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extracts|pageimages&exintro=true&explaintext=true&piprop=original&titles=${encodeURIComponent(pageTitle)}&origin=*`;
116
117 const response = await fetch(url);
129 title: page.title,
130 extract: page.extract,
131 image: page.original?.source,
132 url: `https://en.wikipedia.org/wiki/${encodeURIComponent(page.title)}`
133 };
16 wikipedia?: {
17 summary: string;
18 image?: string;
19 url: string;
20 };
6 <title>React Hono Val Town Starter</title>
7 <link rel="stylesheet" href="/frontend/style.css">
8 <link rel="icon" href="/frontend/favicon.svg" type="image/svg+xml">
9 </head>
10 <body>
3It's common to have code and types that are needed on both the frontend and the backend. It's important that you write this code in a particularly defensive way because it's limited by what both environments support:
4
5
6
7For example, you *cannot* use the `Deno` keyword. For imports, you can't use `npm:` specifiers, so we reccomend `https://esm.sh` because it works on the server & client. You *can* use TypeScript because that is transpiled in `/backend/index.ts` for the frontend. Most code that works on the frontend tends to work in Deno, because Deno is designed to support "web-standards", but there are definitely edge cases to look out for.
21## `favicon.svg`
22
23As of this writing Val Town only supports text files, which is why the favicon is an SVG and not an .ico or any other binary image format. If you need binary file storage, check out [Blob Storage](https://docs.val.town/std/blob/).
24
25## `components/`