# sections

id: authentication:authentication-cli-create-token-restrict
page: authentication
ref: authentication-cli-create-token-restrict
title: Restricting the actions that a token can perform
breadcrumbs: ["Authentication and permissions", "API Tokens", "datasette create-token"]
references: []
content:
Tokens created using  datasette create-token ACTOR_ID  will inherit all of the permissions of the actor that they are associated with. 
                     You can pass additional options to create tokens that are restricted to a subset of that actor's permissions. 
                     To restrict the token to just specific permissions against all available databases, use the  --all  option: 
                     datasette create-token root --all insert-row --all update-row 
                     This option can be passed as many times as you like. In the above example the token will only be allowed to insert and update rows. 
                     You can also restrict permissions such that they can only be used within specific databases: 
                     datasette create-token root --database mydatabase insert-row 
                     The resulting token will only be able to insert rows, and only to tables in the  mydatabase  database. 
                     Finally, you can restrict permissions to individual resources - tables, SQL views and  named queries  - within a specific database: 
                     datasette create-token root --resource mydatabase mytable insert-row 
                     These options have short versions:  -a  for  --all ,  -d  for  --database  and  -r  for  --resource . 
                     You can add  --debug  to see a JSON representation of the token that has been created. Here's a full example: 
                     datasette create-token root \
    --secret mysecret \
    --all view-instance \
    --all view-table \
    --database docs view-query \
    --resource docs documents insert-row \
    --resource docs documents update-row \
    --debug 
                     This example outputs the following: 
                     dstok_.eJxFizEKgDAMRe_y5w4qYrFXERGxDkVsMI0uxbubdjFL8l_ez1jhwEQCA6Fjjxp90qtkuHawzdjYrh8MFobLxZ_wBH0_gtnAF-hpS5VfmF8D_lnd97lHqUJgLd6sls4H1qwlhA.nH_7RecYHj5qSzvjhMU95iy0Xlc

Decoded:

{
  "a": "root",
  "token": "dstok",
  "t": 1670907246,
  "_r": {
    "a": [
      "vi",
      "vt"
    ],
    "d": {
      "docs": [
        "vq"
      ]
    },
    "r": {
      "docs": {
        "documents": [
          "ir",
          "ur"
        ]
      }
    }
  }
}


id: authentication:permissions-plugins
page: authentication
ref: permissions-plugins
title: Checking permissions in plugins
breadcrumbs: ["Authentication and permissions"]
references: []
content:
Datasette plugins can check if an actor has permission to perform an action using the  datasette.permission_allowed(...)  method. 
             Datasette core performs a number of permission checks,  documented below . Plugins can implement the  permission_allowed(datasette, actor, action, resource)  plugin hook to participate in decisions about whether an actor should be able to perform a specified action.


id: authentication:authentication-actor-matches-allow
page: authentication
ref: authentication-actor-matches-allow
title: actor_matches_allow()
breadcrumbs: ["Authentication and permissions"]
references: []
content:
Plugins that wish to implement this same  "allow"  block permissions scheme can take advantage of the  datasette.utils.actor_matches_allow(actor, allow)  function: 
             from datasette.utils import actor_matches_allow

actor_matches_allow({"id": "root"}, {"id": "*"})
# returns True 
             The currently authenticated actor is made available to plugins as  request.actor .


id: authentication:permissionsdebugview
page: authentication
ref: permissionsdebugview
title: The permissions debug tool
breadcrumbs: ["Authentication and permissions"]
references: []
content:
The debug tool at  /-/permissions  is only available to the  authenticated root user  (or any actor granted the  permissions-debug  action). 
             It shows the thirty most recent permission checks that have been carried out by the Datasette instance. 
             It also provides an interface for running hypothetical permission checks against a hypothetical actor. This is a useful way of confirming that your configured permissions work in the way you expect. 
             This is designed to help administrators and plugin authors understand exactly how permission checks are being carried out, in order to effectively configure Datasette's permission system.


id: authentication:authentication-ds-actor
page: authentication
ref: authentication-ds-actor
title: The ds_actor cookie
breadcrumbs: ["Authentication and permissions"]
references: []
content:
Datasette includes a default authentication plugin which looks for a signed  ds_actor  cookie containing a JSON actor dictionary. This is how the  root actor  mechanism works. 
             Authentication plugins can set signed  ds_actor  cookies themselves like so: 
             response = Response.redirect("/")
response.set_cookie(
    "ds_actor",
    datasette.sign({"a": {"id": "cleopaws"}}, "actor"),
) 
             Note that you need to pass  "actor"  as the namespace to  .sign(value, namespace="default") . 
             The shape of data encoded in the cookie is as follows: 
             {
    "a": {... actor ...}
}


id: authentication:authentication-ds-actor-expiry
page: authentication
ref: authentication-ds-actor-expiry
title: Including an expiry time
breadcrumbs: ["Authentication and permissions", "The ds_actor cookie"]
references: []
content:
ds_actor  cookies can optionally include a signed expiry timestamp, after which the cookies will no longer be valid. Authentication plugins may chose to use this mechanism to limit the lifetime of the cookie. For example, if a plugin implements single-sign-on against another source it may decide to set short-lived cookies so that if the user is removed from the SSO system their existing Datasette cookies will stop working shortly afterwards. 
                 To include an expiry, add a  "e"  key to the cookie value containing a base62-encoded integer representing the timestamp when the cookie should expire. For example, here's how to set a cookie that expires after 24 hours: 
                 import time
from datasette.utils import baseconv

expires_at = int(time.time()) + (24 * 60 * 60)

response = Response.redirect("/")
response.set_cookie(
    "ds_actor",
    datasette.sign(
        {
            "a": {"id": "cleopaws"},
            "e": baseconv.base62.encode(expires_at),
        },
        "actor",
    ),
) 
                 The resulting cookie will encode data that looks something like this: 
                 {
    "a": {
        "id": "cleopaws"
    },
    "e": "1jjSji"
}


id: authentication:logoutview
page: authentication
ref: logoutview
title: The /-/logout page
breadcrumbs: ["Authentication and permissions", "The ds_actor cookie"]
references: []
content:
The page at  /-/logout  provides the ability to log out of a  ds_actor  cookie authentication session.


id: authentication:id1
page: authentication
ref: id1
title: Built-in permissions
breadcrumbs: ["Authentication and permissions"]
references: []
content:
This section lists all of the permission checks that are carried out by Datasette core, along with the  resource  if it was passed.


