12 KiB
dclient v2 — Changes from v1
Summary of changes
- Aliases point at Datasette instances, not database URLs
- Instance is always a flag (
-i/--instance), never a positional argument queryandinsert/upserttake database as a required positional argument- Introspection commands (
tables,schema, etc.) use-d/--databasewith defaults - Default instance and default database reduce typing
- Bare SQL defaults to
querycommand viaclick-default-group, using defaults +-doverride - New commands:
databases,tables,schema,plugins - Config and auth remain separate files, with a new config format
- New
DATASETTE_DATABASEenvironment variable
Config changes
config.json (replaces aliases.json)
{
"default_instance": "prod",
"instances": {
"prod": {
"url": "https://myapp.datasette.cloud",
"default_database": "main"
},
"local": {
"url": "http://localhost:8001",
"default_database": null
}
}
}
auth.json (same file, new key structure)
Keys are now alias names instead of URLs:
{
"prod": "dstok_abc123",
"local": "dstok_def456"
}
When no alias exists (using -i with a raw URL), tokens are looked up by URL with the existing prefix-matching logic as a fallback.
Migration
On first run, if config.json doesn't exist but aliases.json does:
- Parse each v1 alias URL. If it has a single path segment (e.g.,
https://example.com/mydb), split into instance URL + default database. Otherwise store the URL as-is. - Migrate auth.json keys from URLs to alias names where a match exists, keep URL-keyed entries as fallbacks.
- Write new files, rename originals to
.bak.
Environment variables
| Variable | Purpose | Precedence |
|---|---|---|
DATASETTE_URL |
Default instance URL (existing) | Below config default, above nothing |
DATASETTE_DATABASE |
Default database name (new) | Below config default, above auto-detect |
DATASETTE_TOKEN |
Auth token (existing) | Below stored token, above nothing |
Two ways to query: explicit and shortcut
The design provides two distinct interfaces for running queries, optimized for different workflows.
Explicit: dclient query <database> <sql>
Database is a required positional argument. No defaults involved. This is unambiguous and works well for zero-config usage and when switching between databases frequently.
dclient query fixtures "select * from facetable limit 5"
dclient query analytics "select count(*) from events" -i staging
Shortcut: dclient <sql>
When the first argument doesn't match any subcommand, click-default-group routes it to a default command that uses the configured default instance and default database. Override either with flags.
dclient "select count(*) from users" # default instance + default db
dclient "select count(*) from users" -d analytics # override database
dclient "select count(*) from users" -i staging # override instance
This is the daily-driver mode for people with a configured default instance and database.
Implementation
from click_default_group import DefaultGroup
@click.group(cls=DefaultGroup, default="default_query", default_if_no_args=False)
@click.version_option()
def cli():
"A client CLI utility for Datasette instances"
@cli.command()
@click.argument("database")
@click.argument("sql")
@click.option("-i", "--instance")
@click.option("--token")
@click.option("-v", "--verbose", is_flag=True)
# ... output format options ...
def query(database, sql, instance, token, verbose, **kwargs):
"""Run a SQL query against a Datasette database
Requires both a database name and a SQL string.
Example:
dclient query fixtures "select * from facetable limit 5"
"""
...
@cli.command(name="default_query", hidden=True)
@click.argument("sql")
@click.option("-i", "--instance")
@click.option("-d", "--database")
@click.option("--token")
@click.option("-v", "--verbose", is_flag=True)
# ... output format options ...
def default_query(sql, instance, database, token, verbose, **kwargs):
"""Run a SQL query using default instance and database."""
# Resolve instance and database from defaults/env vars
# Error if no default database can be resolved
...
The default_query command is hidden from help output. Users see query in the help text; the bare-SQL shortcut just works without being documented as a separate command.
Instance resolution
Every command that talks to a Datasette server accepts -i, --instance TEXT. Resolution order:
-iflag (alias name or URL)config.default_instanceDATASETTE_URLenvironment variable- Error
If the value starts with http:// or https://, use it as a URL directly. Otherwise look it up as an alias name in config.
Database resolution
There are two modes of database resolution depending on the command.
Commands with required positional database
query, insert, upsert: the database is always the first positional argument. No resolution logic, no defaults. You must name the database.
Commands with optional -d flag
tables, schema, and the default query shortcut: resolve in order:
-dflag- Instance's
default_databasefrom config DATASETTE_DATABASEenvironment variable- Auto-detect if instance has exactly one (non-internal) database
- Error with a helpful message listing available databases
Commands that don't need a database
databases, plugins, actor, alias, auth: no database argument or flag.
New commands
dclient databases
List databases on an instance.
$ dclient databases
main
extra
$ dclient databases --json
[{"name": "main", "tables_count": 12, ...}, ...]
$ dclient databases -i https://latest.datasette.io
fixtures
Options: -i, --json.
Implementation: GET <instance>/.json → databases key.
dclient tables
List tables (and optionally views) in a database.
$ dclient tables
facetable 15 rows
facet_cities 4 rows
$ dclient tables -d analytics
events 1503 rows
$ dclient tables --views --json
[{"name": "facetable", "columns": [...], "count": 15, ...}, ...]
Options: -i, -d, --views, --views-only, --hidden, --json.
Implementation: GET <instance>/<database>.json → tables and views keys.
dclient schema
Show SQL schema for a database or a specific table.
$ dclient schema
# all CREATE TABLE/VIEW statements for default database
$ dclient schema -d analytics
# all schemas for the analytics database
$ dclient schema facetable
# just that table's schema (in default database)
$ dclient schema facetable -d analytics
# that table in a specific database
The optional table name is a positional argument. Options: -i, -d, --json.
dclient plugins
List installed plugins on an instance.
$ dclient plugins
datasette-files
datasette-auth-tokens
$ dclient plugins --json
[{"name": "datasette-files", "version": "0.3.1", ...}, ...]
Options: -i, --json.
Implementation: GET <instance>/-/plugins.json.
Changed commands
dclient query
# v1
dclient query https://datasette.io/content "select * from news"
# v2: database is required positional, instance is a flag
dclient query content "select * from news" -i https://datasette.io
dclient query fixtures "select * from facetable" # uses default instance
Signature: dclient query <database> <sql> [-i instance] [--csv|--tsv|--nl|--table] [-o file] [--token TOKEN] [-v]
dclient insert
# v1
dclient insert https://myapp.datasette.cloud/data mytable data.csv --csv
# v2: database and table are required positionals, instance is a flag
dclient insert main mytable data.csv --csv -i myapp
dclient insert main mytable data.csv --csv # uses default instance
Signature: dclient insert <database> <table> <file> [-i instance] [--csv|--tsv|--json|--nl] [--create] [--replace] [--ignore] [--alter] [--pk col] [--batch-size N] [--interval N] [--token TOKEN] [-v] [--silent]
dclient upsert
New command. Same shape as insert but hits /-/upsert.
Signature: dclient upsert <database> <table> <file> [-i instance] [--csv|--tsv|--json|--nl] [--alter] [--pk col] [--batch-size N] [--interval N] [--token TOKEN] [-v] [--silent]
dclient alias
dclient alias add <name> <url> # url is the instance root, stored as-is
dclient alias remove <name>
dclient alias list # shows * for default, (db: x) for default db
dclient alias default [name] # set/show default instance
dclient alias default --clear
dclient alias default-db <alias> [db] # set/show default database for an alias
dclient alias default-db <alias> --clear
Tip: multiple aliases for the same instance. You can create several aliases pointing at the same instance URL with different default databases. This gives you short names for databases you switch between frequently:
dclient alias add prod https://myapp.datasette.cloud
dclient alias add prod-analytics https://myapp.datasette.cloud
dclient alias default-db prod main
dclient alias default-db prod-analytics analytics
dclient tables -i prod # → main
dclient tables -i prod-analytics # → analytics
Auth tokens are resolved by alias name first, then by URL match as a fallback, so a token stored for either alias will work for both.
dclient auth
dclient auth add <alias-or-url> # prompt for token
dclient auth add <alias-or-url> --token TOKEN
dclient auth remove <alias-or-url>
dclient auth list # shows which aliases have tokens, never values
dclient auth status [-i instance] # calls /-/actor.json to verify
dclient actor
Uses -i flag instead of positional URL:
dclient actor # default instance
dclient actor -i prod
Worked examples for the three usage modes
Mode 1: Zero config, public instance
# Explicit database, instance as flag
dclient databases -i https://latest.datasette.io
dclient tables -i https://latest.datasette.io -d fixtures
dclient query fixtures "select * from facetable limit 3" -i https://latest.datasette.io
# Or set env vars for a session
export DATASETTE_URL=https://latest.datasette.io
export DATASETTE_DATABASE=fixtures
dclient tables
dclient "select * from facetable limit 3"
Mode 2: Single default instance
# One-time setup
dclient alias add work https://myapp.datasette.cloud
dclient alias default work
dclient alias default-db work main
dclient auth add work
# Daily use — bare SQL shortcut uses defaults
dclient tables
dclient "select count(*) from users"
dclient "select * from events" -d analytics
# Explicit query when switching databases frequently
dclient query main "select count(*) from users"
dclient query analytics "select count(*) from events"
# Insert always names the database
dclient insert main events data.csv --csv --create --pk id
dclient plugins
Mode 3: Multiple aliases
dclient alias add prod https://prod.datasette.cloud
dclient alias add staging https://staging.datasette.cloud
dclient alias default prod
dclient alias default-db prod main
dclient alias default-db staging main
# Bare shortcut hits prod/main
dclient "select count(*) from users"
# Explicit query — name the database, override instance with -i
dclient query main "select count(*) from users" -i staging
dclient query analytics "select * from events" -i staging
# Insert always explicit
dclient insert main events data.csv --csv -i staging
Not in this version (reserved)
These command names are reserved for future work:
dclient cloud— Datasette Cloud integrationdclient files— datasette-files managementdclient get,dclient rows,dclient update,dclient delete— row-level CRUDdclient create-table,dclient drop-table— DDLdclient queries— canned queries