Run a Datasette Background Job
Build a Datasette plugin that fetches data and writes results in the background.
Introduction
30 Second Summary
Some jobs take long enough to make a website feel stuck. A useful site keeps serving pages while that work continues in the background.
In this project, you will build a one-file Datasette 1.0a40 plugin that runs a background task. You will watch fetched results reach a SQLite table while Datasette keeps serving pages.
What You'll Build
You will refresh a table in your browser to watch new fetch results appear while the rest of your Datasette site keeps responding.
By the end of this project, you'll have:
- A live background job that adds rows on a schedule while the site stays responsive.
- An HTTP fetch history that shows each response status code plus its byte count.
- A clean shutdown where the terminal confirms that the running task was cancelled.
- Secret Mission: An optional challenge to push your skills further.
Are there any prerequisites?
You need Python, pip, basic SQL, a terminal, plus a web browser.
Everything runs locally at no cost.
Before We Start
Your background task eventually needs to keep running while your Datasette site serves pages. That test depends on your local tools being ready before the project files exist.
This step confirms your terminal is available. It also checks Python 3.9 or later.
A browser check completes your local workspace. Your project itself stays untouched until the next step.
In this step, get ready to:
- Open a terminal that can run the project commands.
- Confirm Python 3.9 or later is available.
- Confirm your web browser can load the pages you will use.
Open your terminal
The terminal gives you a place to run every setup command in this project. A visible prompt confirms the shell is accepting input.
macOS
- Press Cmd+Space to open Spotlight.
- Type Terminal into the search field.
- Press Return to open Terminal.
You should see a Terminal window with a prompt where you can enter a command.
Windows
- Press the Windows key to open the search bar.
- Type Windows PowerShell into the search bar.
- Select Windows PowerShell from the search results.
You should see a PowerShell window with a prompt where you can enter a command.
Check your Python version
This project uses Python 3.9 or later as its local runtime baseline. A version check tells you whether your current interpreter is ready.
macOS
- Check your installed Python version by running this command:
python3 --version
What does this command do?
The python3 command selects the Python 3 interpreter. The --version option prints its version number before exiting.
You should see Python followed by a version number.
Windows
- Check your installed Python version by running this command:
python --version
What does this command do?
The python command selects the default Python interpreter. The --version option prints its version number before exiting.
You should see Python followed by a version number.
- Choose the outcome below that matches the result in your terminal.
✔️ I see a supported version
That baseline is confirmed. Your Python interpreter is ready for the local environment you will create next.
ⓧ I see an older version
An older interpreter can make later dependency behavior unpredictable. Updating now gives this project a consistent baseline.
- Visit the official Python downloads page.
- Download a current supported Python release for your operating system.
- Run the downloaded installer.
- Close your terminal window after the installation completes.
- Use the earlier operating system instructions to start a new terminal window.
- Repeat the Python version check for your platform.
You should now see Python 3.9 or later in the output.
Still seeing the older version?
- Close every terminal window before starting a fresh one so the new installation can be detected.
- Check that you downloaded a current supported release from the official Python downloads page.
- Help me fix my Python version check.
ⓧ Command not found
Your terminal cannot currently find a Python interpreter. Installing a supported release adds the runtime needed for this project.
- Visit the official Python downloads page.
- Download a current supported Python release for your operating system.
- Run the downloaded installer.
- Close your terminal window after the installation completes.
- Use the earlier operating system instructions to start a new terminal window.
- Repeat the Python version check for your platform.
You should now see Python 3.9 or later in the output.
Python still unavailable?
- Confirm the installer finished before starting the new terminal window.
- Check that you downloaded the installer for your operating system.
- Help me make Python available in my terminal.
Confirm your browser
Datasette displays databases through pages served to your browser. Loading a trusted page confirms that the browser you will use can open web content.
Before the final check, do you expect your browser page and Python version check to succeed without creating any project files?
- Open the official Python homepage in your web browser.
- Switch back to this project tab after the page loads.
- Return to the terminal from earlier.
- Repeat the Python version check for your platform.
You should see the official Python page in your browser. Your terminal should still report Python 3.9 or later.
You're set. Your terminal, Python runtime, and browser can support the local Datasette work ahead. Next up, you'll create the environment that runs your Datasette site.
Create Your Datasette Database
Your background plugin needs a local site to run inside. It also needs a database where its work can become visible.
In this step, you will isolate Datasette version 1.0a40 inside a virtual environment. You will create two tables in a SQLite database. You will then launch the site in your browser.
In this step, get ready to:
- Create an isolated Python environment that contains Datasette version 1.0a40.
- Create background-demo.db with the two tables needed by the project.
- Start Datasette with the database loaded in your browser.
Create and activate a virtual environment
A virtual environment keeps this project's packages inside one folder. Activating it makes the terminal use that isolated copy of Python.
The terminal from earlier is already positioned inside your project workspace.
macOS
- Create the venv folder and activate its environment by running these commands:
python3 -m venv venv
source venv/bin/activate
What do these commands do?
- The first command creates an isolated Python environment inside the venv folder.
- The second command updates the current terminal session to use that environment.
You should see (venv) at the start of your terminal prompt. That prefix confirms the environment is active.
Missing the environment prefix?
- Check that your terminal is still inside the project workspace.
- Confirm that the venv folder exists before running the activation command again.
- Help me activate my Python virtual environment.
Windows
- Create the venv folder and activate its environment in PowerShell by running these commands:
py -m venv venv
venv\Scripts\Activate.ps1
What do these commands do?
- The first command creates an isolated Python environment inside the venv folder.
- The second command updates the current PowerShell session to use that environment.
You should see (venv) at the start of your PowerShell prompt. That prefix confirms the environment is active.
Activation blocked in PowerShell?
- Check that your terminal is still inside the project workspace.
- Confirm that the venv folder exists before trying the activation command again.
- Help me activate my Python virtual environment in PowerShell.
The active environment may already contain Datasette. A version check tells you which setup path applies.
- Check which Datasette version the active environment can run by running this command:
datasette --version
What does this command check?
The command asks the active environment for its installed Datasette version. The result determines whether you can continue or need to install the pinned release.
✔️ I see version 1.0a40
Your active environment already contains Datasette 1.0a40. Your terminal is ready for the database setup.
ⓧ I see an older version
The project uses the background-task API from Datasette 1.0a40. Pinning the package gives every later step the expected API.
- Install the pinned release and verify it by running these commands:
python -m pip install datasette==1.0a40
datasette --version
What do these commands do?
- The first command installs Datasette 1.0a40 into the active environment.
- The second command prints the installed version for confirmation.
The final line should include version 1.0a40. Your environment now has the release used throughout this project.
Datasette installation failed?
- Confirm that (venv) still appears at the start of your terminal prompt.
- Check your network connection before running the installation command again.
- Help me install the pinned Datasette release.
ⓧ Command not found
The active environment does not contain Datasette yet. Installing the exact release prepares it for the background-task API used later.
- Install the pinned release and verify it by running these commands:
python -m pip install datasette==1.0a40
datasette --version
What do these commands do?
- The first command installs Datasette 1.0a40 inside the active environment.
- The second command checks that the new command is available.
The final line should include version 1.0a40. Your isolated environment is now ready.
Datasette still not found?
- Confirm that (venv) still appears at the start of your terminal prompt.
- Run the installation command again after checking your network connection.
- Help me find Datasette in my virtual environment.
Build the SQLite tables
The plugin needs one table to represent source content. It needs another table to record each background result.
How will the tables be used?
The source_data table gives the database a small source table with an ID and a name. The background_jobs table reserves columns for a timestamp plus the HTTP result collected later.
- Create background-demo.db with both tables by running this command:
python -c "import sqlite3; connection = sqlite3.connect('background-demo.db'); connection.executescript('CREATE TABLE source_data (id INTEGER PRIMARY KEY, name TEXT); CREATE TABLE background_jobs (id INTEGER PRIMARY KEY, created_at TEXT, status_code INTEGER, byte_count INTEGER);'); connection.close()"
What does this command do?
- The connection creates background-demo.db inside the project workspace.
- The SQL script creates source_data with its ID and name columns.
- The same script creates background_jobs with columns for the future task results.
- Closing the connection finishes the database setup.
- Inspect the saved table definitions by running this command:
python -c "import sqlite3; connection = sqlite3.connect('background-demo.db'); print(*connection.execute('SELECT name, sql FROM sqlite_master WHERE type=? ORDER BY name', ('table',)), sep='\n'); connection.close()"
What does this check show?
The query reads the table names and their saved definitions from the database schema. Sorting by name makes the output easy to compare.
You should see definitions for background_jobs and source_data in the output. No background task rows exist yet.
Missing a table definition?
- Check that both table definitions appear inside the database creation command.
- Confirm that the database filename is background-demo.db in both commands.
- Help me fix my SQLite database schema.
Start Datasette and confirm the site
Datasette turns the database file into a local website. Its home page provides the first visible check that both tables loaded correctly.
Before you start the server, which two table names do you expect the home page to show?
- Start Datasette with the database loaded and open its home page by running this command:
datasette serve background-demo.db -o
What does this command do?
- The serve command starts a local Datasette server.
- The database filename tells Datasette which SQLite file to load.
- The -o option opens the site in your browser.
- The terminal stays occupied while the server handles browser requests.
Your browser should open the Datasette home page. You should see source_data and background_jobs listed as tables in background-demo.
That is the first working slice of your project. Datasette is serving the database while the terminal keeps the local server running.
Browser did not open?
- Copy the local address printed by Datasette into your browser address bar.
- Confirm that the terminal still shows the running server process.
- Check that background-demo.db is spelled exactly as shown in the command.
- Help me open my local Datasette site.
✔️ Awesome, I've got everything!
Your active environment contains Datasette 1.0a40. Your browser shows both tables from background-demo.db.
ⓧ I'd like to double check the full code
Compare your finished setup with these project artifacts.
- The venv folder exists inside the project workspace. Its environment is active and contains Datasette 1.0a40.
- The background-demo.db database contains source_data with id INTEGER PRIMARY KEY and name TEXT.
- The background-demo.db database contains background_jobs with id INTEGER PRIMARY KEY, created_at TEXT, status_code INTEGER, and byte_count INTEGER.
- The background_jobs table has no background task rows yet.
- Datasette is running with background-demo.db loaded. Its home page is open in your browser.
Your local Datasette site now has a database ready for background results. Next up, you will load a plugin during server startup and prove that Datasette can see it.
Register a Startup Plugin
Your local Datasette site already serves both SQLite tables. It needs a loading point for your project-specific behavior.
In this step, you will create a one-file plugin that connects to Datasette's startup lifecycle. A confirmation message in the terminal proves that Datasette discovers your code when the server boots.
In this step, get ready to:
- Prepare a directory for your local plugin.
- Register a startup hook in plugins/background_task.py.
- Restart Datasette with plugin discovery enabled.
Prepare the plugin workspace
Datasette currently occupies the terminal from the previous step. Stopping that process returns the prompt without deactivating your virtual environment.
- Switch back to the terminal from the previous step.
- Stop the running server by pressing Ctrl+C.
You will see the command prompt again. Your activated virtual environment remains ready.
- Create the plugins directory by running these commands:
mkdir -p plugins
ls
What do these commands do?
- The mkdir -p plugins command creates the directory that holds your local plugin.
- The ls command lists the current workspace so you can confirm the directory exists.
You should see plugins listed beside background-demo.db.
Do not see the plugins directory?
- Check that your terminal is still inside the project workspace containing background-demo.db.
- Run the directory command again if plugins is missing from the listing.
- Help me create the plugins directory in the correct workspace.
Connect to Datasette's startup lifecycle
The startup() hook runs when the Datasette application server starts. The @hookimpl decorator tells Datasette that your function implements this hook.
- Create plugins/background_task.py with the startup hook by running this terminal block:
cat > plugins/background_task.py <<'PY'
from datasette import hookimpl
@hookimpl
def startup(datasette):
# Confirm that Datasette loaded this plugin during startup
print("Background task plugin started")
PY
What does this code do?
- The cat command writes the content between the two markers into plugins/background_task.py.
- The hookimpl import provides Datasette's plugin hook decorator.
- The startup(datasette) function receives the running Datasette instance when the server boots.
- The print() call provides visible proof that the hook ran.
- Confirm that the plugin file exists by running this command:
ls plugins
What should I see?
You should see background_task.py in the terminal output. This confirms that Datasette has a plugin module to load.
Do not see background_task.py?
- Check that the opening path in the terminal block is exactly plugins/background_task.py.
- Check that the final marker is written as PY on its own line.
- Help me debug why the plugin file was not created.
✔️ Awesome, I've got everything!
Your startup plugin is saved in plugins/background_task.py.
ⓧ I'd like to double check the full code
Compare your plugins/background_task.py file with this complete version.
from datasette import hookimpl
@hookimpl
def startup(datasette):
# Confirm that Datasette loaded this plugin during startup
print("Background task plugin started")
Prove the plugin loads
A one-off plugin loads only when Datasette receives the plugin directory option. The server stays attached to this terminal while it serves your database.
Before you run the server, do you expect the confirmation message to appear before or after you refresh the browser?
- Restart Datasette with the plugins directory enabled by running this command:
datasette background-demo.db --plugins-dir=plugins/
What should I see?
You should see Background task plugin started in the terminal during startup. That line proves Datasette discovered the module and called its startup hook.
The --plugins-dir=plugins/ option tells Datasette where to find this project-specific plugin. Keep the server running for the browser check.
- Switch back to the Datasette browser tab from the previous step.
- Refresh the home page.
You should still see source_data plus background_jobs on the home page. That confirms the database remains available with the plugin enabled.
Missing the startup confirmation?
- Check that the command includes --plugins-dir=plugins/ exactly.
- Check that plugins/background_task.py imports hookimpl from Datasette.
- Check that @hookimpl sits directly above def startup(datasette):.
- Help me debug why Datasette is not loading my startup plugin.
That is the plugin loading path proven. Next, you will give this startup hook a background task that writes rows while the site keeps responding.
Write Rows in the Background
Your plugin now announces itself when Datasette starts. The next goal is to make that plugin perform useful work without holding up the site.
A background task gives Datasette a supervised place for a repeating loop. This step uses that loop to add timestamps to SQLite while browser requests keep moving.
In this step, get ready to:
- Build an asynchronous loop that writes timestamps every five seconds.
- Register the loop with Datasette during startup.
- Watch the table gain rows while the site remains responsive.
Build the timestamp loop
An asynchronous loop can pause between writes without freezing the server. Datasette's execute_write() method sends each insert through the database's write connection.
- Return to plugins/background_task.py in your editor.
- Insert the loop between the existing import and startup hook by pasting this block:
import asyncio
async def write_timestamps(datasette):
# Use Datasette's write queue for safe background writes.
db = datasette.get_database("background-demo")
while True:
await db.execute_write(
"insert into background_jobs (created_at) values (datetime('now'))"
)
# Yield to the server between timestamp inserts.
await asyncio.sleep(5)
What does this loop do?
- The write_timestamps() function receives the running Datasette instance.
- The get_database() call selects background-demo for each write.
- The execute_write() call inserts a value into created_at through Datasette's write queue.
- The insert leaves status_code unset. It also leaves byte_count unset.
- The asyncio.sleep() pause gives the server time to handle browser requests between inserts.
- Save plugins/background_task.py.
- Switch back to the terminal running Datasette.
- Press Ctrl+C to stop the current server.
- Press the Up Arrow key to recall the previous Datasette command.
- Press Enter to restart the server.
The terminal shows the startup confirmation from your existing hook. This proves the new loop has valid Python syntax.
Server not starting?
- Check that import asyncio starts at the left edge of the file.
- Check that every line inside write_timestamps() uses consistent indentation.
- Compare the parentheses around execute_write() with the code block above.
- Help me fix my timestamp loop.
Register the task at startup
The loop now exists as a callable function. Datasette starts supervising it after every startup hook has finished.
- Return to plugins/background_task.py in your editor.
- Replace the existing startup() function with this registered version:
@hookimpl
def startup(datasette):
# Let Datasette supervise the timestamp loop through shutdown.
datasette.add_background_task(
write_timestamps, name="background-timestamp-writer"
)
How does registration work?
- The startup() hook registers work when Datasette boots.
- The add_background_task() method receives the asynchronous function itself.
- Datasette passes its running instance into write_timestamps() when the task starts.
- The task name identifies this loop inside Datasette's background-task supervision.
- Save plugins/background_task.py.
- Switch back to the terminal running Datasette.
- Press Ctrl+C to stop the server.
- Press the Up Arrow key to recall the previous Datasette command.
- Press Enter to start the updated plugin.
The server returns to its running state without a traceback. The registered loop begins writing its first timestamp.
Task not registering?
- Check that @hookimpl sits directly above startup().
- Check that the registered function is spelled write_timestamps.
- Check the terminal traceback for a line number inside plugins/background_task.py.
- Help me debug my task registration.
✔️ Awesome, I've got everything!
Great. Your plugin now defines the timestamp loop and registers it as supervised background work.
ⓧ I'd like to double check the full code
Compare plugins/background_task.py with this complete version.
from datasette import hookimpl
import asyncio
async def write_timestamps(datasette):
# Use Datasette's write queue for safe background writes.
db = datasette.get_database("background-demo")
while True:
await db.execute_write(
"insert into background_jobs (created_at) values (datetime('now'))"
)
# Yield to the server between timestamp inserts.
await asyncio.sleep(5)
@hookimpl
def startup(datasette):
# Let Datasette supervise the timestamp loop through shutdown.
datasette.add_background_task(
write_timestamps, name="background-timestamp-writer"
)
Watch the table grow
Each completed pass through the loop leaves one row in background_jobs. The rising row count becomes a visible heartbeat for the task.
- Return to the Datasette page from earlier in your browser.
- Click background-demo on the home page.
- Click background_jobs in the table list.
- Record the current row count here: your current row count.
Before you refresh, how much do you expect the row count to change after five seconds?
- Wait five seconds.
- Refresh the browser page.
You see a higher row count than your current row count. Each row has a timestamp under created_at.
The status_code cells remain blank. The byte_count cells also remain blank.
- Click background-demo in the page breadcrumb.
- Click source_data in the table list.
The source_data page loads normally while the timestamp loop keeps running.
Before the final refresh, do you expect opening another table to have paused the background task?
- Return to the background_jobs table.
- Refresh the table page.
You see more timestamp rows than before. That is the key proof: your task keeps writing while Datasette continues serving other pages.
Row count not increasing?
- Wait at least five seconds before refreshing the table.
- Check the server terminal for a traceback mentioning the background task.
- Confirm that the database name inside get_database() is background-demo.
- Help me find why no rows are appearing.
Your plugin now writes in the background while the site stays responsive. Next up, you replace the timestamp-only loop with an internal HTTP request that records a real response.
Fetch and Record Internal Data
Your background loop already proves Datasette can write on a schedule. A timestamp alone does not show that the plugin can call back into the site.
Now you'll turn each cycle into an internal HTTP check against the local background_jobs JSON page. Each new SQLite row captures response metadata from that check.
In this step, get ready to:
- Replace the timestamp-only loop with an internal JSON request.
- Record the response status code and byte count.
- Verify response values while other Datasette pages remain available.
Fetch the JSON page inside the task
Datasette's internal client sends a simulated HTTP request through the running app. The returned response exposes metadata that your loop can save.
- Return to plugins/background_task.py in your editor.
- Replace the existing contents of plugins/background_task.py with this code:
import asyncio
from datetime import datetime, timezone
from datasette import hookimpl
async def background_loop(datasette):
# Use Datasette's managed connection for database writes.
db = datasette.get_database("background-demo")
while True:
# Fetch the table's JSON through the running Datasette app.
response = await datasette.client.get(
"/background-demo/background_jobs.json"
)
# Store when the request ran plus its response metadata.
await db.execute_write(
"insert into background_jobs "
"(created_at, status_code, byte_count) values (?, ?, ?)",
(
datetime.now(timezone.utc).isoformat(),
response.status_code,
len(response.content),
),
)
await asyncio.sleep(5)
@hookimpl
def startup(datasette):
# Register the loop for Datasette to supervise after startup.
datasette.add_background_task(background_loop)
What does this code do?
- The db variable holds Datasette's managed connection to background-demo.
- The datasette.client.get() call routes the JSON request through the running Datasette app.
- The response.status_code value records the result of the internal request.
- The len(response.content) expression measures the response body in bytes.
- The db.execute_write() call queues the insert through Datasette's write connection.
- The asyncio.sleep(5) call pauses the loop for five seconds before its next request.
- Save plugins/background_task.py.
- Switch back to the terminal running Datasette.
- Press Ctrl+C to stop the current server.
Your terminal returns to its prompt. The updated plugin loads the next time Datasette starts.
- Press the Up Arrow key to recall the Datasette command from earlier.
- Press Enter to restart Datasette with that command.
You'll see the local server start again. The terminal should show no traceback.
Server not starting cleanly?
- Check that the terminal prompt still shows your activated virtual environment.
- Compare the indentation inside background_loop() with the code block above.
- Confirm the request path contains background-demo followed by background_jobs.json.
- Help me debug my Datasette background task restart.
Verify recorded response data
The browser table proves that one loop iteration completed both the internal request and the database write. A second refresh proves that the work continues on schedule.
Before you refresh, which three columns do you expect the newest rows to fill?
- Switch back to the browser tab showing the background_jobs table.
- Refresh the page.
The newest rows show an ISO timestamp under created_at. They also show integer values under status_code and byte_count.
Earlier timestamp-only rows can still have blank response fields. The populated values appear on rows created after this restart.
- Wait at least five seconds.
- Refresh the background_jobs table again.
You'll see another row with a higher id. Its response fields are populated too.
- Click background-demo in the page breadcrumb.
The database page loads while the task keeps running. You'll see both source_data and background_jobs listed.
- Click the background_jobs table link to return to its rows.
You have the full loop working. Every scheduled pass now leaves evidence of its internal request in SQLite.
Still seeing blank response fields?
- Look at the rows with the highest id values because older rows only contain timestamps.
- Confirm that the saved insert statement names created_at, status_code, and byte_count.
- Check the running terminal for a traceback from the background task.
- Help me find why my newest rows have blank response fields.
✔️ Awesome, I've got everything!
Your saved plugin now performs an internal request during every scheduled cycle.
ⓧ I'd like to double check the full code
Compare your complete plugins/background_task.py file with this version.
import asyncio
from datetime import datetime, timezone
from datasette import hookimpl
async def background_loop(datasette):
# Use Datasette's managed connection for database writes.
db = datasette.get_database("background-demo")
while True:
# Fetch the table's JSON through the running Datasette app.
response = await datasette.client.get(
"/background-demo/background_jobs.json"
)
# Store when the request ran plus its response metadata.
await db.execute_write(
"insert into background_jobs "
"(created_at, status_code, byte_count) values (?, ?, ?)",
(
datetime.now(timezone.utc).isoformat(),
response.status_code,
len(response.content),
),
)
await asyncio.sleep(5)
@hookimpl
def startup(datasette):
# Register the loop for Datasette to supervise after startup.
datasette.add_background_task(background_loop)
Your task now fetches the site's JSON internally. Next, you'll test its clean shutdown.
Verify Graceful Shutdown
Your plugin now fetches data through Datasette’s internal client. It records the results without blocking the pages you browse.
A reusable worker also needs a clean ending. A task that survives server shutdown can keep the process open.
Datasette supervises work registered with add_background_task(). This step tests how that supervision handles graceful shutdown.
In this step, get ready to:
- Stop the running Datasette server with Ctrl+C.
- Inspect the terminal’s shutdown result.
- Compare the result with the official changelog.
Stop the background task cleanly
A graceful shutdown closes active work in a defined order. The running terminal gives you direct evidence of how your background task responds.
- Switch back to the terminal running your Datasette server.
Before you stop the server, what do you expect the active task to do? Your prediction gives you a concrete result to test.
- Press Ctrl+C once.
- Read the final terminal lines after the shell prompt returns.
You should see output confirming that the active background task was cancelled during shutdown. Datasette then returns you to the shell prompt.
That is the lifecycle test passed: your scheduled work stops without leaving the server process hanging.
Server Still Running?
- Confirm that you pressed Ctrl+C in the terminal running Datasette.
- Allow five seconds for the task to respond to cancellation.
- Ask for help if the terminal does not return to the shell prompt.
Compare the result with the changelog
Your terminal shows what happened in this run. The official release notes define the behavior that Datasette promises for supervised tasks.
Before you read the entry, which part of your terminal result do you expect the documentation to confirm? The comparison checks that your plugin follows the documented lifecycle.
- Open the official changelog entry for Datasette 1.0a40.
- Find the Background tasks heading.
- Read the bullet for add_background_task() to identify what happens to running tasks during shutdown.
The changelog says tasks registered with add_background_task() are cancelled on shutdown. It also documents a five-second grace period.
What Does Graceful Shutdown Protect?
Datasette cancels supervised tasks before it closes database connections. This gives each task a bounded opportunity to finish its cancellation path.
Stopping the server leaves background-demo.db in place. Your recorded rows remain available in the database.
Your plugins/background_task.py file also remains intact. You can reuse it as a template for future scheduled work.
Secret mission
Build a Background Task Health Report
Turn the rows in background_jobs into a read-only health report. Prove the worker keeps recording runs while the site remains responsive.
Clean Up Your Resources
Clean Up Your Resources
Everything in this Datasette project runs locally with open-source tools. Keeping the files creates no ongoing costs.
Choose whether to keep the workspace ready for more experiments, pause with the server stopped, or delete the local project entirely.
Resources you used:
- The background-demo.db SQLite database with its source_data table plus the rows recorded in background_jobs.
- The reusable plugins/background_task.py plugin template.
- The Python virtual environment in the project workspace containing Datasette 1.0a40.
Keep everything running
This option preserves the working example for future experiments. No cleanup action is needed.
- Keep plugins/background_task.py as a template for future scheduled work.
- Keep background-demo.db with its recorded timestamps plus its internal HTTP results.
- Keep the Python virtual environment so Datasette remains available in the project workspace.
Pause - I'll come back to this later
The Datasette server is already stopped. The background task uses no memory while the server remains offline.
- Leave the Datasette server stopped.
- Close the terminal window to end the active Python virtual environment session.
- Keep the project workspace folder in its current location for your next session.
Delete - I don't want to use this again
Deleting the project workspace is permanent. This removes the database, plugin, and virtual environment together.
- Copy plugins/background_task.py to another folder if you want to preserve the reusable template.
- Close the terminal window to end the active Python virtual environment session.
- Use Finder on macOS or File Explorer on Windows to locate the project workspace folder containing background-demo.db.
- Delete the project workspace folder from your file browser.
- Confirm that the project workspace folder no longer appears in Finder or File Explorer.
Nice Work!
Nice Work!
You did it! You built a reusable Datasette plugin that runs a background task while its SQLite site keeps serving pages.
You've learned how to:
- Built a local data source in background-demo.db. Watched background_jobs gain timestamp rows while the Datasette site stayed responsive.
- Registered a startup hook that hands asynchronous work to datasette.add_background_task(). Sent every insert through Datasette's write connection.
- Used datasette.client.get() for an internal HTTP request. Stored each response's status code. Recorded its byte count in the same row. Confirmed graceful cancellation after stopping the server with Ctrl+C. Matched that behavior against the 1.0a40 changelog.
- Secret Mission: Extended the reusable plugin template through an optional challenge that pushed your background-task skills further.
Ready to quiz yourself?