id: authentication:permissions-view-instance
page: authentication
ref: permissions-view-instance
title: view-instance
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
references: [{"href": "https://latest.datasette.io/", "label": "https://latest.datasette.io/"}]
content:
Top level permission - Actor is allowed to view any pages within this instance, starting at  https://latest.datasette.io/ 
                 Default  allow .


id: authentication:permissions-view-database
page: authentication
ref: permissions-view-database
title: view-database
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
references: [{"href": "https://latest.datasette.io/fixtures", "label": "https://latest.datasette.io/fixtures"}]
content:
Actor is allowed to view a database page, e.g.  https://latest.datasette.io/fixtures 
                 
                     
                         resource  - string 
                         
                             The name of the database 
                         
                     
                 
                 Default  allow .


id: authentication:permissions-view-database-download
page: authentication
ref: permissions-view-database-download
title: view-database-download
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
content:
Actor is allowed to download a database, e.g.  https://latest.datasette.io/fixtures.db 
                 
                     
                         resource  - string 
                         
                             The name of the database 
                         
                     
                 
                 Default  allow .

references:
[{"href": "https://latest.datasette.io/fixtures.db", "label": "https://latest.datasette.io/fixtures.db"}]


id: authentication:permissions-view-table
page: authentication
ref: permissions-view-table
title: view-table
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
content:
Actor is allowed to view a table (or view) page, e.g.  https://latest.datasette.io/fixtures/complex_foreign_keys 
                 
                     
                         resource  - tuple: (string, string) 
                         
                             The name of the database, then the name of the table 
                         
                     
                 
                 Default  allow .

references:
[{"href": "https://latest.datasette.io/fixtures/complex_foreign_keys", "label": "https://latest.datasette.io/fixtures/complex_foreign_keys"}]


id: authentication:permissions-view-query
page: authentication
ref: permissions-view-query
title: view-query
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
content:
Actor is allowed to view (and execute) a  canned query  page, e.g.  https://latest.datasette.io/fixtures/pragma_cache_size  - this includes executing  Writable canned queries . 
                 
                     
                         resource  - tuple: (string, string) 
                         
                             The name of the database, then the name of the canned query 
                         
                     
                 
                 Default  allow .

references:
[{"href": "https://latest.datasette.io/fixtures/pragma_cache_size", "label": "https://latest.datasette.io/fixtures/pragma_cache_size"}]


id: authentication:permissions-insert-row
page: authentication
ref: permissions-insert-row
title: insert-row
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
references: []
content:
Actor is allowed to insert rows into a table. 
                 
                     
                         resource  - tuple: (string, string) 
                         
                             The name of the database, then the name of the table 
                         
                     
                 
                 Default  deny .


id: authentication:permissions-delete-row
page: authentication
ref: permissions-delete-row
title: delete-row
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
references: []
content:
Actor is allowed to delete rows from a table. 
                 
                     
                         resource  - tuple: (string, string) 
                         
                             The name of the database, then the name of the table 
                         
                     
                 
                 Default  deny .


id: authentication:permissions-update-row
page: authentication
ref: permissions-update-row
title: update-row
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
references: []
content:
Actor is allowed to update rows in a table. 
                 
                     
                         resource  - tuple: (string, string) 
                         
                             The name of the database, then the name of the table 
                         
                     
                 
                 Default  deny .


id: authentication:permissions-create-table
page: authentication
ref: permissions-create-table
title: create-table
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
references: []
content:
Actor is allowed to create a database table. 
                 
                     
                         resource  - string 
                         
                             The name of the database 
                         
                     
                 
                 Default  deny .


id: authentication:permissions-alter-table
page: authentication
ref: permissions-alter-table
title: alter-table
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
references: []
content:
Actor is allowed to alter a database table. 
                 
                     
                         resource  - tuple: (string, string) 
                         
                             The name of the database, then the name of the table 
                         
                     
                 
                 Default  deny .


id: authentication:permissions-drop-table
page: authentication
ref: permissions-drop-table
title: drop-table
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
references: []
content:
Actor is allowed to drop a database table. 
                 
                     
                         resource  - tuple: (string, string) 
                         
                             The name of the database, then the name of the table 
                         
                     
                 
                 Default  deny .


id: authentication:permissions-execute-sql
page: authentication
ref: permissions-execute-sql
title: execute-sql
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
content:
Actor is allowed to run arbitrary SQL queries against a specific database, e.g.  https://latest.datasette.io/fixtures?sql=select+100 
                 
                     
                         resource  - string 
                         
                             The name of the database 
                         
                     
                 
                 Default  allow . See also  the default_allow_sql setting .

references:
[{"href": "https://latest.datasette.io/fixtures?sql=select+100", "label": "https://latest.datasette.io/fixtures?sql=select+100"}]


id: authentication:permissions-debug-menu
page: authentication
ref: permissions-debug-menu
title: debug-menu
breadcrumbs: ["Authentication and permissions", "Built-in permissions"]
references: []
content:
Controls if the various debug pages are displayed in the navigation menu. 
                 Default  deny .


id: javascript_plugins:id1
page: javascript_plugins
ref: id1
title: JavaScript plugins
breadcrumbs: []
references: []
content:
Datasette can run custom JavaScript in several different ways: 
         
             
                 Datasette plugins written in Python can use the  extra_js_urls()  or  extra_body_script()  plugin hooks to inject JavaScript into a page 
             
             
                 Datasette instances with  custom templates  can include additional JavaScript in those templates 
             
             
                 The  extra_js_urls  key in  datasette.yaml   can be used to include extra JavaScript 
             
         
         There are no limitations on what this JavaScript can do. It is executed directly by the browser, so it can manipulate the DOM, fetch additional data and do anything else that JavaScript is capable of. 
         
             Custom JavaScript has security implications, especially for authenticated Datasette instances where the JavaScript might run in the context of the authenticated user. It's important to carefully review any JavaScript you run in your Datasette instance.


id: javascript_plugins:javascript-datasette-init
page: javascript_plugins
ref: javascript-datasette-init
title: The datasette_init event
breadcrumbs: ["JavaScript plugins"]
references: []
content:
Datasette emits a custom event called  datasette_init  when the page is loaded. This event is dispatched on the  document  object, and includes a  detail  object with a reference to the  datasetteManager  object. 
             Your JavaScript code can listen out for this event using  document.addEventListener()  like this: 
             document.addEventListener("datasette_init", function (evt) {
    const manager = evt.detail;
    console.log("Datasette version:", manager.VERSION);
});


