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" });
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
tanstackReactHonoExamplequeries.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" },
tanstackReactHonoExampleindex.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
4} from "npm:@mendable/firecrawl-js";
56const app = new FirecrawlApp({ apiKey: Deno.env.get("FIRECRAWL_API_KEY") });
78// Scrape a website
Towniesend-message.ts12 matches
48project,
49branchId,
50anthropicApiKey,
51selectedFiles,
52images,
5455// do we want to allow user-provided tokens still
56const apiKey = anthropicApiKey || Deno.env.get("ANTHROPIC_API_KEY");
57const our_api_token = apiKey === Deno.env.get("ANTHROPIC_API_KEY");
5859if (our_api_token) {
60if (await hasInsufficientCredits({ bearerToken })) {
61return Response.json({
7374const rowid = await startTrackingUsage({
75our_api_token,
76bearerToken, // will look up the userId from this
77branch_id: branchId,
8283// Initialize PostHog client
84const projectApiKey = Deno.env.get("POSTHOG_PROJECT_API_KEY");
8586let tracedModel;
8788if (projectApiKey) {
89const phClient = new PostHog(projectApiKey, {
90host: "https://us.i.posthog.com",
91});
98const traceId = `townie_${rowid}_${Date.now()}`;
99100const anthropic = createAnthropic({ apiKey });
101102// Wrap the Anthropic model with PostHog tracing
108townie_branch_id: branchId,
109townie_usage_id: rowid,
110townie_our_api_token: our_api_token,
111townie_num_images: images?.length || 0,
112townie_selected_files_count: selectedFiles?.length || 0,
121} else {
122// Fallback to regular Anthropic call if PostHog is not configured
123const anthropic = createAnthropic({ apiKey });
124tracedModel = anthropic(model);
125}
271});
272273if (our_api_token) {
274const stillHasCredits =
275!(await hasInsufficientCredits({ bearerToken })); // Check for at least 1 cent