chatExampleREADME.md5 matches
8## Hono
910This app uses [Hono](https://hono.dev/) as the API framework. You can think of Hono as a replacement for [ExpressJS](https://expressjs.com/) that works in serverless environments like Val Town or Cloudflare Workers. If you come from Python or Ruby, Hono is also a lot like [Flask](https://github.com/pallets/flask) or [Sinatra](https://github.com/sinatra/sinatra), respectively.
1112## Serving assets to the frontend
20### `index.html`
2122The most complicated part of this backend API is serving index.html. In this app (like most apps) we serve it at the root, ie `GET /`.
2324We *bootstrap* `index.html` with some initial data from the server, so that it gets dynamically injected JSON data without having to make another round-trip request to the server to get that data on the frontend. This is a common pattern for client-side rendered apps.
2526## CRUD API Routes
2728This app has two CRUD API routes: for reading and inserting into the messages table. They both speak JSON, which is standard. They import their functions from `/backend/database/queries.ts`. These routes are called from the React app to refresh and update data.
2930## Errors
3132Hono and other API frameworks have a habit of swallowing up Errors. We turn off this default behavior by re-throwing errors, because we think most of the time you'll want to see the full stack trace instead of merely "Internal Server Error". You can customize how you want errors to appear.
chatExamplequeries.ts2 matches
7queryKey: ["messages"],
8queryFn: async () => {
9const response = await fetch("/api/messages");
10if (!response.ok) {
11throw new Error("Failed to fetch messages");
25return useMutation({
26mutationFn: async (content: string) => {
27const response = await fetch("/api/messages", {
28method: "POST",
29headers: { "Content-Type": "application/json" },
chatExampleindex.ts3 matches
34app.get("/shared/**/*", c => serveFile(c.req.path, import.meta.url));
3536// API endpoints
37app.get("/api/messages", async (c) => {
38const messages = await getMessages();
39return c.json(messages);
40});
4142app.post("/api/messages", async (c) => {
43const { content } = await c.req.json();
44
ValTownValsdk.ts3 matches
19return true;
20} catch (error) {
21if (error instanceof ValTown.APIError && error.status === 404) {
22return false;
23}
61return targetVal.id;
62} catch (error) {
63if (error instanceof ValTown.APIError) {
64throw new Error(`Failed to resolve Val identifier: ${identifier}`);
65}
217});
218219export const sdk = new ValTown({ bearerToken: "VAL_TOWN_API_KEY_1" });
Loudaily_lineup_scheduler.tsx23 matches
22}
2324export interface YahooAPIConfig {
25access_token: string;
26refresh_token: string;
99}
100101// Initialize Yahoo Fantasy API client
102const yahooAPI = new YahooFantasyAPIClient(tokenData, this.tokenStorage, userId);
103104// Get user's leagues
105const leagues = await yahooAPI.getUserLeagues(userId);
106console.log(`🏟️ Found ${leagues.length} leagues for user ${userId}`);
107111112// Get user's team in this league
113const teamKey = await yahooAPI.getTeamKey(userId, league.league_id);
114if (!teamKey) {
115throw new Error(`Could not find team key for league ${league.league_id}`);
117118// Schedule pitchers for today
119const scheduleResult = await this.schedulePitchersForTeam(yahooAPI, teamKey, date);
120121results.leagues_processed.push({
139}
140141private async schedulePitchersForTeam(yahooAPI: YahooFantasyAPIClient, teamKey: string, date: Date) {
142// Get today's probable pitchers from MLB API
143const probablePitchers = await this.getTodaysProbablePitchers(date);
144console.log(`🎯 Found ${probablePitchers.length} probable pitchers for ${date.toDateString()}`);
145146// Get current team roster
147const roster = await yahooAPI.getTeamRoster(teamKey);
148console.log(`👥 Team roster has ${roster.length} players`);
149166for (const change of optimization.changes) {
167try {
168await yahooAPI.setPlayerPosition(teamKey, change.playerId, change.newPosition);
169results.pitchers_scheduled.push(change.playerId);
170results.changes_made.push(change);
186): Promise<Array<{ name: string; team: string; game_time?: string }>> {
187try {
188// Call MLB Stats API for probable pitchers
189const dateStr = date.toISOString().split("T")[0];
190const response = await fetch(
191`https://statsapi.mlb.com/api/v1/schedule?sportId=1&date=${dateStr}&hydrate=probablePitcher`,
192);
193194if (!response.ok) {
195throw new Error(`MLB API error: ${response.status}`);
196}
197399}
400401// Simplified Yahoo Fantasy API client for Val.town
402class YahooFantasyAPIClient {
403private config: YahooAPIConfig;
404private baseUrl = "https://fantasysports.yahooapis.com/fantasy/v2";
405private tokenStorage: LouTokenStorage;
406private userId: string;
407408constructor(config: YahooAPIConfig, tokenStorage: LouTokenStorage, userId: string) {
409this.config = config;
410this.tokenStorage = tokenStorage;
424private async refreshAccessToken(): Promise<void> {
425try {
426const response = await fetch("https://api.login.yahoo.com/oauth2/get_token", {
427method: "POST",
428headers: {
510511if (!retryResponse.ok) {
512throw new Error(`Yahoo API error after refresh: ${retryResponse.status} ${retryResponse.statusText}`);
513}
514517518if (!response.ok) {
519throw new Error(`Yahoo API error: ${response.status} ${response.statusText}`);
520}
521700701async setPlayerPosition(teamKey: string, playerId: string, position: string): Promise<void> {
702// Yahoo Fantasy API requires PUT/POST for roster changes
703const response = await fetch(`${this.baseUrl}/teams;team_keys=${teamKey}/roster/players;player_keys=${playerId}`, {
704method: "PUT",
745746// Store results in Val.town's blob storage for history
747await fetch("https://api.val.town/v1/blob/scheduler_results", {
748method: "POST",
749headers: {
175await fetchWeatherData(locationData.lat, locationData.lon);
176} else {
177// Get location from API
178const locationResponse = await fetch("/api/location");
179const locationData = await locationResponse.json();
180setLocation(locationData);
206}
207208const response = await fetch(`/api/weather?lat=${lat}&lon=${lon}`);
209const data = await response.json();
210257// Normalize the query for better search results
258const normalizedQuery = query.trim();
259const response = await fetch(`/api/geocode?q=${encodeURIComponent(normalizedQuery)}`);
260const data = await response.json();
261272);
273
274// Sort API results normally
275const sortedApiResults = data.results.sort((a: GeocodingResult, b: GeocodingResult) => {
276const aNameLower = a.name.toLowerCase();
277const bNameLower = b.name.toLowerCase();
289});
290
291// Combine priority locations first, then API results (removing duplicates)
292const allResults = [
293...matchingPriorityLocations,
294...sortedApiResults.filter(apiResult =>
295!matchingPriorityLocations.some(priority =>
296priority.name === apiResult.name && priority.country === apiResult.country
297)
298)
307queryClient.prefetchQuery({
308queryKey: ['weather', location.latitude, location.longitude],
309queryFn: () => fetch(`/api/weather?lat=${location.latitude}&lon=${location.longitude}`).then(res => res.json()),
310staleTime: 5 * 60 * 1000, // 5 minutes
311});
14- **Routing**: TanStack Router for URL state management
15- **Styling**: CSS with responsive grid layouts
16- **Data Source**: Open-Meteo KNMI API (no API key required)
1718## Project Structure
34- **Hono server** with error unwrapping for better debugging
35- **Static file serving** using Val Town utilities (`serveFile`, `readFile`)
36- **Three main API endpoints**:
37- `/api/weather?lat={lat}&lon={lon}` - Weather data with photography insights
38- `/api/location` - Auto-detect location from IP with Amsterdam fallback
39- `/api/geocode?q={query}` - Search locations by city/area name
40- **Photography scoring algorithm** that calculates 1-10 ratings based on:
41- Cloud cover percentage (main factor)
57### Core Data Flow
581. **Location Detection**: Auto-detect via IP or manual search/coordinates
592. **Weather Fetch**: Query Open-Meteo KNMI API with comprehensive hourly data
603. **Photography Analysis**: Generate insights with scoring algorithm
614. **UI Rendering**: Display in responsive grid with day/night distinction
145- Text files only (no binary uploads)
146- Serverless Deno environment
147- No Node.js APIs available
148- Cannot use `alert()`, `prompt()`, or `confirm()`
149- Use `return new Response(null, { status: 302, headers: { Location: "/path" }})` for redirects
158- **Error handling**: Let errors bubble up with context rather than catching/logging
159- **Never include secrets** in code - always use environment variables
160- **Prefer official SDKs** over writing API calls directly
161- **Comments**: Only explain complex logic, avoid commenting obvious operations
162- **Complete solutions**: Provide functional implementations, not skeleton code
174```
175176## Data Sources and APIs
177178- **Weather**: Open-Meteo KNMI API (free, no API key)
179- **Geocoding**: Open-Meteo Geocoding API (free, no API key)
180- **Location**: ip-api.com for IP-based location detection
181- **Fallback**: Amsterdam coordinates (52.3676, 4.9041) if detection fails
182213```
214215**Important**: These utilities ONLY run on the server. Pass data to client via HTML injection or API calls.
216217## Testing and Deployment
111. `Remix` this val
122. In `Environment Variables` on the val's left sidebar, put in your
13`FIRECRAWL_API_KEY`
143. Click `Run` on `main.tsx`
154. See the website markdown in the logs below!
TowniePurchaseCreditsRoute.tsx2 matches
15const fetchBalance = async () => {
16try {
17const response = await fetch("/api/credit-balance");
18if (response.ok) {
19const data = await response.json();
3536try {
37const response = await fetch("/api/purchase-credits", {
38method: "POST",
39headers: { "Content-Type": "application/json" },
tanstackReactHonoExampleREADME.md3 matches
38```
3940## API Endpoints
4142- `GET /` - Serves the React application with initial data
43- `GET /api/messages` - Fetch all messages (JSON)
44- `POST /api/messages` - Create a new message
45- `GET /public/**` - Static assets (CSS, JS, etc.)
46- `/*` - All other routes handled by TanStack Router