id: javascript_plugins:javascript-datasette-manager
page: javascript_plugins
ref: javascript-datasette-manager
title: datasetteManager
breadcrumbs: ["JavaScript plugins"]
references: []
content:
The  datasetteManager  object 
             
                 
                     VERSION  - string 
                     
                         The version of Datasette 
                     
                 
                 
                     plugins  -  Map() 
                     
                         A Map of currently loaded plugin names to plugin implementations 
                     
                 
                 
                     registerPlugin(name, implementation) 
                     
                         Call this to register a plugin, passing its name and implementation 
                     
                 
                 
                     selectors  - object 
                     
                         An object providing named aliases to useful CSS selectors,  listed below


id: javascript_plugins:id2
page: javascript_plugins
ref: id2
title: JavaScript plugin objects
breadcrumbs: ["JavaScript plugins"]
references: []
content:
JavaScript plugins are blocks of code that can be registered with Datasette using the  registerPlugin()  method on the  datasetteManager  object. 
             The  implementation  object passed to this method should include a  version  key defining the plugin version, and one or more of the following named functions providing the implementation of the plugin:


id: javascript_plugins:javascript-plugins-makeabovetablepanelconfigs
page: javascript_plugins
ref: javascript-plugins-makeabovetablepanelconfigs
title: makeAboveTablePanelConfigs()
breadcrumbs: ["JavaScript plugins", "JavaScript plugin objects"]
references: []
content:
This method should return a JavaScript array of objects defining additional panels to be added to the top of the table page. Each object should have the following: 
                 
                     
                         id  - string 
                         
                             A unique string ID for the panel, for example  map-panel 
                         
                     
                     
                         label  - string 
                         
                             A human-readable label for the panel 
                         
                     
                     
                         render(node)  - function 
                         
                             A function that will be called with a DOM node to render the panel into 
                         
                     
                 
                 This example shows how a plugin might define a single panel: 
                 document.addEventListener('datasette_init', function(ev) {
  ev.detail.registerPlugin('panel-plugin', {
    version: 0.1,
    makeAboveTablePanelConfigs: () => {
      return [
        {
          id: 'first-panel',
          label: 'First panel',
          render: node => {
            node.innerHTML = '<h2>My custom panel</h2><p>This is a custom panel that I added using a JavaScript plugin</p>';
          }
        }
      ]
    }
  });
}); 
                 When a page with a table loads, all registered plugins that implement  makeAboveTablePanelConfigs()  will be called and panels they return will be added to the top of the table page.


id: javascript_plugins:javascript-plugins-makecolumnactions
page: javascript_plugins
ref: javascript-plugins-makecolumnactions
title: makeColumnActions(columnDetails)
breadcrumbs: ["JavaScript plugins", "JavaScript plugin objects"]
references: []
content:
This method, if present, will be called when Datasette is rendering the cog action menu icons that appear at the top of the table view. By default these include options like "Sort ascending/descending" and "Facet by this", but plugins can return additional actions to be included in this menu. 
                 The method will be called with a  columnDetails  object with the following keys: 
                 
                     
                         columnName  - string 
                         
                             The name of the column 
                         
                     
                     
                         columnNotNull  - boolean 
                         
                             True if the column is defined as NOT NULL 
                         
                     
                     
                         columnType  - string 
                         
                             The SQLite data type of the column 
                         
                     
                     
                         isPk  - boolean 
                         
                             True if the column is part of the primary key 
                         
                     
                 
                 It should return a JavaScript array of objects each with a  label  and  onClick  property: 
                 
                     
                         label  - string 
                         
                             The human-readable label for the action 
                         
                     
                     
                         onClick(evt)  - function 
                         
                             A function that will be called when the action is clicked 
                         
                     
                 
                 The  evt  object passed to the  onClick  is the standard browser event object that triggered the click. 
                 This example plugin adds two menu items - one to copy the column name to the clipboard and another that displays the column metadata in an  alert()  window: 
                 document.addEventListener('datasette_init', function(ev) {
  ev.detail.registerPlugin('column-name-plugin', {
    version: 0.1,
    makeColumnActions: (columnDetails) => {
      return [
        {
          label: 'Copy column to clipboard',
          onClick: async (evt) => {
            await navigator.clipboard.writeText(columnDetails.columnName)
          }
        },
        {
          label: 'Alert column metadata',
          onClick: () => alert(JSON.stringify(columnDetails, null, 2))
        }
      ];
    }
  });
});


id: javascript_plugins:javascript-datasette-manager-selectors
page: javascript_plugins
ref: javascript-datasette-manager-selectors
title: Selectors
breadcrumbs: ["JavaScript plugins"]
references: []
content:
These are available on the  selectors  property of the  datasetteManager  object. 
             const DOM_SELECTORS = {
  /** Should have one match */
  jsonExportLink: ".export-links a[href*=json]",

  /** Event listeners that go outside of the main table, e.g. existing scroll listener */
  tableWrapper: ".table-wrapper",
  table: "table.rows-and-columns",
  aboveTablePanel: ".above-table-panel",

  // These could have multiple matches
  /** Used for selecting table headers. Use makeColumnActions if you want to add menu items. */
  tableHeaders: `table.rows-and-columns th`,

  /** Used to add "where"  clauses to query using direct manipulation */
  filterRows: ".filter-row",
  /** Used to show top available enum values for a column ("facets") */
  facetResults: ".facet-results [data-column]",
};


id: index:datasette
page: index
ref: datasette
title: Datasette
breadcrumbs: []
content:
An open source multi-tool for exploring and publishing data 
         Datasette is a tool for exploring and publishing data. It helps people take data of any shape or size and publish that as an interactive, explorable website and accompanying API. 
         Datasette is aimed at data journalists, museum curators, archivists, local governments and anyone else who has data that they wish to share with the world. It is part of a  wider ecosystem of tools and plugins  dedicated to making working with structured data as productive as possible. 
         Explore a demo , watch  a presentation about the project  or  Try Datasette without installing anything using Glitch . 
         Interested in learning Datasette? Start with  the official tutorials . 
         Support questions, feedback? Join the  Datasette Discord .

