README
ΒΆ
Tinker π οΈ
A project-aware CLI for database, API, and gRPC interaction β inspired by Laravel Tinker, built for Go (and any language).
Tinker comes with built-in database drivers (SQLite, PostgreSQL, MySQL) for zero-dependency one-shot queries, and composes best-in-class open source tools (usql, httpie/curlie, grpcurl/evans) for interactive sessions. It reads your project's tinker.toml and .env to automatically configure connections, so you can jump straight into working with your project.
Why Tinker?
If you've used Laravel Tinker, you know the workflow: open a shell, query the database, call an API endpoint, test a service β all within your project context. Go doesn't have an equivalent because it's compiled, not interpreted.
Tinker takes a different approach. Instead of trying to be a REPL for Go code, it:
- Reads your project's existing config (
.env,tinker.toml) β no wrapper abstractions - Composes proven OSS tools β
usqlfor DB,httpiefor HTTP,evans/grpcurlfor gRPC - Defines a simple contract β any project that implements
tinker.tomlgets the full experience - Works with any language β not just Go! Any project with a
.envandtinker.tomlworks
Quick Start
Install
One-line install (recommended) β installs binary + configures PATH:
curl -fsSL https://raw.githubusercontent.com/mvaliolahi/tinker/main/install.sh | bash
Or manually with Go:
go install github.com/mvaliolahi/tinker/cmd/tinker@latest
# Then add to PATH (if not already):
export PATH="$(go env GOPATH)/bin:$PATH"
Initialize
cd your-project
tinker init
Tinker will scan your project for:
.envfiles withDATABASE_URL,API_BASE_URL,GRPC_ADDR, etc.- OpenAPI/Swagger spec files
.protodirectories- SQLite database files
- Docker Compose files
And generate a tinker.toml:
# tinker.toml
[database]
source = "env:DATABASE_URL"
type = "postgres"
[api]
base_url = "env:API_BASE_URL"
spec = "openapi.yaml"
auth = "env:API_TOKEN"
auth_type = "bearer"
[grpc]
addr = "env:GRPC_ADDR"
proto_dir = "./proto"
reflection = true
Use
# Open interactive database session (usql)
tinker db
# Database queries (with handy shortcuts)
tinker db tables # list all tables (alias: ls)
tinker db describe users # show table schema (alias: desc)
tinker db indexes users # show table indexes (alias: idx)
tinker db schema users # show CREATE TABLE statement (alias: s)
tinker db count users # count rows (alias: c)
tinker db count users "status='active'" # count with WHERE clause
tinker db find users 1 # find row by ID (alias: f)
tinker db exec "SELECT * FROM users LIMIT 5" # run SQL (aliases: e, sql)
tinker db ping # test database connectivity
tinker db size # show table row counts
# Handy shortcuts
tinker db ls # same as: tinker db tables
tinker db desc users # same as: tinker db describe users
tinker db idx users # same as: tinker db indexes users
tinker db s users # same as: tinker db schema users
tinker db c users # same as: tinker db count users
tinker db f users 1 # same as: tinker db find users 1
tinker db sql "SELECT" # same as: tinker db exec "SELECT 1"
# API endpoints (from OpenAPI spec)
tinker api endpoints # list all endpoints (alias: ep)
tinker api endpoints --tag users # filter by tag
tinker api explore # interactive API explorer
# Call API endpoints
tinker api GET /users
tinker api POST /users '{"name": "Ali"}'
tinker api PUT /users/1 '{"name": "Updated"}'
tinker api DELETE /users/1
# gRPC interactions
tinker grpc # Interactive REPL (evans)
tinker grpc list # List services
tinker grpc describe UserService
tinker grpc call UserService/GetUser '{"id": 1}'
# Custom commands (from [commands] section)
tinker cmd migrate # run custom migrate command
tinker cmd seed # run custom seed command
tinker cmd test # run custom test command
tinker cmd list # list available commands
# Multi-environment support
tinker --env staging db # use staging environment
tinker --env production db ping
tinker env list # list available environments
tinker env show staging # show staging overrides
# Docker Compose
tinker docker # show Docker Compose info
tinker docker list # list services (alias: ls)
# Configuration
tinker config show # display resolved configuration
tinker config validate # validate tinker.toml
# Run one-off Go code in project context
tinker run 'fmt.Println("Hello from tinker!")'
# Run Makefile targets
tinker make build
tinker make test
tinker make list # list available targets
# Manage dependencies
tinker deps list # check what's installed
tinker deps install # install missing tools
The Contract
The tinker.toml file is Tinker's contract with your project. It's declarative, minimal, and language-agnostic.
Full Spec
[database]
# How to get the connection string:
# "env:VAR_NAME" β read from environment variable (supports .env)
# "postgres://user:pass@host:5432/db" β direct DSN
source = "env:DATABASE_URL"
# Database type: postgres, mysql, sqlite3, sqlserver, mongodb
type = "postgres"
# Override the driver name (optional)
driver = ""
[api]
# Base URL for API calls
base_url = "env:API_BASE_URL"
# Path to OpenAPI/Swagger spec (optional β enables autocomplete + explore)
spec = "openapi.yaml"
# Auth token source
auth = "env:API_TOKEN"
# Auth type: bearer, basic, api_key, or raw
auth_type = "bearer"
# Additional default headers
[api.headers]
X-Custom-Header = "value"
[grpc]
# gRPC server address
addr = "env:GRPC_ADDR"
# Directory containing .proto files
proto_dir = "./proto"
# Enable gRPC server reflection
reflection = true
# Custom project commands
[commands]
migrate = "go run ./cmd/migrate"
seed = "go run ./cmd/seed"
test = "go test ./..."
# Multi-environment overrides
[envs.staging.database]
source = "env:STAGING_DATABASE_URL"
[envs.staging.api]
base_url = "env:STAGING_API_BASE_URL"
auth = "env:STAGING_API_TOKEN"
[envs.production.database]
source = "env:PRODUCTION_DATABASE_URL"
[envs.production.api]
base_url = "env:PRODUCTION_API_BASE_URL"
auth = "env:PRODUCTION_API_TOKEN"
Environment Variable Resolution
Any value starting with env: is resolved from the environment. Tinker automatically loads .env files, so this works:
# .env
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
API_BASE_URL=http://localhost:8080
API_TOKEN=secret-token
# tinker.toml
[database]
source = "env:DATABASE_URL"
type = "postgres"
[api]
base_url = "env:API_BASE_URL"
auth = "env:API_TOKEN"
auth_type = "bearer"
Native Database Drivers
Tinker v0.23+ includes pure Go database drivers compiled into the binary:
| Driver | Package | Supports |
|---|---|---|
| PostgreSQL | jackc/pgx/v5 |
All one-shot queries |
| MySQL | go-sql-driver/mysql |
All one-shot queries |
| SQLite | CLI fallback | Queries via sqlite3/litecli/usql |
This means:
tinker db ls,tinker db desc,tinker db c,tinker db f,tinker db sqlwork for PostgreSQL and MySQL without any external CLI tools installed- SQLite uses CLI tools (
sqlite3/litecli/usql) for all queries β this avoids the ~41MBmodernc.org/sqlitedependency which is blocked by some Go module proxies - Cross-compilation works everywhere (pure Go, no CGO)
- External CLIs (
litecli,pgcli,mycli,usql) are needed for interactive sessions (tinker db connect)
Rich Output
Tinker v0.24+ includes professional-grade output formatting:
| Feature | Package | What it does |
|---|---|---|
| Table Rendering | jedib0t/go-pretty/v6 |
Beautiful aligned tables for db describe, db indexes, db size, db find, db exec |
| Syntax Highlighting | alecthomas/chroma/v2 |
SQL highlighting for db schema, JSON highlighting for api responses, 500+ lexers |
| JSON Filtering | tidwall/gjson |
Native --jq filter for tinker api β no jq binary needed for simple paths like data.users.0.name |
Before (tab-separated):
Column Type Nullable Default Key
id INTEGER NOT NULL NULL PK
name TEXT NULL NULL
After (go-pretty table):
COLUMN TYPE NULLABLE DEFAULT KEY
id INTEGER NOT NULL NULL PK
name TEXT NULL NULL
The --jq flag now tries gjson first (native Go), then falls back to jq CLI for complex expressions:
# Simple path β gjson (no jq binary needed)
tinker api GET /users -q "data.0.name"
# Complex expression β falls back to jq CLI
tinker api GET /users -q '.[] | select(.active)'
OpenAPI Spec Integration
Tinker v0.25+ can parse your OpenAPI/Swagger spec to provide endpoint discovery and an interactive API explorer.
List Endpoints
# List all endpoints from your spec
tinker api endpoints
# Filter by tag
tinker api endpoints --tag users
# Shortcut
tinker api ep
Output:
API Endpoints
spec: My API v2.1.0
total: 12 endpoint(s)
users
GET /users β List all users
POST /users β Create a user
GET /users/{id} β Get user by ID
PUT /users/{id} β Update user
DELETE /users/{id} β Delete user
Interactive API Explorer
tinker api explore
Provides a REPL for browsing and calling endpoints:
API API Explorer
base: http://localhost:8080
spec: My API v2.1.0
endpoints: 12 available
Commands:
list List all endpoints
call <method> <path> [body] Call an endpoint
find <keyword> Search endpoints
tags List tags
quit / q Exit
tinker/api> GET /users/1
Spec Configuration
Add the spec field to your [api] section:
[api]
base_url = "env:API_BASE_URL"
spec = "openapi.yaml" # or swagger.json, openapi.yml
auth = "env:API_TOKEN"
auth_type = "bearer"
Supports both YAML and JSON formats (OpenAPI 3.x and Swagger 2.x).
Multi-Environment Support
Tinker v0.25+ supports environment-specific configuration overrides via the [envs.*] sections in tinker.toml.
Configuration
# Base configuration (default environment)
[database]
source = "env:DATABASE_URL"
type = "postgres"
[api]
base_url = "env:API_BASE_URL"
auth = "env:API_TOKEN"
auth_type = "bearer"
# Staging overrides
[envs.staging.database]
source = "env:STAGING_DATABASE_URL"
[envs.staging.api]
base_url = "env:STAGING_API_BASE_URL"
auth = "env:STAGING_API_TOKEN"
# Production overrides
[envs.production.database]
source = "env:PRODUCTION_DATABASE_URL"
[envs.production.api]
base_url = "env:PRODUCTION_API_BASE_URL"
auth = "env:PRODUCTION_API_TOKEN"
Usage
# Default environment
tinker db ping
# Staging environment
tinker --env staging db ping
# Production environment
tinker --env production db tables
# List available environments
tinker env list
# Show overrides for an environment
tinker env show staging
Only the fields you specify in the override section are changed; everything else inherits from the base configuration.
Custom Commands
Tinker v0.25+ supports custom project commands via the [commands] section in tinker.toml.
Configuration
[commands]
migrate = "go run ./cmd/migrate"
seed = "go run ./cmd/seed"
test = "go test ./..."
lint = "golangci-lint run"
dev = "air"
build = "go build -o bin/app ./cmd/app"
Usage
# Run a custom command
tinker cmd migrate
tinker cmd seed
tinker cmd test
# With extra arguments (appended to the command)
tinker cmd test -v -run TestUser
# List available commands
tinker cmd list
Commands are executed via your system shell, so they can include pipes, redirects, and any shell features.
Docker Compose Integration
Tinker v0.25+ auto-detects Docker Compose files and extracts service information.
Usage
# Show Docker Compose info
tinker docker
# List services
tinker docker list
Output:
Docker Compose Services
file: docker-compose.yml
db postgres:15 ports: 5432:5432
detected: [DB database]
api myapp:latest ports: 8080:8080
detected: [API api]
Auto-Detection
tinker init automatically detects:
docker-compose.yml,docker-compose.yaml,compose.yml,compose.yaml- Environment-specific compose files (
docker-compose.staging.yml, etc.) - Service types (database, API, gRPC) based on image names, service names, and environment variables
Prerequisites
Tinker's one-shot DB commands work out of the box with built-in drivers. For interactive sessions, Tinker orchestrates existing tools. tinker init auto-installs the ones you need based on what's detected in your project. You can also manage them manually:
# Check which tools are installed
tinker deps list
# Install all missing tools
tinker deps install
| Tool | Purpose | Required? | Manual Install |
|---|---|---|---|
| usql | Database REPL (fallback) | Interactive only | go install github.com/xo/usql@latest |
| litecli | SQLite REPL (syntax highlighting) | Interactive only | pip install litecli |
| pgcli | PostgreSQL REPL (syntax highlighting) | Interactive only | pip install pgcli |
| mycli | MySQL REPL (syntax highlighting) | Interactive only | pip install mycli |
| httpie | HTTP client | API feature | pip install httpie or brew install httpie |
| curlie | HTTP client (alt) | API feature | go install github.com/rs/curlie@latest |
| evans | gRPC REPL | gRPC feature | go install github.com/ktr0731/evans@latest |
| grpcurl | gRPC client | gRPC feature | go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest |
You don't need all of them β only install the tools for the features you use.
Note: If
go installfails due to proxy rate limits, tryGOPROXY=direct go install <module>@latest.
Architecture
βββββββββββββββββββββββββββββββββββββββββββ
β tinker CLI (Go) β
ββββββββββββββββ¬ββββββββββββ¬βββββββββββββββ€
β DB Module β API Moduleβ gRPC Module β
β (native + β (native + β (grpcurl/ β
β CLI) β CLI) β evans) β
ββββββββββββββββ΄ββββββββββββ΄βββββββββββββββ€
β OpenAPI Parser β Commands β Envs β Dockerβ
βββββββββββββββββββββββββββββββββββββββββββ€
β Config Layer β
β (tinker.toml + .env resolver) β
βββββββββββββββββββββββββββββββββββββββββββ€
β Auto-Detection Engine β
β (tinker init scans project) β
βββββββββββββββββββββββββββββββββββββββββββ
Design principles:
- Native first, CLI fallback β Built-in pure Go drivers for zero-dependency DB queries; external CLIs for interactive sessions
- Compose, don't reimplement β Shell out to httpie/evans for interactive sessions, don't rewrite them
- Contract is declarative β
tinker.toml, not a Go interface - Auto-detect first, configure second β
tinker initshould work for 80% of cases - Cross-language by default β Works with any project that has a
.envandtinker.toml - Beautiful terminal UI β Styled output with lipgloss, colored badges, and clear visual hierarchy
- Spec-driven API exploration β Parse OpenAPI specs for endpoint discovery and interactive exploration
Comparison
| Feature | Laravel Tinker | gore | Tinker (this project) |
|---|---|---|---|
| Styled TUI | β | β | β lipgloss |
| Interactive DB session | β Eloquent | β | β usql (raw SQL) |
| Call API endpoints | β HTTP client | β | β httpie/curlie |
| Call gRPC services | β | β | β evans/grpcurl |
| Language-agnostic | β PHP only | β Go only | β Any project |
| Framework-agnostic | β Laravel only | β | β |
| Execute project code | β Full PHP | β Go | β οΈ tinker run (limited) |
| Auto-configuration | β | β | β
tinker init |
| Persistent state | β | β οΈ Per eval | β DB session |
| OpenAPI spec parsing | β | β | β
tinker api endpoints |
| Interactive API explorer | β | β | β
tinker api explore |
| Multi-environment | β | β | β
tinker --env staging |
| Custom commands | β | β | β
tinker cmd <name> |
| Docker Compose detection | β | β | β
tinker docker |
Roadmap
- Native database drivers (SQLite, PostgreSQL, MySQL) β no external CLI needed for queries
-
tinker db indexβ show table indexes -
tinker db pingβ test connectivity -
tinker db sizeβ table row counts - Rich table formatting for describe/indexes output
- Shell completion (bash, zsh, fish, powershell)
- Rich table rendering (go-pretty)
- Syntax highlighting for SQL output (chroma)
- Native JSON filtering via gjson (--jq without jq binary)
- OpenAPI spec parsing for API endpoint autocomplete
-
tinker api exploreβ interactive API explorer - Custom command support from
[commands]section - Multi-environment support (
tinker --env staging db) - Docker integration β auto-detect docker-compose services
- Plugin system for custom modules
-
tinker db seedβ run seed files -
tinker db migrateβ run migrations -
tinker db exploreβ interactive TUI database browser - Native gRPC via grpcurl library (no external binary)
- HTTP session persistence (cookies, auth state across requests)
Contributing
Contributions are welcome! This project is in early development.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT License β see LICENSE for details.