TL;DR
Val Town lets you write, run, and deploy TypeScript functions in the cloud with zero infrastructure. Create APIs, cron jobs, and email handlers — all from your browser. Free tier includes 100K function calls/month.
What Is Val Town?
Val Town is a social coding platform:
- Instant deployment — write a function, it's live immediately
- HTTP endpoints — each function gets a URL
- Cron jobs — schedule functions to run on intervals
- Email handlers — receive and process emails
- SQLite database — built-in persistence
- NPM packages — import any npm package
- Free tier — 100K requests/month
Quick Start
Go to val.town and create a new val:
// HTTP endpoint — instantly gets a URL
export default async function(req: Request): Promise<Response> {
const url = new URL(req.url);
const name = url.searchParams.get("name") || "World";
return Response.json({
message: `Hello, ${name}!`,
timestamp: new Date().toISOString(),
});
}
// Live at: https://username-funcname.web.val.run
Cron Jobs
// Runs every hour — check website status
export default async function() {
const sites = [
"https://example.com",
"https://myapp.com",
"https://api.myservice.com/health",
];
for (const site of sites) {
const start = Date.now();
try {
const res = await fetch(site);
const latency = Date.now() - start;
if (!res.ok) {
await sendAlert(`${site} returned ${res.status} (${latency}ms)`);
}
} catch (err) {
await sendAlert(`${site} is DOWN: ${err.message}`);
}
}
}
Built-in SQLite
import { sqlite } from "https://esm.town/v/std/sqlite";
// Create table
await sqlite.execute(`
CREATE TABLE IF NOT EXISTS pageviews (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT NOT NULL,
user_agent TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
export default async function(req: Request): Promise<Response> {
const url = new URL(req.url);
// Track pageview
await sqlite.execute({
sql: "INSERT INTO pageviews (path, user_agent) VALUES (?, ?)",
args: [url.pathname, req.headers.get("user-agent")],
});
// Get stats
const stats = await sqlite.execute(
"SELECT path, COUNT(*) as views FROM pageviews GROUP BY path ORDER BY views DESC LIMIT 10"
);
return Response.json(stats.rows);
}
Email Handler
// Receive emails at username.funcname@valtown.email
export default async function(email: {
from: string;
to: string[];
subject: string;
text: string;
}) {
// Process incoming email
console.log(`Email from ${email.from}: ${email.subject}`);
// Store in SQLite
await sqlite.execute({
sql: "INSERT INTO emails (sender, subject, body) VALUES (?, ?, ?)",
args: [email.from, email.subject, email.text],
});
// Auto-reply
await email.reply("Thanks! I got your message.");
}
Use NPM Packages
import Anthropic from "npm:@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: Deno.env.get("ANTHROPIC_API_KEY") });
export default async function(req: Request): Promise<Response> {
const { prompt } = await req.json();
const message = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
});
return Response.json({ response: message.content[0].text });
}
Val Town vs Alternatives
| Feature | Val Town | Cloudflare Workers | AWS Lambda | Vercel Functions |
|---|---|---|---|---|
| Free tier | 100K calls | 100K calls | 1M calls | 100K calls |
| Deploy time | Instant | ~30s | ~60s | ~30s |
| Browser IDE | Yes | Yes (Wrangler) | No | No |
| Built-in DB | SQLite | D1 | DynamoDB ($) | Vercel KV ($) |
| Cron jobs | Yes | Yes | EventBridge | Yes |
| Email handler | Yes | Email Workers | SES | No |
| Social/sharing | Yes | No | No | No |
Resources
Need web data for your Val Town functions? My Apify scraping tools extract data from any website — call them from Val Town for serverless data pipelines. Questions? Email spinov001@gmail.com
Top comments (0)