home / docs

docs

Custom SQL query returning 101 rows (hide)

This data as json, CSV

rowidtitlecontentsections_ftsrank
1 Performance and caching Datasette runs on top of SQLite, and SQLite has excellent performance. For small databases almost any query should return in just a few milliseconds, and larger databases (100s of MBs or even GBs of data) should perform extremely well provided your queries make sensible use of database indexes. That said, there are a number of tricks you can use to improve Datasette's performance. 59  
2 Immutable mode If you can be certain that a SQLite database file will not be changed by another process you can tell Datasette to open that file in immutable mode . Doing so will disable all locking and change detection, which can result in improved query performance. This also enables further optimizations relating to HTTP caching, described below. To open a file in immutable mode pass it to the datasette command using the -i option: datasette -i data.db When you open a file in immutable mode like this Datasette will also calculate and cache the row counts for each table in that database when it first starts up, further improving performance. 59  
3 Using "datasette inspect" Counting the rows in a table can be a very expensive operation on larger databases. In immutable mode Datasette performs this count only once and caches the results, but this can still cause server startup time to increase by several seconds or more. If you know that a database is never going to change you can precalculate the table row counts once and store then in a JSON file, then use that file when you later start the server. To create a JSON file containing the calculated row counts for a database, use the following: datasette inspect data.db --inspect-file=counts.json Then later you can start Datasette against the counts.json file and use it to skip the row counting step and speed up server startup: datasette -i data.db --inspect-file=counts.json You need to use the -i immutable mode against the database file here or the counts from the JSON file will be ignored. You will rarely need to use this optimization in every-day use, but several of the datasette publish commands described in Publishing data use this optimization for better performance when deploying a database file to a hosting provider. 59  
4 HTTP caching If your database is immutable and guaranteed not to change, you can gain major performance improvements from Datasette by enabling HTTP caching. This can work at two different levels. First, it can tell browsers to cache the results of queries and serve future requests from the browser cache. More significantly, it allows you to run Datasette behind a caching proxy such as Varnish or use a cache provided by a hosted service such as Fastly or Cloudflare . This can provide incredible speed-ups since a query only needs to be executed by Datasette the first time it is accessed - all subsequent hits can then be served by the cache. Using a caching proxy in this way could enable a Datasette-backed visualization to serve thousands of hits a second while running Datasette itself on extremely inexpensive hosting. Datasette's integration with HTTP caches can be enabled using a combination of configuration options and query string arguments. The default_cache_ttl setting sets the default HTTP cache TTL for all Datasette pages. This is 5 seconds unless you change it - you can set it to 0 if you wish to disable HTTP caching entirely. You can also change the cache timeout on a per-request basis using the ?_ttl=10 query string parameter. This can be useful when you are working with the Datasette JSON API - you may decide that a specific query can be cached for a longer time, or maybe you need to set ?_ttl=0 for some requests for example if you are running a SQL order by random() query. 59  
5 datasette-hashed-urls If you open a database file in immutable mode using the -i option, you can be assured that the content of that database will not change for the lifetime of the Datasette server. The datasette-hashed-urls plugin implements an optimization where your database is served with part of the SHA-256 hash of the database contents baked into the URL. A database at /fixtures will instead be served at /fixtures-aa7318b , and a year-long cache expiry header will be returned with those pages. This will then be cached by both browsers and caching proxies such as Cloudflare or Fastly, providing a potentially significant performance boost. To install the plugin, run the following: datasette install datasette-hashed-urls Prior to Datasette 0.61 hashed URL mode was a core Datasette feature, enabled using the hash_urls setting. This implementation has now been removed in favor of the datasette-hashed-urls plugin. Prior to Datasette 0.28 hashed URL mode was the default behaviour for Datasette, since all database files were assumed to be immutable and unchanging. From 0.28 onwards the default has been to treat database files as mutable unless explicitly configured otherwise. 59  
6 Settings   59  
7 Using --setting Datasette supports a number of settings. These can be set using the --setting name value option to datasette serve . You can set multiple settings at once like this: datasette mydatabase.db \ --setting default_page_size 50 \ --setting sql_time_limit_ms 3500 \ --setting max_returned_rows 2000 Settings can also be specified in the database.yaml configuration file . 59  
8 Configuration directory mode Normally you configure Datasette using command-line options. For a Datasette instance with custom templates, custom plugins, a static directory and several databases this can get quite verbose: datasette one.db two.db \ --metadata=metadata.json \ --template-dir=templates/ \ --plugins-dir=plugins \ --static css:css As an alternative to this, you can run Datasette in configuration directory mode. Create a directory with the following structure: # In a directory called my-app: my-app/one.db my-app/two.db my-app/datasette.yaml my-app/metadata.json my-app/templates/index.html my-app/plugins/my_plugin.py my-app/static/my.css Now start Datasette by providing the path to that directory: datasette my-app/ Datasette will detect the files in that directory and automatically configure itself using them. It will serve all *.db files that it finds, will load metadata.json if it exists, and will load the templates , plugins and static folders if they are present. The files that can be included in this directory are as follows. All are optional. *.db (or *.sqlite3 or *.sqlite ) - SQLite database files that will be served by Datasette datasette.yaml - Configuration for the Datasette instance metadata.json - Metadata for those databases - metadata.yaml or metadata.yml can be used as well inspect-data.json - the result of running datasette inspect *.db --inspect-file=inspect-data.json from the configuration directory - any database files listed here will be treated as immutable, so they should not be changed while Datasette is running templates/ - a directory containing Custom templates … 59  
9 Settings The following options can be set using --setting name value , or by storing them in the settings.json file for use with Configuration directory mode . 59  
10 default_allow_sql Should users be able to execute arbitrary SQL queries by default? Setting this to off causes permission checks for execute-sql to fail by default. datasette mydatabase.db --setting default_allow_sql off Another way to achieve this is to add "allow_sql": false to your datasette.yaml file, as described in Controlling the ability to execute arbitrary SQL . This setting offers a more convenient way to do this. 59  
11 default_page_size The default number of rows returned by the table page. You can over-ride this on a per-page basis using the ?_size=80 query string parameter, provided you do not specify a value higher than the max_returned_rows setting. You can set this default using --setting like so: datasette mydatabase.db --setting default_page_size 50 59  
12 sql_time_limit_ms By default, queries have a time limit of one second. If a query takes longer than this to run Datasette will terminate the query and return an error. If this time limit is too short for you, you can customize it using the sql_time_limit_ms limit - for example, to increase it to 3.5 seconds: datasette mydatabase.db --setting sql_time_limit_ms 3500 You can optionally set a lower time limit for an individual query using the ?_timelimit=100 query string argument: /my-database/my-table?qSpecies=44&_timelimit=100 This would set the time limit to 100ms for that specific query. This feature is useful if you are working with databases of unknown size and complexity - a query that might make perfect sense for a smaller table could take too long to execute on a table with millions of rows. By setting custom time limits you can execute queries "optimistically" - e.g. give me an exact count of rows matching this query but only if it takes less than 100ms to calculate. 59  
13 max_returned_rows Datasette returns a maximum of 1,000 rows of data at a time. If you execute a query that returns more than 1,000 rows, Datasette will return the first 1,000 and include a warning that the result set has been truncated. You can use OFFSET/LIMIT or other methods in your SQL to implement pagination if you need to return more than 1,000 rows. You can increase or decrease this limit like so: datasette mydatabase.db --setting max_returned_rows 2000 59  
14 max_insert_rows Maximum rows that can be inserted at a time using the bulk insert API, see Inserting rows . Defaults to 100. You can increase or decrease this limit like so: datasette mydatabase.db --setting max_insert_rows 1000 59  
15 num_sql_threads Maximum number of threads in the thread pool Datasette uses to execute SQLite queries. Defaults to 3. datasette mydatabase.db --setting num_sql_threads 10 Setting this to 0 turns off threaded SQL queries entirely - useful for environments that do not support threading such as Pyodide . 59  
16 allow_facet Allow users to specify columns they would like to facet on using the ?_facet=COLNAME URL parameter to the table view. This is enabled by default. If disabled, facets will still be displayed if they have been specifically enabled in metadata.json configuration for the table. Here's how to disable this feature: datasette mydatabase.db --setting allow_facet off 59  
17 default_facet_size The default number of unique rows returned by Facets is 30. You can customize it like this: datasette mydatabase.db --setting default_facet_size 50 59  
18 facet_time_limit_ms This is the time limit Datasette allows for calculating a facet, which defaults to 200ms: datasette mydatabase.db --setting facet_time_limit_ms 1000 59  
19 facet_suggest_time_limit_ms When Datasette calculates suggested facets it needs to run a SQL query for every column in your table. The default for this time limit is 50ms to account for the fact that it needs to run once for every column. If the time limit is exceeded the column will not be suggested as a facet. You can increase this time limit like so: datasette mydatabase.db --setting facet_suggest_time_limit_ms 500 59  
20 suggest_facets Should Datasette calculate suggested facets? On by default, turn this off like so: datasette mydatabase.db --setting suggest_facets off 59  
21 allow_download Should users be able to download the original SQLite database using a link on the database index page? This is turned on by default. However, databases can only be downloaded if they are served in immutable mode and not in-memory. If downloading is unavailable for either of these reasons, the download link is hidden even if allow_download is on. To disable database downloads, use the following: datasette mydatabase.db --setting allow_download off 59  
22 allow_signed_tokens Should users be able to create signed API tokens to access Datasette? This is turned on by default. Use the following to turn it off: datasette mydatabase.db --setting allow_signed_tokens off Turning this setting off will disable the /-/create-token page, described here . It will also cause any incoming Authorization: Bearer dstok_... API tokens to be ignored. 59  
23 max_signed_tokens_ttl Maximum allowed expiry time for signed API tokens created by users. Defaults to 0 which means no limit - tokens can be created that will never expire. Set this to a value in seconds to limit the maximum expiry time. For example, to set that limit to 24 hours you would use: datasette mydatabase.db --setting max_signed_tokens_ttl 86400 This setting is enforced when incoming tokens are processed. 59  
24 default_cache_ttl Default HTTP caching max-age header in seconds, used for Cache-Control: max-age=X . Can be over-ridden on a per-request basis using the ?_ttl= query string parameter. Set this to 0 to disable HTTP caching entirely. Defaults to 5 seconds. datasette mydatabase.db --setting default_cache_ttl 60 59  
25 cache_size_kb Sets the amount of memory SQLite uses for its per-connection cache , in KB. datasette mydatabase.db --setting cache_size_kb 5000 59  
26 allow_csv_stream Enables the CSV export feature where an entire table (potentially hundreds of thousands of rows) can be exported as a single CSV file. This is turned on by default - you can turn it off like this: datasette mydatabase.db --setting allow_csv_stream off 59  
27 max_csv_mb The maximum size of CSV that can be exported, in megabytes. Defaults to 100MB. You can disable the limit entirely by settings this to 0: datasette mydatabase.db --setting max_csv_mb 0 59  
28 truncate_cells_html In the HTML table view, truncate any strings that are longer than this value. The full value will still be available in CSV, JSON and on the individual row HTML page. Set this to 0 to disable truncation. datasette mydatabase.db --setting truncate_cells_html 0 59  
29 force_https_urls Forces self-referential URLs in the JSON output to always use the https:// protocol. This is useful for cases where the application itself is hosted using HTTP but is served to the outside world via a proxy that enables HTTPS. datasette mydatabase.db --setting force_https_urls 1 59  
30 template_debug This setting enables template context debug mode, which is useful to help understand what variables are available to custom templates when you are writing them. Enable it like this: datasette mydatabase.db --setting template_debug 1 Now you can add ?_context=1 or &_context=1 to any Datasette page to see the context that was passed to that template. Some examples: https://latest.datasette.io/?_context=1 https://latest.datasette.io/fixtures?_context=1 https://latest.datasette.io/fixtures/roadside_attractions?_context=1 59  
31 trace_debug This setting enables appending ?_trace=1 to any page in order to see the SQL queries and other trace information that was used to generate that page. Enable it like this: datasette mydatabase.db --setting trace_debug 1 Some examples: https://latest.datasette.io/?_trace=1 https://latest.datasette.io/fixtures/roadside_attractions?_trace=1 See datasette.tracer for details on how to hook into this mechanism as a plugin author. 59  
32 base_url If you are running Datasette behind a proxy, it may be useful to change the root path used for the Datasette instance. For example, if you are sending traffic from https://www.example.com/tools/datasette/ through to a proxied Datasette instance you may wish Datasette to use /tools/datasette/ as its root URL. You can do that like so: datasette mydatabase.db --setting base_url /tools/datasette/ 59  
33 Configuring the secret Datasette uses a secret string to sign secure values such as cookies. If you do not provide a secret, Datasette will create one when it starts up. This secret will reset every time the Datasette server restarts though, so things like authentication cookies and API tokens will not stay valid between restarts. You can pass a secret to Datasette in two ways: with the --secret command-line option or by setting a DATASETTE_SECRET environment variable. datasette mydb.db --secret=SECRET_VALUE_HERE Or: export DATASETTE_SECRET=SECRET_VALUE_HERE datasette mydb.db One way to generate a secure random secret is to use Python like this: python3 -c 'import secrets; print(secrets.token_hex(32))' cdb19e94283a20f9d42cca50c5a4871c0aa07392db308755d60a1a5b9bb0fa52 Plugin authors can make use of this signing mechanism in their plugins using the datasette.sign() and datasette.unsign() methods. 59  
34 Using secrets with datasette publish The datasette publish and datasette package commands both generate a secret for you automatically when Datasette is deployed. This means that every time you deploy a new version of a Datasette project, a new secret will be generated. This will cause signed cookies to become invalid on every fresh deploy. You can fix this by creating a secret that will be used for multiple deploys and passing it using the --secret option: datasette publish cloudrun mydb.db --service=my-service --secret=cdb19e94283a20f9d42cca5 59  
35 Plugins Datasette's plugin system allows additional features to be implemented as Python code (or front-end JavaScript) which can be wrapped up in a separate Python package. The underlying mechanism uses pluggy . See the Datasette plugins directory for a list of existing plugins, or take a look at the datasette-plugin topic on GitHub. Things you can do with plugins include: Add visualizations to Datasette, for example datasette-cluster-map and datasette-vega . Make new custom SQL functions available for use within Datasette, for example datasette-haversine and datasette-jellyfish . Define custom output formats with custom extensions, for example datasette-atom and datasette-ics . Add template functions that can be called within your Jinja custom templates, for example datasette-render-markdown . Customize how database values are rendered in the Datasette interface, for example datasette-render-binary and datasette-pretty-json . Customize how Datasette's authentication and permissions systems work, for example datasette-auth-passwords and datasette-permissions-sql . 59  
36 Installing plugins If a plugin has been packaged for distribution using setuptools you can use the plugin by installing it alongside Datasette in the same virtual environment or Docker container. You can install plugins using the datasette install command: datasette install datasette-vega You can uninstall plugins with datasette uninstall : datasette uninstall datasette-vega You can upgrade plugins with datasette install --upgrade or datasette install -U : datasette install -U datasette-vega This command can also be used to upgrade Datasette itself to the latest released version: datasette install -U datasette You can install multiple plugins at once by listing them as lines in a requirements.txt file like this: datasette-vega datasette-cluster-map Then pass that file to datasette install -r : datasette install -r requirements.txt The install and uninstall commands are thin wrappers around pip install and pip uninstall , which ensure that they run pip in the same virtual environment as Datasette itself. 59  
37 One-off plugins using --plugins-dir You can also define one-off per-project plugins by saving them as plugin_name.py functions in a plugins/ folder and then passing that folder to datasette using the --plugins-dir option: datasette mydb.db --plugins-dir=plugins/ 59  
38 Deploying plugins using datasette publish The datasette publish and datasette package commands both take an optional --install argument. You can use this one or more times to tell Datasette to pip install specific plugins as part of the process: datasette publish cloudrun mydb.db --install=datasette-vega You can use the name of a package on PyPI or any of the other valid arguments to pip install such as a URL to a .zip file: datasette publish cloudrun mydb.db \ --install=https://url-to-my-package.zip 59  
39 Controlling which plugins are loaded Datasette defaults to loading every plugin that is installed in the same virtual environment as Datasette itself. You can set the DATASETTE_LOAD_PLUGINS environment variable to a comma-separated list of plugin names to load a controlled subset of plugins instead. For example, to load just the datasette-vega and datasette-cluster-map plugins, set DATASETTE_LOAD_PLUGINS to datasette-vega,datasette-cluster-map : export DATASETTE_LOAD_PLUGINS='datasette-vega,datasette-cluster-map' datasette mydb.db Or: DATASETTE_LOAD_PLUGINS='datasette-vega,datasette-cluster-map' \ datasette mydb.db To disable the loading of all additional plugins, set DATASETTE_LOAD_PLUGINS to an empty string: export DATASETTE_LOAD_PLUGINS='' datasette mydb.db A quick way to test this setting is to use it with the datasette plugins command: DATASETTE_LOAD_PLUGINS='datasette-vega' datasette plugins This should output the following: [ { "name": "datasette-vega", "static": true, "templates": false, "version": "0.6.2", "hooks": [ "extra_css_urls", "extra_js_urls" ] } ] 59  
40 Seeing what plugins are installed You can see a list of installed plugins by navigating to the /-/plugins page of your Datasette instance - for example: https://fivethirtyeight.datasettes.com/-/plugins You can also use the datasette plugins command: datasette plugins Which outputs: [ { "name": "datasette_json_html", "static": false, "templates": false, "version": "0.4.0" } ] [[[cog from datasette import cli from click.testing import CliRunner import textwrap, json cog.out("\n") result = CliRunner().invoke(cli.cli, ["plugins", "--all"]) # cog.out() with text containing newlines was unindenting for some reason cog.outl("If you run ``datasette plugins --all`` it will include default plugins that ship as part of Datasette:\n") cog.outl(".. code-block:: json\n") plugins = [p for p in json.loads(result.output) if p["name"].startswith("datasette.")] indented = textwrap.indent(json.dumps(plugins, indent=4), " ") for line in indented.split("\n"): cog.outl(line) cog.out("\n\n") ]]] If you run datasette plugins --all it will include default plugins that ship as part of Datasette: [ { "name": "datasette.actor_auth_cookie", "static": false, "templates": false, "version": null, "hooks": [ "actor_from_request" ] }, { "name": "datasette.blob_renderer", "static": false, "templates": false, "version": null, "hooks": [ "register_output_renderer" ] }, { "name": "datasette.default_actions", "static": false, "templates": false, "version": null, "hooks": [ "register_actions" ] }, { "name": "datasette.default_magic_parameters", "static": false, "templates": false, "version": null, "hooks": [ "register_magic_parameters" ] }, { "n… 59  
41 Plugin configuration Plugins can have their own configuration, embedded in a configuration file . Configuration options for plugins live within a "plugins" key in that file, which can be included at the root, database or table level. Here is an example of some plugin configuration for a specific table: [[[cog from metadata_doc import config_example config_example(cog, { "databases": { "sf-trees": { "tables": { "Street_Tree_List": { "plugins": { "datasette-cluster-map": { "latitude_column": "lat", "longitude_column": "lng" } } } } } } }) ]]] [[[end]]] This tells the datasette-cluster-map column which latitude and longitude columns should be used for a table called Street_Tree_List inside a database file called sf-trees.db . 59  
42 Secret configuration values Some plugins may need configuration that should stay secret - API keys for example. There are two ways in which you can store secret configuration values. As environment variables . If your secret lives in an environment variable that is available to the Datasette process, you can indicate that the configuration value should be read from that environment variable like so: [[[cog config_example(cog, { "plugins": { "datasette-auth-github": { "client_secret": { "$env": "GITHUB_CLIENT_SECRET" } } } }) ]]] [[[end]]] As values in separate files . Your secrets can also live in files on disk. To specify a secret should be read from a file, provide the full file path like this: [[[cog config_example(cog, { "plugins": { "datasette-auth-github": { "client_secret": { "$file": "/secrets/client-secret" } } } }) ]]] [[[end]]] If you are publishing your data using the datasette publish family of commands, you can use the --plugin-secret option to set these secrets at publish time. For example, using Heroku you might run the following command: datasette publish heroku my_database.db \ --name my-heroku-app-demo \ --install=datasette-auth-github \ --plugin-secret datasette-auth-github client_id your_client_id \ --plugin-secret datasette-auth-github client_secret your_client_secret This will set the necessary environment variables and add the following to the deployed metadata.yaml : [[[cog config_example(cog, { "plugins": { "datasette-auth-github": { "client_id": { "$env": "DATASETTE_AUTH_GITHUB_CLIENT_ID" }, "client_secret": { "$env": "DATASETTE_AUTH_GITHUB_CLIENT_SECRET" } … 59  
43 Changelog   59  
44 1.0a25 (2026-02-25)   59  
45   A new write_wrapper() plugin hook allows plugins to intercept and wrap database write operations. ( #2636 ) Plugins implement the hook as a generator-based context manager: @hookimpl def write_wrapper(datasette, database, request): def wrapper(conn): # Setup code runs before the write yield # Cleanup code runs after the write return wrapper 59  
46   A new register_token_handler() plugin hook allows plugins to provide custom token backends for API authentication. ( #2650 ) This includes a backwards incompatible change : the datasette.create_token() internal method is now an async method. Consult the upgrade guide for details on how to update your code. 59  
47   The render_cell() plugin hook now receives a pks parameter containing the list of primary key column names for the table being rendered. This avoids plugins needing to make redundant async calls to look up primary keys. ( #2641 ) 59  
48 Other changes Facets defined in metadata now preserve their configured order, instead of being sorted by result count. Request-based facets added via the _facet parameter are still sorted by result count and appear after metadata-defined facets. ( #2647 ) Fixed --reload incorrectly interpreting the serve command as a file argument. Thanks, Daniel Bates . ( #2646 ) 59  
49 1.0a24 (2026-01-29)   59  
50   Datasette now includes a request.form() method for parsing form submissions, including handling file uploads. ( #2626 ) This supports both application/x-www-form-urlencoded and multipart/form-data content types, and uses a new streaming multipart parser that processes uploads without buffering entire request bodies in memory. # Parse form fields (files are discarded by default) form = await request.form() username = form["username"] # Parse form fields AND file uploads form = await request.form(files=True) uploaded = form["avatar"] content = await uploaded.read() The returned FormData object provides dictionary-style access with support for multiple values per key via form.getlist("key") . Uploaded files are represented as UploadedFile objects with filename , content_type , size properties and async read() and seek() methods. Files smaller than 1MB are held in memory; larger files automatically spill to temporary files on disk. Configurable limits control maximum file size, request size, field counts and more. Several internal views (permissions debug, messages debug, create token) now use request.form() instead of request.post_vars() . request.post_vars() remains available for backwards compatibility but is no longer the recommended API for handling POST data. 59  
51   The table JSON API now supports ?_extra=render_cell , which returns the rendered HTML for each cell as produced by the render_cell plugin hook . Only columns whose rendered output differs from the default are included. ( #2619 ) The row JSON API also gains ?_extra=render_cell and ?_extra=foreign_key_tables extras, bringing it closer to parity with the table API. The row JSON API now returns "ok": true in its response, for consistency with the table API. 59  
52   The recommended development environment for Datasette now uses uv . You can now set up a development environment and run the test suite with just uv run pytest — no manual virtualenv or pip install step required. ( #2611 ) 59  
53 Other changes Plugins that raise datasette.utils.StartupError() during startup now display a clean error message instead of a full traceback. ( #2624 ) Schema refreshes are now throttled to at most once per second, providing a small performance increase. ( #2629 ) Minor performance improvement to remove_infinites — rows without infinity values now skip the list/dict reconstruction step. ( #2629 ) Filter inputs and the search input no longer trigger unwanted zoom on iOS Safari. Thanks, Daniel Olasubomi Sobowale . ( #2346 ) table_names() and get_all_foreign_keys() now return results in deterministic sorted order. ( #2628 ) Switched linting to ruff and fixed all lint errors. ( #2630 ) 59  
54 1.0a23 (2025-12-02) Fix for bug where a stale database entry in internal.db could cause a 500 error on the homepage. ( #2605 ) Cosmetic improvement to /-/actions page. ( #2599 ) 59  
55 1.0a22 (2025-11-13) datasette serve --default-deny option for running Datasette configured to deny all permissions by default . ( #2592 ) datasette.is_client() method for detecting if code is executing inside a datasette.client request . ( #2594 ) datasette.pm property can now be used to register and unregister plugins in tests . ( #2595 ) 59  
56 1.0a21 (2025-11-05) Fixes an open redirect security issue: Datasette instances would redirect to example.com/foo/bar if you accessed the path //example.com/foo/bar . Thanks to James Jefferies for the fix. ( #2429 ) Fixed datasette publish cloudrun to work with changes to the underlying Cloud Run architecture. ( #2511 ) New datasette --get /path --headers option for inspecting the headers returned by a path. ( #2578 ) New datasette.client.get(..., skip_permission_checks=True) parameter to bypass permission checks when making requests using the internal client. ( #2583 ) 59  
57 0.65.2 (2025-11-05) Fixes an open redirect security issue: Datasette instances would redirect to example.com/foo/bar if you accessed the path //example.com/foo/bar . Thanks to James Jefferies for the fix. ( #2429 ) Upgraded for compatibility with Python 3.14. Fixed datasette publish cloudrun to work with changes to the underlying Cloud Run architecture. ( #2511 ) Minor upgrades to fix warnings, including pkg_resources deprecation. 59  
58 1.0a20 (2025-11-03) This alpha introduces a major breaking change prior to the 1.0 release of Datasette concerning how Datasette's permission system works. 59  
59 Permission system redesign Previously the permission system worked using datasette.permission_allowed() checks which consulted all available plugins in turn to determine whether a given actor was allowed to perform a given action on a given resource. This approach could become prohibitively expensive for large lists of items - for example to determine the list of tables that a user could view in a large Datasette instance each plugin implementation of that hook would be fired for every table. The new design uses SQL queries against Datasette's internal catalog tables to derive the list of resources for which an actor has permission for a given action. This turns an N x M problem (N resources, M plugins) into a single SQL query. Plugins can use the new permission_resources_sql(datasette, actor, action) hook to return SQL fragments which will be used as part of that query. Plugins that use any of the following features will need to be updated to work with this and following alphas (and Datasette 1.0 stable itself): Checking permissions with datasette.permission_allowed() - this method has been replaced with datasette.allowed() . Implementing the permission_allowed() plugin hook - this hook has been removed in favor of permission_resources_sql() . Using register_permissions() to register permissions - this hook has been removed in favor of register_actions() . Consult the v1.0a20 upgrade guide for further details on how to upgrade affected plugins. Plugins can now make use of two new internal methods to help resolve permission checks: datasette.allowed_resources() returns a PaginatedResources object with a .re… 59  
60 Other changes The internal catalog_views table now tracks SQLite views alongside tables in the introspection database. ( #2495 ) Hitting the / brings up a search interface for navigating to tables that the current user can view. A new /-/tables endpoint supports this functionality. ( #2523 ) Datasette attempts to detect some configuration errors on startup. Datasette now supports Python 3.14 and no longer tests against Python 3.9. 59  
61 1.0a19 (2025-04-21) Tiny cosmetic bug fix for mobile display of table rows. ( #2479 ) 59  
62 1.0a18 (2025-04-16) Fix for incorrect foreign key references in the internal database schema. ( #2466 ) The prepare_connection() hook no longer runs for the internal database. ( #2468 ) Fixed bug where link: HTTP headers used invalid syntax. ( #2470 ) No longer tested against Python 3.8. Now tests against Python 3.13. FTS tables are now hidden by default if they correspond to a content table. ( #2477 ) Fixed bug with foreign key links to rows in databases with filenames containing a special character. Thanks, Jack Stratton . ( #2476 ) 59  
63 1.0a17 (2025-02-06) DATASETTE_SSL_KEYFILE and DATASETTE_SSL_CERTFILE environment variables as alternatives to --ssl-keyfile and --ssl-certfile . Thanks, Alex Garcia. ( #2422 ) SQLITE_EXTENSIONS environment variable has been renamed to DATASETTE_LOAD_EXTENSION . ( #2424 ) datasette serve environment variables are now documented here . The register_magic_parameters(datasette) plugin hook can now register async functions. ( #2441 ) Datasette is now tested against Python 3.13. Breadcrumbs on database and table pages now include a consistent self-link for resetting query string parameters. ( #2454 ) Fixed issue where Datasette could crash on metadata.json with nested values. ( #2455 ) New internal methods datasette.set_actor_cookie() and datasette.delete_actor_cookie() , described here . ( #1690 ) /-/permissions page now shows a list of all permissions registered by plugins. ( #1943 ) If a table has a single unique text column Datasette now detects that as the foreign key label for that table. ( #2458 ) The /-/permissions page now includes options for filtering or exclude permission checks recorded against the current user. ( #2460 ) Fixed a bug where replacing a database with a new one with the same name did not pick up the new database correctly. ( #2465 ) 59  
64 0.65.1 (2024-11-28) Fixed bug with upgraded HTTPX 0.28.0 dependency. ( #2443 ) 59  
65 0.65 (2024-10-07) Upgrade for compatibility with Python 3.13 (by vendoring Pint dependency). ( #2434 ) Dropped support for Python 3.8. 59  
66 1.0a16 (2024-09-05) This release focuses on performance, in particular against large tables, and introduces some minor breaking changes for CSS styling in Datasette plugins. Removed the unit conversions feature and its dependency, Pint. This means Datasette is now compatible with the upcoming Python 3.13. ( #2400 , #2320 ) The datasette --pdb option now uses the ipdb debugger if it is installed. You can install it using datasette install ipdb . Thanks, Tiago Ilieve . ( #2342 ) Fixed a confusing error that occurred if metadata.json contained nested objects. ( #2403 ) Fixed a bug with ?_trace=1 where it returned a blank page if the response was larger than 256KB. ( #2404 ) Tracing mechanism now also displays SQL queries that returned errors or ran out of time. datasette-pretty-traces 0.5 includes support for displaying this new type of trace. ( #2405 ) Fixed a text spacing with table descriptions on the homepage. ( #2399 ) Performance improvements for large tables: Suggested facets now only consider the first 1000 rows. ( #2406 ) Improved performance of date facet suggestion against large tables. ( #2407 ) Row counts stop at 10,000 rows when listing tables. ( #2398 ) … 59  
67 1.0a15 (2024-08-15) Datasette now defaults to hiding SQLite "shadow" tables, as seen in extensions such as SQLite FTS and sqlite-vec . Virtual tables that it makes sense to display, such as FTS core tables, are no longer hidden. Thanks, Alex Garcia . ( #2296 ) Fixed bug where running Datasette with one or more -s/--setting options could over-ride settings that were present in datasette.yml . ( #2389 ) The Datasette homepage is now duplicated at /-/ , using the default index.html template. This ensures that the information on that page is still accessible even if the Datasette homepage has been customized using a custom index.html template, for example on sites like datasette.io . ( #2393 ) Failed CSRF checks now display a more user-friendly error page. ( #2390 ) Fixed a bug where the json1 extension was not correctly detected on the /-/versions page. Thanks, Seb Bacon . ( #2326 ) Fixed a bug where the Datasette write API did not correctly accept Content-Type: application/json; charset=utf-8 . ( #2384 ) Fixed a bug where Datasette would fail to start if metadata.yml contained a queries block. ( #2386 ) 59  
68 1.0a14 (2024-08-05) This alpha introduces significant changes to Datasette's Metadata system, some of which represent breaking changes in advance of the full 1.0 release. The new Upgrade guide document provides detailed coverage of those breaking changes and how they affect plugin authors and Datasette API consumers. The /databasename?sql= interface and JSON API for executing arbitrary SQL queries can now be found at /databasename/-/query?sql= . Requests with a ?sql= parameter to the old endpoints will be redirected. Thanks, Alex Garcia . ( #2360 ) Metadata about tables, databases, instances and columns is now stored in Datasette's internal database . Thanks, Alex Garcia. ( #2341 ) Database write connections now execute using the IMMEDIATE isolation level for SQLite. This should help avoid a rare SQLITE_BUSY error that could occur when a transaction upgraded to a write mid-flight. ( #2358 ) Fix for a bug where canned queries with named parameters could fail against SQLite 3.46. ( #2353 ) Datasette now serves E-Tag headers for static files. Thanks, Agustin Bacigalup . ( #2306 ) Dropdown menus now use a z-index that should avoid them being hidden by plugins. ( #2311 ) Incorrect table and row names are no longer reflected back on the resulting 404 page. ( #2359 ) Improved documentation for async usage of the track_event(datasette, event) hook. ( #2319 ) Fixed some HTTPX deprecation warnings. ( #2307 ) Datasette now serves a <html lang="en"> attrib… 59  
69 0.64.8 (2024-06-21) Security improvement: 404 pages used to reflect content from the URL path, which could be used to display misleading information to Datasette users. 404 errors no longer display additional information from the URL. ( #2359 ) Backported a better fix for correctly extracting named parameters from canned query SQL against SQLite 3.46.0. ( #2353 ) 59  
70 0.64.7 (2024-06-12) Fixed a bug where canned queries with named parameters threw an error when run against SQLite 3.46.0. ( #2353 ) 59  
71 1.0a13 (2024-03-12) Each of the key concepts in Datasette now has an actions menu , which plugins can use to add additional functionality targeting that entity. Plugin hook: view_actions() for actions that can be applied to a SQL view. ( #2297 ) Plugin hook: homepage_actions() for actions that apply to the instance homepage. ( #2298 ) Plugin hook: row_actions() for actions that apply to the row page. ( #2299 ) Action menu items for all of the *_actions() plugin hooks can now return an optional "description" key, which will be displayed in the menu below the action label. ( #2294 ) Plugin hooks documentation page is now organized with additional headings. ( #2300 ) Improved the display of action buttons on pages that also display metadata. ( #2286 ) The header and footer of the page now uses a subtle gradient effect, and options in the navigation menu are better visually defined. ( #2302 ) Table names that start with an underscore now default to hidden. ( #2104 ) pragma_table_list has been added to the allow-list of SQLite pragma functions supported by Datasette. select * from pragma_table_list() is no longer blocked. ( #2104 ) 59  
72 1.0a12 (2024-02-29) New query_actions() plugin hook, similar to table_actions() and database_actions() . Can be used to add a menu of actions to the canned query or arbitrary SQL query page. ( #2283 ) New design for the button that opens the query, table and database actions menu. ( #2281 ) "does not contain" table filter for finding rows that do not contain a string. ( #2287 ) Fixed a bug in the makeColumnActions(columnDetails) JavaScript plugin mechanism where the column action menu was not fully reset in between each interaction. ( #2289 ) 59  
73 1.0a11 (2024-02-19) The "replace": true argument to the /db/table/-/insert API now requires the actor to have the update-row permission. ( #2279 ) Fixed some UI bugs in the interactive permissions debugging tool. ( #2278 ) The column action menu now aligns better with the cog icon, and positions itself taking into account the width of the browser window. ( #2263 ) 59  
74 1.0a10 (2024-02-17) The only changes in this alpha correspond to the way Datasette handles database transactions. ( #2277 ) The database.execute_write_fn() method has a new transaction=True parameter. This defaults to True which means all functions executed using this method are now automatically wrapped in a transaction - previously the functions needed to roll transaction handling on their own, and many did not. Pass transaction=False to execute_write_fn() if you want to manually handle transactions in your function. Several internal Datasette features, including parts of the JSON write API , had been failing to wrap their operations in a transaction. This has been fixed by the new transaction=True default. 59  
75 1.0a9 (2024-02-16) This alpha release adds basic alter table support to the Datasette Write API and fixes a permissions bug relating to the /upsert API endpoint. 59  
76 Alter table support for create, insert, upsert and update The JSON write API can now be used to apply simple alter table schema changes, provided the acting actor has the new alter-table permission. ( #2101 ) The only alter operation supported so far is adding new columns to an existing table. The /db/-/create API now adds new columns during large operations to create a table based on incoming example "rows" , in the case where one of the later rows includes columns that were not present in the earlier batches. This requires the create-table but not the alter-table permission. When /db/-/create is called with rows in a situation where the table may have been already created, an "alter": true key can be included to indicate that any missing columns from the new rows should be added to the table. This requires the alter-table permission. /db/table/-/insert and /db/table/-/upsert and /db/table/row-pks/-/update all now also accept "alter": true , depending on the alter-table permission. Operations that alter a table now fire the new alter-table event . 59  
77 Permissions fix for the upsert API The /database/table/-/upsert API had a minor permissions bug, only affecting Datasette instances that had configured the insert-row and update-row permissions to apply to a specific table rather than the database or instance as a whole. Full details in issue #2262 . To avoid similar mistakes in the future the datasette.permission_allowed() method now specifies default= as a keyword-only argument. 59  
78 Permission checks now consider opinions from every plugin The datasette.permission_allowed() method previously consulted every plugin that implemented the permission_allowed() plugin hook and obeyed the opinion of the last plugin to return a value. ( #2275 ) Datasette now consults every plugin and checks to see if any of them returned False (the veto rule), and if none of them did, it then checks to see if any of them returned True . This is explained at length in the new documentation covering How permissions are resolved . 59  
79 Other changes The new DATASETTE_TRACE_PLUGINS=1 environment variable turns on detailed trace output for every executed plugin hook, useful for debugging and understanding how the plugin system works at a low level. ( #2274 ) Datasette on Python 3.9 or above marks its non-cryptographic uses of the MD5 hash function as usedforsecurity=False , for compatibility with FIPS systems. ( #2270 ) SQL relating to Datasette's internal database now executes inside a transaction, avoiding a potential database locked error. ( #2273 ) The /-/threads debug page now identifies the database in the name associated with each dedicated write thread. ( #2265 ) The /db/-/create API now fires a insert-rows event if rows were inserted after the table was created. ( #2260 ) 59  
80 1.0a8 (2024-02-07) This alpha release continues the migration of Datasette's configuration from metadata.yaml to the new datasette.yaml configuration file, introduces a new system for JavaScript plugins and adds several new plugin hooks. See Datasette 1.0a8: JavaScript plugins, new plugin hooks and plugin configuration in datasette.yaml for an annotated version of these release notes. 59  
81 Configuration Plugin configuration now lives in the datasette.yaml configuration file , passed to Datasette using the -c/--config option. Thanks, Alex Garcia. ( #2093 ) datasette -c datasette.yaml Where datasette.yaml contains configuration that looks like this: plugins: datasette-cluster-map: latitude_column: xlat longitude_column: xlon Previously plugins were configured in metadata.yaml , which was confusing as plugin settings were unrelated to database and table metadata. The -s/--setting option can now be used to set plugin configuration as well. See Configuration via the command-line for details. ( #2252 ) The above YAML configuration example using -s/--setting looks like this: datasette mydatabase.db \ -s plugins.datasette-cluster-map.latitude_column xlat \ -s plugins.datasette-cluster-map.longitude_column xlon The new /-/config page shows the current instance configuration, after redacting keys that could contain sensitive data such as API keys or passwords. ( #2254 ) Existing Datasette installations may already have configuration set in metadata.yaml that should be migrated to datasette.yaml . To avoid breaking these installations, Datasette will silently treat table configuration, plugin configuration and allow blocks in metadata as if they had been specified in configuration instead. ( #2247 ) ( #2248 ) ( #2249 ) Note that the datasette publish command has not yet been updated to accept a datasette.yaml configuration file. This will be addressed in #2195 but for the moment you can include those settings in metadata.yaml instead. 59  
82 JavaScript plugins Datasette now includes a JavaScript plugins mechanism , allowing JavaScript to customize Datasette in a way that can collaborate with other plugins. This provides two initial hooks, with more to come in the future: makeAboveTablePanelConfigs() can add additional panels to the top of the table page. makeColumnActions() can add additional actions to the column menu. Thanks Cameron Yick for contributing this feature. ( #2052 ) 59  
83 Plugin hooks New jinja2_environment_from_request(datasette, request, env) plugin hook, which can be used to customize the current Jinja environment based on the incoming request. This can be used to modify the template lookup path based on the incoming request hostname, among other things. ( #2225 ) New family of template slot plugin hooks : top_homepage , top_database , top_table , top_row , top_query , top_canned_query . Plugins can use these to provide additional HTML to be injected at the top of the corresponding pages. ( #1191 ) New track_event() mechanism for plugins to emit and receive events when certain events occur within Datasette. ( #2240 ) Plugins can register additional event classes using register_events(datasette) . They can then trigger those events with the datasette.track_event(event) internal method. Plugins can subscribe to notifications of events using the track_event(datasette, event) plugin hook. Datasette core now emits login , logout , create-token , create-table , drop-table , insert-rows , upsert-rows , update-row , delete-row events, documented here . … 59  
84 Documentation Documentation describing how to write tests that use signed actor cookies using datasette.client.actor_cookie() . ( #1830 ) Documentation on how to register a plugin for the duration of a test . ( #2234 ) The configuration documentation now shows examples of both YAML and JSON for each setting. 59  
85 Minor fixes Datasette no longer attempts to run SQL queries in parallel when rendering a table page, as this was leading to some rare crashing bugs. ( #2189 ) Fixed warning: DeprecationWarning: pkg_resources is deprecated as an API ( #2057 ) Fixed bug where ?_extra=columns parameter returned an incorrectly shaped response. ( #2230 ) 59  
86 0.64.6 (2023-12-22) Fixed a bug where CSV export with expanded labels could fail if a foreign key reference did not correctly resolve. ( #2214 ) 59  
87 0.64.5 (2023-10-08) Dropped dependency on click-default-group-wheel , which could cause a dependency conflict. ( #2197 ) 59  
88 1.0a7 (2023-09-21) Fix for a crashing bug caused by viewing the table page for a named in-memory database. ( #2189 ) 59  
89 0.64.4 (2023-09-21) Fix for a crashing bug caused by viewing the table page for a named in-memory database. ( #2189 ) 59  
90 1.0a6 (2023-09-07) New plugin hook: actors_from_ids(datasette, actor_ids) and an internal method to accompany it, await .actors_from_ids(actor_ids) . This mechanism is intended to be used by plugins that may need to display the actor who was responsible for something managed by that plugin: they can now resolve the recorded IDs of actors into the full actor objects. ( #2181 ) DATASETTE_LOAD_PLUGINS environment variable for controlling which plugins are loaded by Datasette. ( #2164 ) Datasette now checks if the user has permission to view a table linked to by a foreign key before turning that foreign key into a clickable link. ( #2178 ) The execute-sql permission now implies that the actor can also view the database and instance. ( #2169 ) Documentation describing a pattern for building plugins that themselves define further hooks for other plugins. ( #1765 ) Datasette is now tested against the Python 3.12 preview. ( #2175 ) 59  
91 1.0a5 (2023-08-29) When restrictions are applied to API tokens , those restrictions now behave slightly differently: applying the view-table restriction will imply the ability to view-database for the database containing that table, and both view-table and view-database will imply view-instance . Previously you needed to create a token with restrictions that explicitly listed view-instance and view-database and view-table in order to view a table without getting a permission denied error. ( #2102 ) New datasette.yaml (or .json ) configuration file, which can be specified using datasette -c path-to-file . The goal here to consolidate settings, plugin configuration, permissions, canned queries, and other Datasette configuration into a single single file, separate from metadata.yaml . The legacy settings.json config file used for Configuration directory mode has been removed, and datasette.yaml has a "settings" section where the same settings key/value pairs can be included. In the next future alpha release, more configuration such as plugins/permissions/canned queries will be moved to the datasette.yaml file. See #2093 for more details. Thanks, Alex Garcia. The -s/--setting option can now take dotted paths to nested settings. These will then be used to set or over-ride the same options as are present in the new configuration file. ( #2156 ) New --actor '{"id": "json-goes-here"}' option for use with datasette --get to treat the simulated request as being made by a specific actor, see datasette --get . ( #2153 ) The Datasette _internal database has had some changes. It no longer shows up in the datasette.databases list by default, and is now instead available to plugins using the datasette.get_internal_database() . Plugins are invited to use this as a private data… 59  
92 1.0a4 (2023-08-21) This alpha fixes a security issue with the /-/api API explorer. On authenticated Datasette instances (instances protected using plugins such as datasette-auth-passwords ) the API explorer interface could reveal the names of databases and tables within the protected instance. The data stored in those tables was not revealed. For more information and workarounds, read the security advisory . The issue has been present in every previous alpha version of Datasette 1.0: versions 1.0a0, 1.0a1, 1.0a2 and 1.0a3. Also in this alpha: The new datasette plugins --requirements option outputs a list of currently installed plugins in Python requirements.txt format, useful for duplicating that installation elsewhere. ( #2133 ) Writable canned queries can now define a on_success_message_sql field in their configuration, containing a SQL query that should be executed upon successful completion of the write operation in order to generate a message to be shown to the user. ( #2138 ) The automatically generated border color for a database is now shown in more places around the application. ( #2119 ) Every instance of example shell script code in the documentation should now include a working copy button, free from additional syntax. ( #2140 ) 59  
93 1.0a3 (2023-08-09) This alpha release previews the updated design for Datasette's default JSON API. ( #782 ) The new default JSON representation for both table pages ( /dbname/table.json ) and arbitrary SQL queries ( /dbname.json?sql=... ) is now shaped like this: { "ok": true, "rows": [ { "id": 3, "name": "Detroit" }, { "id": 2, "name": "Los Angeles" }, { "id": 4, "name": "Memnonia" }, { "id": 1, "name": "San Francisco" } ], "truncated": false } Tables will include an additional "next" key for pagination, which can be passed to ?_next= to fetch the next page of results. The various ?_shape= options continue to work as before - see Different shapes for details. A new ?_extra= mechanism is available for tables, but has not yet been stabilized or documented. Details on that are available in #262 . 59  
94 Smaller changes Datasette documentation now shows YAML examples for Metadata by default, with a tab interface for switching to JSON. ( #1153 ) register_output_renderer(datasette) plugins now have access to error and truncated arguments, allowing them to display error messages and take into account truncated results. ( #2130 ) render_cell() plugin hook now also supports an optional request argument. ( #2007 ) New Justfile to support development workflows for Datasette using Just . datasette.render_template() can now accepts a datasette.views.Context subclass as an alternative to a dictionary. ( #2127 ) datasette install -e path option for editable installations, useful while developing plugins. ( #2106 ) When started with the --cors option Datasette now serves an Access-Control-Max-Age: 3600 header, ensuring CORS OPTIONS requests are repeated no more than once an hour. ( #2079 ) Fixed a bug where the _internal database could display None instead of null for in-memory databases. ( #1970 ) 59  
95 0.64.2 (2023-03-08) Fixed a bug with datasette publish cloudrun where deploys all used the same Docker image tag. This was mostly inconsequential as the service is deployed as soon as the image has been pushed to the registry, but could result in the incorrect image being deployed if two different deploys for two separate services ran at exactly the same time. ( #2036 ) 59  
96 0.64.1 (2023-01-11) Documentation now links to a current source of information for installing Python 3. ( #1987 ) Incorrectly calling the Datasette constructor using Datasette("path/to/data.db") instead of Datasette(["path/to/data.db"]) now returns a useful error message. ( #1985 ) 59  
97 0.64 (2023-01-09) Datasette now strongly recommends against allowing arbitrary SQL queries if you are using SpatiaLite . SpatiaLite includes SQL functions that could cause the Datasette server to crash. See SpatiaLite for more details. New default_allow_sql setting, providing an easier way to disable all arbitrary SQL execution by end users: datasette --setting default_allow_sql off . See also Controlling the ability to execute arbitrary SQL . ( #1409 ) Building a location to time zone API with SpatiaLite is a new Datasette tutorial showing how to safely use SpatiaLite to create a location to time zone API. New documentation about how to debug problems loading SQLite extensions . The error message shown when an extension cannot be loaded has also been improved. ( #1979 ) Fixed an accessibility issue: the <select> elements in the table filter form now show an outline when they are currently focused. ( #1771 ) 59  
98 0.63.3 (2022-12-17) Fixed a bug where datasette --root , when running in Docker, would only output the URL to sign in root when the server shut down, not when it started up. ( #1958 ) You no longer need to ensure await datasette.invoke_startup() has been called in order for Datasette to start correctly serving requests - this is now handled automatically the first time the server receives a request. This fixes a bug experienced when Datasette is served directly by an ASGI application server such as Uvicorn or Gunicorn. It also fixes a bug with the datasette-gunicorn plugin. ( #1955 ) 59  
99 1.0a2 (2022-12-14) The third Datasette 1.0 alpha release adds upsert support to the JSON API, plus the ability to specify finely grained permissions when creating an API token. See Datasette 1.0a2: Upserts and finely grained permissions for an extended, annotated version of these release notes. New /db/table/-/upsert API, documented here . upsert is an update-or-insert: existing rows will have specified keys updated, but if no row matches the incoming primary key a brand new row will be inserted instead. ( #1878 ) New register_permissions() plugin hook. Plugins can now register named permissions, which will then be listed in various interfaces that show available permissions. ( #1940 ) The /db/-/create API for creating a table now accepts "ignore": true and "replace": true options when called with the "rows" property that creates a new table based on an example set of rows. This means the API can be called multiple times with different rows, setting rules for what should happen if a primary key collides with an existing row. ( #1927 ) Arbitrary permissions can now be configured at the instance, database and resource (table, SQL view or canned query) level in Datasette's Metadata JSON and YAML files. The new "permissions" key can be used to specify which actors should have which permissions. See Other permissions in datasette.yaml for details. ( #1636 ) The /-/create-token page can now be used to create API tokens which are restricted to just a subset of actions, including against specific databases or resources. See API Tokens for details. ( #1947 ) Likewise, the datasette create-token CLI command can now create tokens with a subset of permissions . (… 59  
100 1.0a1 (2022-12-01) Write APIs now serve correct CORS headers if Datasette is started in --cors mode. See the full list of CORS headers in the documentation. ( #1922 ) Fixed a bug where the _memory database could be written to even though writes were not persisted. ( #1917 ) The https://latest.datasette.io/ demo instance now includes an ephemeral database which can be used to test Datasette's write APIs, using the new datasette-ephemeral-tables plugin to drop any created tables after five minutes. This database is only available if you sign in as the root user using the link on the homepage. ( #1915 ) Fixed a bug where hitting the write endpoints with a GET request returned a 500 error. It now returns a 405 (method not allowed) error instead. ( #1916 ) The list of endpoints in the API explorer now lists mutable databases first. ( #1918 ) The "ignore": true and "replace": true options for the insert API are now documented . ( #1924 ) 59  
101 1.0a0 (2022-11-29) This first alpha release of Datasette 1.0 introduces a brand new collection of APIs for writing to the database ( #1850 ), as well as a new API token mechanism baked into Datasette core. Previously, API tokens have only been supported by installing additional plugins. This is very much a preview: expect many more backwards incompatible API changes prior to the full 1.0 release. Feedback enthusiastically welcomed, either through issue comments or via the Datasette Discord community. 59  
Powered by Datasette