references:
[{"href": "https://pypi.org/project/datasette/", "label": null}, {"href": "https://docs.datasette.io/en/stable/changelog.html", "label": null}, {"href": "https://pypi.org/project/datasette/", "label": null}, {"href": "https://github.com/simonw/datasette/actions?query=workflow%3ATest", "label": null}, {"href": "https://github.com/simonw/datasette/blob/main/LICENSE", "label": null}, {"href": "https://hub.docker.com/r/datasetteproject/datasette", "label": null}, {"href": "https://datasette.io/discord", "label": null}, {"href": "https://pypi.org/project/datasette/", "label": null}, {"href": "https://docs.datasette.io/en/stable/changelog.html", "label": null}, {"href": "https://pypi.org/project/datasette/", "label": null}, {"href": "https://github.com/simonw/datasette/actions?query=workflow%3ATest", "label": null}, {"href": "https://github.com/simonw/datasette/blob/main/LICENSE", "label": null}, {"href": "https://hub.docker.com/r/datasetteproject/datasette", "label": null}, {"href": "https://datasette.io/discord", "label": null}, {"href": "https://fivethirtyeight.datasettes.com/fivethirtyeight", "label": "Explore a demo"}, {"href": "https://static.simonwillison.net/static/2018/pybay-datasette/", "label": "a presentation about the project"}, {"href": "https://datasette.io/tutorials", "label": "the official tutorials"}, {"href": "https://datasette.io/discord", "label": "Datasette Discord"}]


