This advanced example demonstrates how to use GoScript to build a full-stack todo list application with modern TypeScript tooling.
- GoScript - Business logic written in Go, transpiled to TypeScript
- tRPC - Type-safe API layer with end-to-end type safety
- React - Frontend UI library
- Vite - Fast frontend build tool
- TanStack Query - Powerful data fetching for React
- Drizzle ORM - Type-safe database access with excellent DX
- SQLite - Embedded database (via Bun's native bun:sqlite)
- Zod - Runtime input validation with TypeScript type inference
- Bun - Fast all-in-one JavaScript runtime
┌─────────────────────────────────────────────────────────────┐
│ Client │
│ (client.ts / curl) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ tRPC Server │
│ (server.ts) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ tRPC Router │
│ (trpc/router.ts) │
│ │
│ ┌─────────────────────┐ ┌────────────────────────────┐ │
│ │ GoScript Logic │◄───│ Zod Validation │ │
│ │ (todo/todo.go) │ │ (input schemas) │ │
│ │ Compiled to TS │ └────────────────────────────┘ │
│ └─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Drizzle ORM │ │
│ │ (db/schema.ts) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ SQLite │
│ (todos.db) │
└─────────────────────────────────────────────────────────────┘
The core business logic for todos is written in Go (todo/todo.go) and includes:
Todo- Struct with ID, title, description, priority, timestampsPriority- Enum type (Low, Medium, High)TodoList- Collection manager with CRUD operationsStats- Statistics struct for analytics
NewTodo(title)- Create a new todoNewTodoList()- Create a new listValidate(title)- Validate todo titleValidateDescription(desc)- Validate descriptionPriorityString(p)- Convert priority to stringParsePriority(s)- Parse string to priority
SetDescription(desc)- Set description with timestampSetPriority(p)- Set priority with timestampMarkComplete()/MarkIncomplete()- Toggle completionToggle()- Toggle completed stateIsOverdue(deadline)- Check if overdue
Add(todo)- Add todo with auto-IDGet(id)/Remove(id)- CRUD operationsAll()/Active()/Completed()- FilteringByPriority(p)- Filter by priorityCount()/ActiveCount()/CompletedCount()- StatsClearCompleted()- Bulk deleteGetStats()- Get full statistics
The Go code is compiled to TypeScript using GoScript and then imported in the tRPC router to provide type-safe validation and business logic.
# Install dependencies
bun install
# Build GoScript code
bash build.bash
# Start development mode (API server + Vite frontend)
bun run dev- Frontend: http://localhost:5173
- API Server: http://localhost:3000
The Vite dev server proxies /api requests to the backend server.
All endpoints are tRPC procedures accessible via HTTP:
| Procedure | Type | Description |
|---|---|---|
list |
Query | List all todos (filterable by status/priority) |
get |
Query | Get a single todo by ID |
create |
Mutation | Create a new todo |
update |
Mutation | Update a todo |
toggle |
Mutation | Toggle todo completion |
delete |
Mutation | Delete a todo |
clearCompleted |
Mutation | Clear all completed todos |
stats |
Query | Get todo statistics |
bulkCreate |
Mutation | Create multiple todos at once |
priorities |
Query | Get priority definitions from GoScript |
# Create a todo
curl -X POST http://localhost:3000/create \
-H "Content-Type: application/json" \
-d '{"title":"Buy groceries","priority":"high"}'
# List all todos
curl http://localhost:3000/list
# Get stats
curl http://localhost:3000/stats
# Toggle completion
curl -X POST http://localhost:3000/toggle \
-H "Content-Type: application/json" \
-d '{"id":1}'
# Filter by priority
curl 'http://localhost:3000/list?input={"priority":"high"}'
# Filter active only
curl 'http://localhost:3000/list?input={"filter":"active"}'# Start the server in one terminal
bash run.bash
# Run the client demo in another terminal
bun run client.tsThe client demo showcases:
- Creating todos with different priorities
- Listing and filtering todos
- Toggling completion status
- Getting statistics
- Bulk operations
- Cleanup
example/app/
├── src/ # React frontend source
│ ├── App.tsx # Main React component
│ ├── main.tsx # React entry point
│ ├── trpc.ts # tRPC React client setup
│ └── index.css # Styles
├── todo/
│ └── todo.go # Go business logic (GoScript source)
├── db/
│ ├── schema.ts # Drizzle ORM schema definition
│ └── index.ts # Database connection setup
├── trpc/
│ └── router.ts # tRPC router with all procedures
├── output/ # GoScript compiled output (generated)
│ └── @goscript/
│ ├── builtin/ # GoScript runtime
│ ├── time/ # Go time package
│ └── github.com/ # Compiled app packages
├── index.html # Vite HTML entry point
├── vite.config.ts # Vite configuration
├── server.ts # HTTP server entry point
├── client.ts # Example tRPC client (CLI)
├── drizzle.config.ts # Drizzle Kit configuration
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── go.mod # Go module definition
├── build.bash # GoScript build script
├── run.bash # Build and run script
└── README.md # This file
# Build GoScript code only
bash build.bash
# Start full dev mode (API + frontend with hot reload)
bun run dev
# Start only the API server
bun run dev:server
# Start only the Vite frontend
bun run dev:frontend
# Build frontend for production
bun run build:frontend
# Generate Drizzle migrations (if using migrations)
bun run db:generate
# Run Drizzle Studio (visual database browser)
bun run db:studio- Go Business Logic: Write your business logic in Go (
todo/todo.go) - GoScript Compilation: Run
bash build.bashto transpile Go to TypeScript - Import in TypeScript: The compiled code is imported in
trpc/router.ts - tRPC Integration: GoScript validation functions are used in tRPC mutations
- Drizzle Persistence: Drizzle ORM handles database operations
- Type Safety: End-to-end type safety from Go types through tRPC to the client
- Write Once: Business logic written in Go, usable in TypeScript
- Type Safety: Full type safety from database to API to client
- Validation: Go validation logic reused in the TypeScript API
- Modern Stack: Uses the latest TypeScript tooling (tRPC, Drizzle, Zod)
- Fast Runtime: Bun provides excellent performance
- Simple Setup: SQLite requires no external database server
- GoScript is experimental - some Go features may have limited support
- Priority constants from iota are manually defined in the router (GoScript iota support is being improved)
- The
timepackage from Go is used for timestamps in the generated code