16```
17backend/
18 database.ts # Database setup and queries
19 api.ts # API endpoints
20frontend/
40 <li>We extract email domains from your user list</li>
41 <li>
42 We match these domains against our database of YC companies (sourced{" "}
43 <a href="https://docs.google.com/spreadsheets/d/181GQmXflgMCCI9awLbzK4Zf0uneQBKoh51wBjNTc8Us/edit?gid=0#gid=0">
44 here
45 </a>
46 , cached <a href="https://www.val.town/v/stevekrouse/yc_database">here</a>)
47 </li>
48 <li>You get a detailed report of matches, enriched with YC company data</li>
226
227export default async function server(request: Request): Promise<Response> {
228 const companies = await fetch("https://stevekrouse-yc_database.web.val.run").then(res => res.json());
229 const url = new URL(request.url);
230 if (url.pathname === "/companies.json") {
2
3const kvStore = {
4 // Initializes the database by creating the table if it doesn't exist.
5 async init() {
6 await mockSqliteExecute(`
47};
48
49// Initialize database
50async function initDB() {
51 await sqlite.execute(`CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (
46 "slug": "codegen",
47 "link": "/blog/codegen",
48 "description": "Like Claude Artifacts, but with a backend and database",
49 "pubDate": "Thu, 22 Aug 2024 00:00:00 GMT",
50 "author": "JP Posma",
1---
2title: "Post-mortem: A Backward Incompatible Database Migration"
3description: Val runs failed due to a database migration that was not backward compatible
4pubDate: 2025-04-02T00:00:00.000Z
5author: Sophie Houser
6---
7
8Today at 10:11am we experienced a 12-minute outage, which caused HTTP vals to return 503 errors and other types of vals to fail. In the end, the root cause was a deployment timing issue where database migrations were deployed successfully, but our application code deployment hung for several minutes. The new database migrations were incompatible with the old application code and crashed the process.
9
10We aim to make all database migrations maintain backward compatibility, but in this case, we only discovered through the delayed deployment feedback that the new migrations were not compatible with previous versions.
11
12## Timeline
27## Next Steps
28
29Reliability is important to us and we’ve taken steps to make sure this doesn’t happen again. We’ve added a test to ensure database migrations are backward compatible, which we’ll run before we deploy any new code that includes database migrations.
5
6 // Step 1: Query active URLs
7 console.log("[STEP] Querying active URLs from the database...");
8 const result = await sqlite.execute("SELECT url FROM urls WHERE active = 1");
9 const urls: string[] = result.rows.map((row) => row[0] as string);
25 console.log(`[SUCCESS] Received status code: ${res.status}`);
26 console.log(`[METRIC] Response time: ${responseTime}s`);
27 console.log(`[DB] Updating database with success metrics...`);
28
29 await sqlite.execute({
42 const message = err instanceof Error ? err.message : "Unknown error";
43 console.error(`[ERROR] Failed to fetch ${url}: ${message}`);
44 console.log(`[DB] Updating database with error state...`);
45
46 await sqlite.execute({
55 });
56
57 console.log(`[DB] Error logged in database for: ${url}`);
58 }
59 }
385
386 public static function activate() {
387 // Create database tables if needed
388 self::create_tables();
389
396
397 private static function create_tables() {
398 // Database creation logic here if needed
399 }
400
127 console.error("Public dashboard error:", error);
128
129 // Return default data if database is not ready
130 return c.json({
131 stats: [
65export const relaxedRateLimit = rateLimitMiddleware(200, 60000); // 200 requests per minute
66
67// Database-backed rate limiter for persistent limits
68export const persistentRateLimitMiddleware = (
69 limit: number = 100,