id: index:contents
page: index
ref: contents
title: Contents
breadcrumbs: ["Datasette"]
references: []
content:
Getting started Play with a live demo Follow a tutorial Datasette in your browser with Datasette Lite Try Datasette without installing anything using Glitch Using Datasette on your own computer Installation Basic installation Datasette Desktop for Mac Using Homebrew Using pip Advanced installation options Using pipx Using Docker A note about extensions Configuration Configuration via the command-line datasette.yaml  reference Settings Plugin configuration Permissions configuration Canned queries configuration Custom CSS and JavaScript The Datasette Ecosystem sqlite-utils Dogsheep CLI reference datasette --help datasette serve datasette --get datasette serve --help-settings datasette plugins datasette install datasette uninstall datasette publish datasette publish cloudrun datasette publish heroku datasette package datasette inspect datasette create-token Pages and API endpoints Top-level index Database Hidden tables Table Row Publishing data datasette publish Publishing to Google Cloud Run Publishing to Heroku Publishing to Vercel Publishing to Fly Custom metadata and plugins datasette package Deploying Datasette Deployment fundamentals Running Datasette using systemd Running Datasette using OpenRC Deploying using buildpacks Running Datasette behind a proxy Nginx proxy configuration Apache proxy configuration JSON API Default representation Different shapes Pagination Special JSON arguments Table arguments Column filter arguments Special table arguments Expanding foreign key references Discovering the JSON for a page Enabling CORS The JSON write API Inserting rows Upserting rows Updating a row Deleting a row Creating a table Creating a table from example data Dropping tables Running SQL queries Named parameters Views Canned queries Canned query parameters Additional canned query options Writable canned queries Magic parameters JSON API for writable canned queries Pagination Cross-database queries Authentication and permissions Actors Using the "root" actor Permissions How permissions are resolved Defining permissions with "allow" blocks The /-/allow-debug tool Access permissions in  datasette.yaml Access to an instance Access to specific databases Access to specific tables and views Access to specific canned queries Controlling the ability to execute arbitrary SQL Other permissions in  datasette.yaml API Tokens datasette create-token Checking permissions in plugins actor_matches_allow() The permissions debug tool The ds_actor cookie Including an expiry time The /-/logout page Built-in permissions view-instance view-database view-database-download view-table view-query insert-row delete-row update-row create-table alter-table drop-table execute-sql permissions-debug debug-menu Performance and caching Immutable mode Using "datasette inspect" HTTP caching datasette-hashed-urls CSV export URL parameters Streaming all records Binary data Linking to binary downloads Binary plugins Facets Facets in query strings Facets in metadata Suggested facets Speeding up facets with indexes Facet by JSON array Facet by date Full-text search The table page and table view API Advanced SQLite search queries Configuring full-text search for a table or view Searches using custom SQL Enabling full-text search for a SQLite table Configuring FTS using sqlite-utils Configuring FTS using csvs-to-sqlite Configuring FTS by hand FTS versions SpatiaLite Warning Installation Installing SpatiaLite on OS X Installing SpatiaLite on Linux Spatial indexing latitude/longitude columns Making use of a spatial index Importing shapefiles into SpatiaLite Importing GeoJSON polygons using Shapely Querying polygons using within() Metadata Per-database and per-table metadata Source, license and about Column descriptions Specifying units for a column Setting a default sort order Setting a custom page size Setting which columns can be used for sorting Specifying the label column for a table Hiding tables Metadata reference Top-level metadata Database-level metadata Table-level metadata Settings Using --setting Configuration directory mode Settings default_allow_sql default_page_size sql_time_limit_ms max_returned_rows max_insert_rows num_sql_threads allow_facet default_facet_size facet_time_limit_ms facet_suggest_time_limit_ms suggest_facets allow_download allow_signed_tokens max_signed_tokens_ttl default_cache_ttl cache_size_kb allow_csv_stream max_csv_mb truncate_cells_html force_https_urls template_debug trace_debug base_url Configuring the secret Using secrets with datasette publish Introspection /-/metadata /-/versions /-/plugins /-/settings /-/config /-/databases /-/threads /-/actor /-/messages Custom pages and templates CSS classes on the <body> Serving static files Publishing static assets Custom templates Custom pages Path parameters for pages Custom headers and status codes Returning 404s Custom redirects Custom error pages Plugins Installing plugins One-off plugins using --plugins-dir Deploying plugins using datasette publish Controlling which plugins are loaded Seeing what plugins are installed Plugin configuration Secret configuration values Writing plugins Tracing plugin hooks Writing one-off plugins Starting an installable plugin using cookiecutter Packaging a plugin Static assets Custom templates Writing plugins that accept configuration Designing URLs for your plugin Building URLs within plugins Plugins that define new plugin hooks JavaScript plugins The datasette_init event datasetteManager JavaScript plugin objects makeAboveTablePanelConfigs() makeColumnActions(columnDetails) Selectors Plugin hooks prepare_connection(conn, database, datasette) prepare_jinja2_environment(env, datasette) Page extras extra_template_vars(template, database, table, columns, view_name, request, datasette) extra_css_urls(template, database, table, columns, view_name, request, datasette) extra_js_urls(template, database, table, columns, view_name, request, datasette) extra_body_script(template, database, table, columns, view_name, request, datasette) publish_subcommand(publish) render_cell(row, value, column, table, database, datasette, request) register_output_renderer(datasette) register_routes(datasette) register_commands(cli) register_facet_classes() register_permissions(datasette) asgi_wrapper(datasette) startup(datasette) canned_queries(datasette, database, actor) actor_from_request(datasette, request) actors_from_ids(datasette, actor_ids) jinja2_environment_from_request(datasette, request, env) filters_from_request(request, database, table, datasette) permission_allowed(datasette, actor, action, resource) register_magic_parameters(datasette) forbidden(datasette, request, message) handle_exception(datasette, request, exception) skip_csrf(datasette, scope) get_metadata(datasette, key, database, table) menu_links(datasette, actor, request) Action hooks table_actions(datasette, actor, database, table, request) view_actions(datasette, actor, database, view, request) query_actions(datasette, actor, database, query_name, request, sql, params) row_actions(datasette, actor, request, database, table, row) database_actions(datasette, actor, database, request) homepage_actions(datasette, actor, request) Template slots top_homepage(datasette, request) top_database(datasette, request, database) top_table(datasette, request, database, table) top_row(datasette, request, database, table, row) top_query(datasette, request, database, sql) top_canned_query(datasette, request, database, query_name) Event tracking track_event(datasette, event) register_events(datasette) Testing plugins Setting up a Datasette test instance Using datasette.client in tests Using pdb for errors thrown inside Datasette Using pytest fixtures Testing outbound HTTP calls with pytest-httpx Registering a plugin for the duration of a test Internals for plugins Request object The MultiParams class Response class Returning a response with .asgi_send(send) Setting cookies with response.set_cookie() Datasette class .databases .permissions .plugin_config(plugin_name, database=None, table=None) await .render_template(template, context=None, request=None) await .actors_from_ids(actor_ids) await .permission_allowed(actor, action, resource=None, default=...) await .ensure_permissions(actor, permissions) await .check_visibility(actor, action=None, resource=None, permissions=None) .create_token(actor_id, expires_after=None, restrict_all=None, restrict_database=None, restrict_resource=None) .get_permission(name_or_abbr) .get_database(name) .get_internal_database() .add_database(db, name=None, route=None) .add_memory_database(name) .remove_database(name) await .track_event(event) .sign(value, namespace="default") .unsign(value, namespace="default") .add_message(request, message, type=datasette.INFO) .absolute_url(request, path) .setting(key) .resolve_database(request) .resolve_table(request) .resolve_row(request) datasette.client datasette.urls Database class Database(ds, path=None, is_mutable=True, is_memory=False, memory_name=None) db.hash await db.execute(sql, ...) Results await db.execute_fn(fn) await db.execute_write(sql, params=None, block=True) await db.execute_write_script(sql, block=True) await db.execute_write_many(sql, params_seq, block=True) await db.execute_write_fn(fn, block=True, transaction=True) await db.execute_isolated_fn(fn) db.close() Database introspection CSRF protection Datasette's internal database The datasette.utils module parse_metadata(content) await_me_maybe(value) derive_named_parameters(db, sql) Tilde encoding datasette.tracer Tracing child tasks Import shortcuts Events LoginEvent LogoutEvent CreateTokenEvent CreateTableEvent DropTableEvent AlterTableEvent InsertRowsEvent UpsertRowsEvent UpdateRowEvent DeleteRowEvent Contributing General guidelines Setting up a development environment Running the tests Using fixtures Debugging Code formatting Running Black blacken-docs Prettier Editing and building the documentation Running Cog Continuously deployed demo instances Release process Alpha and beta releases Releasing bug fixes from a branch Upgrading CodeMirror Changelog 1.0a13 (2024-03-12) 1.0a12 (2024-02-29) 1.0a11 (2024-02-19) 1.0a10 (2024-02-17) 1.0a9 (2024-02-16) Alter table support for create, insert, upsert and update Permissions fix for the upsert API Permission checks now consider opinions from every plugin Other changes 1.0a8 (2024-02-07) Configuration JavaScript plugins Plugin hooks Documentation Minor fixes 0.64.6 (2023-12-22) 0.64.5 (2023-10-08) 1.0a7 (2023-09-21) 0.64.4 (2023-09-21) 1.0a6 (2023-09-07) 1.0a5 (2023-08-29) 1.0a4 (2023-08-21) 1.0a3 (2023-08-09) Smaller changes 0.64.2 (2023-03-08) 0.64.1 (2023-01-11) 0.64 (2023-01-09) 0.63.3 (2022-12-17) 1.0a2 (2022-12-14) 1.0a1 (2022-12-01) 1.0a0 (2022-11-29) Signed API tokens Write API 0.63.2 (2022-11-18) 0.63.1 (2022-11-10) 0.63 (2022-10-27) Features Plugin hooks and internals Documentation 0.62 (2022-08-14) Features Plugin hooks Bug fixes Documentation 0.61.1 (2022-03-23) 0.61 (2022-03-23) 0.60.2 (2022-02-07) 0.60.1 (2022-01-20) 0.60 (2022-01-13) Plugins and internals Faceting Other small fixes 0.59.4 (2021-11-29) 0.59.3 (2021-11-20) 0.59.2 (2021-11-13) 0.59.1 (2021-10-24) 0.59 (2021-10-14) 0.58.1 (2021-07-16) 0.58 (2021-07-14) 0.57.1 (2021-06-08) 0.57 (2021-06-05) New features Bug fixes and other improvements 0.56.1 (2021-06-05) 0.56 (2021-03-28) 0.55 (2021-02-18) 0.54.1 (2021-02-02) 0.54 (2021-01-25) The _internal database Named in-memory database support JavaScript modules Code formatting with Black and Prettier Other changes 0.53 (2020-12-10) 0.52.5 (2020-12-09) 0.52.4 (2020-12-05) 0.52.3 (2020-12-03) 0.52.2 (2020-12-02) 0.52.1 (2020-11-29) 0.52 (2020-11-28) 0.51.1 (2020-10-31) 0.51 (2020-10-31) New visual design Plugins can now add links within Datasette Binary data URL building Running Datasette behind a proxy Smaller changes 0.50.2 (2020-10-09) 0.50.1 (2020-10-09) 0.50 (2020-10-09) 0.49.1 (2020-09-15) 0.49 (2020-09-14) 0.48 (2020-08-16) 0.47.3 (2020-08-15) 0.47.2 (2020-08-12) 0.47.1 (2020-08-11) 0.47 (2020-08-11) 0.46 (2020-08-09) 0.45 (2020-07-01) Magic parameters for canned queries Log out Better plugin documentation New plugin hooks Smaller changes 0.44 (2020-06-11) Authentication Permissions Writable canned queries Flash messages Signed values and secrets CSRF protection Cookie methods register_routes() plugin hooks Smaller changes The road to Datasette 1.0 0.43 (2020-05-28) 0.42 (2020-05-08) 0.41 (2020-05-06) 0.40 (2020-04-21) 0.39 (2020-03-24) 0.38 (2020-03-08) 0.37.1 (2020-03-02) 0.37 (2020-02-25) 0.36 (2020-02-21) 0.35 (2020-02-04) 0.34 (2020-01-29) 0.33 (2019-12-22) 0.32 (2019-11-14) 0.31.2 (2019-11-13) 0.31.1 (2019-11-12) 0.31 (2019-11-11) 0.30.2 (2019-11-02) 0.30.1 (2019-10-30) 0.30 (2019-10-18) 0.29.3 (2019-09-02) 0.29.2 (2019-07-13) 0.29.1 (2019-07-11) 0.29 (2019-07-07) ASGI New plugin hook: asgi_wrapper New plugin hook: extra_template_vars Secret plugin configuration options Facet by date Easier custom templates for table rows ?_through= for joins through many-to-many tables Small changes 0.28 (2019-05-19) Supporting databases that change Faceting improvements, and faceting plugins datasette publish cloudrun register_output_renderer plugins Medium changes Small changes 0.27.1 (2019-05-09) 0.27 (2019-01-31) 0.26.1 (2019-01-10) 0.26 (2019-01-02) 0.25.2 (2018-12-16) 0.25.1 (2018-11-04) 0.25 (2018-09-19) 0.24 (2018-07-23) 0.23.2 (2018-07-07) 0.23.1 (2018-06-21) 0.23 (2018-06-18) CSV export Foreign key expansions New configuration settings Control HTTP caching with ?_ttl= Improved support for SpatiaLite latest.datasette.io Miscellaneous 0.22.1 (2018-05-23) 0.22 (2018-05-20) 0.21 (2018-05-05) 0.20 (2018-04-20) 0.19 (2018-04-16) 0.18 (2018-04-14) 0.17 (2018-04-13) 0.16 (2018-04-13) 0.15 (2018-04-09) 0.14 (2017-12-09) 0.13 (2017-11-24) 0.12 (2017-11-16) 0.11 (2017-11-14) 0.10 (2017-11-14) 0.9 (2017-11-13) 0.8 (2017-11-13)


