DEV Community

Alex Spinov
Alex Spinov

Posted on

Val Town Has a Free API — Here's How to Deploy Serverless Functions in Seconds

A developer told me: 'I needed a webhook endpoint. I spent 45 minutes setting up a Vercel project, configuring the build, deploying. Then I found Val Town — I had a live endpoint in 30 seconds.'

What Val Town Offers for Free

Val Town free tier:

  • Unlimited vals (serverless functions)
  • HTTP endpoints — instant API endpoints
  • Cron jobs — scheduled functions
  • Email handling — receive and send emails
  • Blob storage — persistent key-value store
  • SQLite database — built-in SQL database
  • npm packages — import any package
  • Web-based editor with TypeScript support

Quick Start

// Create an HTTP endpoint (instant API)
// Just write this in Val Town's editor:
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()
  });
}
// Instantly available at: https://username-valname.web.val.run
Enter fullscreen mode Exit fullscreen mode

Build an API in Minutes

import { blob } from 'https://esm.town/v/std/blob';

// Simple CRUD API with blob storage
export default async function(req: Request): Promise<Response> {
  const url = new URL(req.url);
  const path = url.pathname;

  // GET /items — list all
  if (req.method === 'GET' && path === '/items') {
    const items = await blob.getJSON('items') || [];
    return Response.json(items);
  }

  // POST /items — create
  if (req.method === 'POST' && path === '/items') {
    const body = await req.json();
    const items = await blob.getJSON('items') || [];
    const newItem = { id: Date.now(), ...body, createdAt: new Date().toISOString() };
    items.push(newItem);
    await blob.setJSON('items', items);
    return Response.json(newItem, { status: 201 });
  }

  return new Response('Not Found', { status: 404 });
}
Enter fullscreen mode Exit fullscreen mode

Cron Jobs

// Runs every hour — no infrastructure to manage
export default async function() {
  const res = await fetch('https://api.example.com/health');

  if (!res.ok) {
    // Send alert via email
    await email({ to: 'you@example.com', subject: 'API Down!', text: `Status: ${res.status}` });
  }

  console.log(`Health check: ${res.status} at ${new Date().toISOString()}`);
}
// Set schedule in Val Town: @cron('0 * * * *') — every hour
Enter fullscreen mode Exit fullscreen mode

SQLite Database

import { sqlite } from 'https://esm.town/v/std/sqlite';

// Create table
await sqlite.execute(`
  CREATE TABLE IF NOT EXISTS bookmarks (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    url TEXT NOT NULL,
    title TEXT,
    tags TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  )
`);

export default async function(req: Request): Promise<Response> {
  if (req.method === 'POST') {
    const { url, title, tags } = await req.json();
    await sqlite.execute(
      'INSERT INTO bookmarks (url, title, tags) VALUES (?, ?, ?)',
      [url, title, tags?.join(',')]
    );
    return Response.json({ success: true });
  }

  const { rows } = await sqlite.execute(
    'SELECT * FROM bookmarks ORDER BY created_at DESC LIMIT 50'
  );
  return Response.json(rows);
}
Enter fullscreen mode Exit fullscreen mode

Email Handling

import { email } from 'https://esm.town/v/std/email';

// Send email
await email({
  to: 'alice@example.com',
  subject: 'Your weekly report',
  html: '<h1>Weekly Stats</h1><p>Views: 1,234</p>'
});

// Receive email — any email sent to your@valtown.email triggers this val
export default async function(receivedEmail) {
  console.log(`From: ${receivedEmail.from}`);
  console.log(`Subject: ${receivedEmail.subject}`);
  console.log(`Body: ${receivedEmail.text}`);

  // Auto-reply, save to database, trigger webhook, etc.
}
Enter fullscreen mode Exit fullscreen mode

REST API for Val Town Itself

# Run a val
curl 'https://api.val.town/v1/run/username.myVal' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -d '{"args": [{"name": "Alice"}]}'

# List your vals
curl 'https://api.val.town/v1/me/vals' \
  -H 'Authorization: Bearer YOUR_API_TOKEN'

# Create a new val
curl -X POST 'https://api.val.town/v1/vals' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"code": "export default function() { return 42; }", "name": "myNewVal"}'
Enter fullscreen mode Exit fullscreen mode

Use Cases

  • Webhook receivers — Stripe, GitHub, Slack webhooks
  • API proxies — add auth, caching, or rate limiting to any API
  • Cron jobs — scheduled scrapers, health checks, reports
  • Quick APIs — bookmark savers, URL shorteners, form backends
  • Email automation — auto-responders, email-to-database

Need to scrape data on autopilot? Check out my web scraping actors on Apify — no-code data collection.

Need custom serverless functions? Email me at spinov001@gmail.com.

Top comments (0)