• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
project logo

tijs

kipclip-appview

Save bookmarks using atproto
Public
Like
1
kipclip-appview
Home
Code
15
.claude
1
.github
1
.local
backend
9
frontend
7
lexicons
1
resources
1
shared
2
.env.example
.gitignore
.vtignore
CLAUDE.md
DEVELOPMENT.md
README.md
deno.json
Branches
1
Pull requests
Remixes
2
History
Environment variables
6
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
DEVELOPMENT.md
Code
/
DEVELOPMENT.md
Search
11/15/2025
DEVELOPMENT.md

Local Development Guide

This guide explains how to run kipclip locally for development and testing.

Full-Stack Local Development

The local dev server provides complete full-stack development:

  • ✅ Backend API - All endpoints work locally
  • ✅ Frontend UI - React/TSX transpiled on-the-fly with esbuild
  • ✅ Database - Local SQLite with migrations
  • ⚠️ OAuth Flow - Requires public URL (see below)

OAuth Setup with ngrok

ATProto OAuth requires a publicly accessible client metadata URL. For local development, use ngrok to expose your local server:

Setup Steps:

# In terminal 1: Start dev server deno task dev # In terminal 2: Expose with ngrok ngrok http 8000 # Update .env with ngrok URL BASE_URL=https://your-random-id.ngrok.io # Restart dev server to pick up new BASE_URL # Ctrl+C in terminal 1, then: deno task dev

Important: Always restart the dev server after changing BASE_URL in .env so OAuth redirects work correctly.

Option 2: Mock authentication (recommended for API development)

  • Use the test utilities in backend/test-utils.ts
  • Create mock sessions for testing authenticated endpoints
  • No internet required, very fast

Option 3: Deploy to Val.Town for OAuth testing

  • Deploy changes to Val.Town
  • Test OAuth flow in production
  • Use local dev for API logic only

Quick Start

  1. Copy environment template

    cp .env.example .env
  2. Generate a cookie secret

    openssl rand -base64 32

    Paste the output into .env as COOKIE_SECRET

  3. Start the dev server

    deno task dev
  4. Open in browser

    http://localhost:8000
    

Development Setup

Environment Configuration

Edit .env with your values:

# Base URL for OAuth redirects BASE_URL=http://localhost:8000 # Secure random string (min 32 chars) COOKIE_SECRET=your-generated-secret-from-openssl # Optional: Custom port (default: 8000) # PORT=3000

Local vs Production

The app automatically detects whether it's running on Val.Town or locally:

  • Val.Town: Uses Val.Town's sqlite2 module
  • Local: Uses Deno's native SQLite with file at .local/kipclip.db

No code changes needed between environments!

Database

Local SQLite file: .local/kipclip.db

  • Created automatically on first run
  • Migrations run automatically
  • Inspect with any SQLite browser (e.g., DB Browser for SQLite)
  • Ignored by git (see .gitignore)

Schema: Defined in backend/database/schema.ts

Migrations: Tracked in backend/database/migrations.ts

Testing

Unit Tests

Fast tests using MemoryStorage and mocked OAuth:

# Run all tests deno task test # Watch mode for TDD deno task test:watch

Test Utilities

Use backend/test-utils.ts for testing:

import { createMockSession, createTestOAuth } from "./test-utils.ts"; // Create test OAuth instance with MemoryStorage const oauth = createTestOAuth(); // Create mock session (bypasses real authentication) const session = createMockSession({ sub: "did:plc:testuser", handle: "test.bsky.social", });

Example Tests

See backend/routes/bookmarks.test.ts for examples of:

  • Testing route handlers
  • Mocking authentication
  • Validating responses

Testing the API

Since the frontend UI requires Val.Town's transpilation, test the backend API directly:

Example API Requests

# Check server health curl http://localhost:8000/ # Start OAuth login (redirects to Bluesky) curl -I http://localhost:8000/login # List bookmarks (requires authentication) curl http://localhost:8000/api/bookmarks # Get OAuth callback (after authentication) # This happens automatically in browser flow

Using with Frontend on Val.Town

  1. Run backend locally: deno task dev
  2. Deploy frontend-only changes to Val.Town
  3. Point Val.Town frontend to http://localhost:8000 API
  4. Test full stack with local backend + deployed frontend

Available Tasks

deno task dev # Start local dev server (with watch mode) deno task test # Run unit tests deno task test:watch # Run tests in watch mode deno task quality # Run formatters and linters deno task check # Type check all files deno task deploy # Quality checks + deploy to Val.Town

OAuth Flow

The local dev server uses real ATProto OAuth:

  1. Click "Login with Bluesky"
  2. Redirects to your PDS (e.g., bsky.social)
  3. Authorize the app
  4. Redirects back to http://localhost:8000/oauth/callback
  5. Session stored in local SQLite database

Project Structure

kipclip-appview/
├── .env                    # Your local environment (gitignored)
├── .env.example            # Environment template
├── .local/                 # Local dev files (gitignored)
│   └── kipclip.db         # SQLite database
├── backend/
│   ├── dev.ts             # Local dev server entry point
│   ├── index.ts           # Production entry point (Val.Town)
│   ├── test-utils.ts      # Test helpers
│   ├── database/
│   │   ├── db.ts          # Environment-aware DB config
│   │   ├── local-sqlite.ts # Local SQLite adapter
│   │   ├── schema.ts      # Database schema
│   │   └── migrations.ts  # Migration runner
│   ├── routes/
│   │   ├── bookmarks.ts   # Bookmark API routes
│   │   └── bookmarks.test.ts # Example tests
│   └── services/
│       └── auth.ts        # OAuth session handling
└── frontend/              # React frontend

Troubleshooting

Database locked error

SQLite can only have one writer at a time. Make sure you don't have multiple dev servers running.

OAuth redirect fails

Check that BASE_URL in .env matches the URL you're accessing (including port).

Port already in use

Change the port in .env:

PORT=3000

Tests failing

Make sure you're using deno task test (not just deno test) to get the correct permissions.

Tips

  1. Database inspection: Use DB Browser for SQLite to inspect .local/kipclip.db
  2. Watch mode: Both dev and test:watch tasks auto-reload on file changes
  3. Real OAuth: Test with your actual Bluesky account for realistic testing
  4. Fast tests: Unit tests use MemoryStorage, so they're very fast
  5. Clean state: Delete .local/ directory to reset local database

Next Steps

  • Write more tests for your API routes
  • Add integration tests that hit real PDS (optional)
  • Inspect database schema to understand data model
  • Try adding new features locally before deploying
FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.