id: introspection:id1
page: introspection
ref: id1
title: Introspection
breadcrumbs: []
references: []
content:
Datasette includes some pages and JSON API endpoints for introspecting the current instance. These can be used to understand some of the internals of Datasette and to see how a particular instance has been configured. 
         Each of these pages can be viewed in your browser. Add  .json  to the URL to get back the contents as JSON.


id: introspection:jsondataview-metadata
page: introspection
ref: jsondataview-metadata
title: /-/metadata
breadcrumbs: ["Introspection"]
references: [{"href": "https://fivethirtyeight.datasettes.com/-/metadata", "label": "Metadata example"}]
content:
Shows the contents of the  metadata.json  file that was passed to  datasette serve , if any.  Metadata example : 
             {
    "license": "CC Attribution 4.0 License",
    "license_url": "http://creativecommons.org/licenses/by/4.0/",
    "source": "fivethirtyeight/data on GitHub",
    "source_url": "https://github.com/fivethirtyeight/data",
    "title": "Five Thirty Eight",
    "databases": {

    }
}


id: introspection:jsondataview-versions
page: introspection
ref: jsondataview-versions
title: /-/versions
breadcrumbs: ["Introspection"]
references: [{"href": "https://latest.datasette.io/-/versions", "label": "Versions example"}]
content:
Shows the version of Datasette, Python and SQLite.  Versions example : 
             {
    "datasette": {
        "version": "0.60"
    },
    "python": {
        "full": "3.8.12 (default, Dec 21 2021, 10:45:09) \n[GCC 10.2.1 20210110]",
        "version": "3.8.12"
    },
    "sqlite": {
        "extensions": {
            "json1": null
        },
        "fts_versions": [
            "FTS5",
            "FTS4",
            "FTS3"
        ],
        "compile_options": [
            "COMPILER=gcc-6.3.0 20170516",
            "ENABLE_FTS3",
            "ENABLE_FTS4",
            "ENABLE_FTS5",
            "ENABLE_JSON1",
            "ENABLE_RTREE",
            "THREADSAFE=1"
        ],
        "version": "3.37.0"
    }
}


id: introspection:jsondataview-plugins
page: introspection
ref: jsondataview-plugins
title: /-/plugins
breadcrumbs: ["Introspection"]
references: [{"href": "https://san-francisco.datasettes.com/-/plugins", "label": "Plugins example"}]
content:
Shows a list of currently installed plugins and their versions.  Plugins example : 
             [
    {
        "name": "datasette_cluster_map",
        "static": true,
        "templates": false,
        "version": "0.10",
        "hooks": ["extra_css_urls", "extra_js_urls", "extra_body_script"]
    }
] 
             Add  ?all=1  to include details of the default plugins baked into Datasette.


id: introspection:jsondataview-settings
page: introspection
ref: jsondataview-settings
title: /-/settings
breadcrumbs: ["Introspection"]
references: [{"href": "https://fivethirtyeight.datasettes.com/-/settings", "label": "Settings example"}]
content:
Shows the  Settings  for this instance of Datasette.  Settings example : 
             {
    "default_facet_size": 30,
    "default_page_size": 100,
    "facet_suggest_time_limit_ms": 50,
    "facet_time_limit_ms": 1000,
    "max_returned_rows": 1000,
    "sql_time_limit_ms": 1000
}


id: introspection:jsondataview-config
page: introspection
ref: jsondataview-config
title: /-/config
breadcrumbs: ["Introspection"]
references: [{"href": "https://latest.datasette.io/-/config", "label": "Config example"}]
content:
Shows the  configuration  for this instance of Datasette. This is generally the contents of the  datasette.yaml or datasette.json  file, which can include plugin configuration as well.  Config example : 
             {
    "settings": {
        "template_debug": true,
        "trace_debug": true,
        "force_https_urls": true
    }
} 
             Any keys that include the one of the following substrings in their names will be returned as redacted  ***  output, to help avoid accidentally leaking private configuration information:  secret ,  key ,  password ,  token ,  hash ,  dsn .


id: introspection:jsondataview-databases
page: introspection
ref: jsondataview-databases
title: /-/databases
breadcrumbs: ["Introspection"]
references: [{"href": "https://latest.datasette.io/-/databases", "label": "Databases example"}]
content:
Shows currently attached databases.  Databases example : 
             [
    {
        "hash": null,
        "is_memory": false,
        "is_mutable": true,
        "name": "fixtures",
        "path": "fixtures.db",
        "size": 225280
    }
]


id: introspection:jsondataview-threads
page: introspection
ref: jsondataview-threads
title: /-/threads
breadcrumbs: ["Introspection"]
references: [{"href": "https://latest.datasette.io/-/threads", "label": "Threads example"}]
content:
Shows details of threads and  asyncio  tasks.  Threads example : 
             {
    "num_threads": 2,
    "threads": [
        {
            "daemon": false,
            "ident": 4759197120,
            "name": "MainThread"
        },
        {
            "daemon": true,
            "ident": 123145319682048,
            "name": "Thread-1"
        },
    ],
    "num_tasks": 3,
    "tasks": [
        "<Task pending coro=<RequestResponseCycle.run_asgi() running at uvicorn/protocols/http/httptools_impl.py:385> cb=[set.discard()]>",
        "<Task pending coro=<Server.serve() running at uvicorn/main.py:361> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10365c3d0>()]> cb=[run_until_complete.<locals>.<lambda>()]>",
        "<Task pending coro=<LifespanOn.main() running at uvicorn/lifespan/on.py:48> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10364f050>()]>>"
    ]
}


id: introspection:jsondataview-actor
page: introspection
ref: jsondataview-actor
title: /-/actor
breadcrumbs: ["Introspection"]
references: []
content:
Shows the currently authenticated actor. Useful for debugging Datasette authentication plugins. 
             {
    "actor": {
        "id": 1,
        "username": "some-user"
    }
}


id: introspection:messagesdebugview
page: introspection
ref: messagesdebugview
title: /-/messages
breadcrumbs: ["Introspection"]
references: []
content:
The debug tool at  /-/messages  can be used to set flash messages to try out that feature. See  .add_message(request, message, type=datasette.INFO)  for details of this feature.


id: publish:publishing
page: publish
ref: publishing
title: Publishing data
breadcrumbs: []
references: []
content:
Datasette includes tools for publishing and deploying your data to the internet. The  datasette publish  command will deploy a new Datasette instance containing your databases directly to a Heroku or Google Cloud hosting account. You can also use  datasette package  to create a Docker image that bundles your databases together with the datasette application that is used to serve them.


id: publish:cli-publish
page: publish
ref: cli-publish
title: datasette publish
breadcrumbs: ["Publishing data"]
content:
Once you have created a SQLite database (e.g. using  csvs-to-sqlite ) you can deploy it to a hosting account using a single command. 
             You will need a hosting account with  Heroku  or  Google Cloud . Once you have created your account you will need to install and configure the  heroku  or  gcloud  command-line tools.

references:
[{"href": "https://github.com/simonw/csvs-to-sqlite/", "label": "csvs-to-sqlite"}, {"href": "https://www.heroku.com/", "label": "Heroku"}, {"href": "https://cloud.google.com/", "label": "Google Cloud"}]


id: publish:publish-cloud-run
page: publish
ref: publish-cloud-run
title: Publishing to Google Cloud Run
breadcrumbs: ["Publishing data", "datasette publish"]
content:
Google Cloud Run  allows you to publish data in a scale-to-zero environment, so your application will start running when the first request is received and will shut down again when traffic ceases. This means you only pay for time spent serving traffic. 
                 
                     Cloud Run is a great option for inexpensively hosting small, low traffic projects - but costs can add up for projects that serve a lot of requests. 
                     Be particularly careful if your project has tables with large numbers of rows. Search engine crawlers that index a page for every row could result in a high bill. 
                     The  datasette-block-robots  plugin can be used to request search engine crawlers omit crawling your site, which can help avoid this issue. 
                 
                 You will first need to install and configure the Google Cloud CLI tools by following  these instructions . 
                 You can then publish one or more SQLite database files to Google Cloud Run using the following command: 
                 datasette publish cloudrun mydatabase.db --service=my-database 
                 A Cloud Run  service  is a single hosted application. The service name you specify will be used as part of the Cloud Run URL. If you deploy to a service name that you have used in the past your new deployment will replace the previous one. 
                 If you omit the  --service  option you will be asked to pick a service name interactively during the deploy. 
                 You may need to interact with prompts from the tool. Many of the prompts ask for values that can be  set as properties for the Google Cloud SDK  if you want to avoid the prompts. 
                 For example, the default region for the deployed instance can be set using the command: 
                 gcloud config set run/region us-central1 
                 You should replace  us-central1  with your desired  region . Alternately, you can specify the region by setting the  CLOUDSDK_RUN_REGION  environment variable. 
                 Once it has finished it will output a URL like this one: 
                 Service [my-service] revision [my-service-00001] has been deployed
and is serving traffic at https://my-service-j7hipcg4aq-uc.a.run.app 
                 Cloud Run provides a URL on the  .run.app  domain, but you can also point your own domain or subdomain at your Cloud Run service - see  mapping custom domains  in the Cloud Run documentation for details. 
                 See  datasette publish cloudrun  for the full list of options for this command.

references:
[{"href": "https://cloud.google.com/run/", "label": "Google Cloud Run"}, {"href": "https://datasette.io/plugins/datasette-block-robots", "label": "datasette-block-robots"}, {"href": "https://cloud.google.com/sdk/", "label": "these instructions"}, {"href": "https://cloud.google.com/sdk/docs/properties", "label": "set as properties for the Google Cloud SDK"}, {"href": "https://cloud.google.com/about/locations", "label": "region"}, {"href": "https://cloud.google.com/run/docs/mapping-custom-domains", "label": "mapping custom domains"}]


id: publish:publish-heroku
page: publish
ref: publish-heroku
title: Publishing to Heroku
breadcrumbs: ["Publishing data", "datasette publish"]
content:
To publish your data using  Heroku , first create an account there and install and configure the  Heroku CLI tool . 
                 You can publish one or more databases to Heroku using the following command: 
                 datasette publish heroku mydatabase.db 
                 This will output some details about the new deployment, including a URL like this one: 
                 https://limitless-reef-88278.herokuapp.com/ deployed to Heroku 
                 You can specify a custom app name by passing  -n my-app-name  to the publish command. This will also allow you to overwrite an existing app. 
                 Rather than deploying directly you can use the  --generate-dir  option to output the files that would be deployed to a directory: 
                 datasette publish heroku mydatabase.db --generate-dir=/tmp/deploy-this-to-heroku 
                 See  datasette publish heroku  for the full list of options for this command.

references:
[{"href": "https://www.heroku.com/", "label": "Heroku"}, {"href": "https://devcenter.heroku.com/articles/heroku-cli", "label": "Heroku CLI tool"}]


id: publish:publish-vercel
page: publish
ref: publish-vercel
title: Publishing to Vercel
breadcrumbs: ["Publishing data", "datasette publish"]
content:
Vercel   - previously known as Zeit Now - provides a layer over AWS Lambda to allow for quick, scale-to-zero deployment. You can deploy Datasette instances to Vercel using the  datasette-publish-vercel  plugin. 
                 pip install datasette-publish-vercel
datasette publish vercel mydatabase.db --project my-database-project 
                 Not every feature is supported: consult the  datasette-publish-vercel README  for more details.

references:
[{"href": "https://vercel.com/", "label": "Vercel"}, {"href": "https://github.com/simonw/datasette-publish-vercel", "label": "datasette-publish-vercel"}, {"href": "https://github.com/simonw/datasette-publish-vercel/blob/main/README.md", "label": "datasette-publish-vercel README"}]


id: publish:publish-fly
page: publish
ref: publish-fly
title: Publishing to Fly
breadcrumbs: ["Publishing data", "datasette publish"]
content:
Fly  is a  competitively priced  Docker-compatible hosting platform that supports running applications in globally distributed data centers close to your end users. You can deploy Datasette instances to Fly using the  datasette-publish-fly  plugin. 
                 pip install datasette-publish-fly
datasette publish fly mydatabase.db --app="my-app" 
                 Consult the  datasette-publish-fly README  for more details.

references:
[{"href": "https://fly.io/", "label": "Fly"}, {"href": "https://fly.io/docs/pricing/", "label": "competitively priced"}, {"href": "https://github.com/simonw/datasette-publish-fly", "label": "datasette-publish-fly"}, {"href": "https://github.com/simonw/datasette-publish-fly/blob/main/README.md", "label": "datasette-publish-fly README"}]


id: publish:publish-custom-metadata-and-plugins
page: publish
ref: publish-custom-metadata-and-plugins
title: Custom metadata and plugins
breadcrumbs: ["Publishing data", "datasette publish"]
content:
datasette publish  accepts a number of additional options which can be used to further customize your Datasette instance. 
                 You can define your own  Metadata  and deploy that with your instance like so: 
                 datasette publish cloudrun --service=my-service mydatabase.db -m metadata.json 
                 If you just want to set the title, license or source information you can do that directly using extra options to  datasette publish : 
                 datasette publish cloudrun mydatabase.db --service=my-service \
    --title="Title of my database" \
    --source="Where the data originated" \
    --source_url="http://www.example.com/" 
                 You can also specify plugins you would like to install. For example, if you want to include the  datasette-vega  visualization plugin you can use the following: 
                 datasette publish cloudrun mydatabase.db --service=my-service --install=datasette-vega 
                 If a plugin has any  Secret configuration values  you can use the  --plugin-secret  option to set those secrets at publish time. For example, using Heroku with  datasette-auth-github  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

references:
[{"href": "https://github.com/simonw/datasette-vega", "label": "datasette-vega"}, {"href": "https://github.com/simonw/datasette-auth-github", "label": "datasette-auth-github"}]


id: publish:cli-package
page: publish
ref: cli-package
title: datasette package
breadcrumbs: ["Publishing data"]
references: [{"href": "https://www.docker.com/docker-mac", "label": "Docker for Mac"}]
content:
If you have docker installed (e.g. using  Docker for Mac ) you can use the  datasette package  command to create a new Docker image in your local repository containing the datasette app bundled together with one or more SQLite databases: 
             datasette package mydatabase.db 
             Here's example output for the package command: 
             datasette package parlgov.db --extra-options="--setting sql_time_limit_ms 2500"
Sending build context to Docker daemon  4.459MB
Step 1/7 : FROM python:3.11.0-slim-bullseye
 ---> 79e1dc9af1c1
Step 2/7 : COPY . /app
 ---> Using cache
 ---> cd4ec67de656
Step 3/7 : WORKDIR /app
 ---> Using cache
 ---> 139699e91621
Step 4/7 : RUN pip install datasette
 ---> Using cache
 ---> 340efa82bfd7
Step 5/7 : RUN datasette inspect parlgov.db --inspect-file inspect-data.json
 ---> Using cache
 ---> 5fddbe990314
Step 6/7 : EXPOSE 8001
 ---> Using cache
 ---> 8e83844b0fed
Step 7/7 : CMD datasette serve parlgov.db --port 8001 --inspect-file inspect-data.json --setting sql_time_limit_ms 2500
 ---> Using cache
 ---> 1bd380ea8af3
Successfully built 1bd380ea8af3 
             You can now run the resulting container like so: 
             docker run -p 8081:8001 1bd380ea8af3 
             This exposes port 8001 inside the container as port 8081 on your host machine, so you can access the application at  http://localhost:8081/ 
             You can customize the port that is exposed by the container using the  --port  option: 
             datasette package mydatabase.db --port 8080 
             A full list of options can be seen by running  datasette package --help : 
             See  datasette package  for the full list of options for this command.


