Astro DB is a SQL database service created specifically for Astro ( v4.5 or later). One of its goals is
to provide an API for directly interacting with a SQL database inside an Astro project. This opens up serveral use-cases
which include full-text search (FTS).
Internally, Astro DB partnered with Turso as their DB hosting provider. The underlying SQL language used by
Turso is a fork of SQLite called: libSQL . For the context of this guide, this basically means that we can
leverage existing SQLite features including FTS5 . This feature allows us to perform full-text search queries and
is enabled by default through Turso’s Extensions .
Lets start by creating a Virtual Table using the FTS5 virtual table module. We can place this logic
within db/seed.ts so it is run automatically when Astro is started locally:
Prepare the data we want to INSERT into the Virtual Table. The following example will be working with a collection
of blog posts.
Create the Virtual Table using the FTS5 virtual table module and insert the data we prepared in the previous step.
We DROP the table initially to ensure a clean slate and prevent duplicate entries during local development.
Columns slug and title are unindexed as we only care to preform full-text searches against content .
When working with Astro DB, table schemas are defined within db/config.ts and will automatically generate TypeScript
types for each table. Unfortunately, this does not apply to Virtual Tables. For that, we must manually define schemas
using Zod . We can set this up within db/config.ts to keep our schema definitions centralized:
Now that we have our FTS5 Virtual Table setup with proper type-safety, we can create a page that will allow us to
perform full-text searches.
This example will be using SSR to handle form submissions and to re-render the page with search results.
This can also be achieved using Hybrid Mode in combination with Page Partials . The
attached repository below explores the latter approach.
We can create a simple search form with submission validation via Zod. Within the Zod schema, we wrap the raw query with
double quotes to allow special characters. Without this, SQLite will throw an error when trying to perform a full-text
search. Also, note the * character at the end of the schema which allows for partial or fuzzy matching.
With the search form in place, we can now perform a full-text search against the PostSearch FTS5 Virtual Table. We
check the validity of the query string and if it passes, we can proceed with the full-text search using the MATCH
operator:
To this point, we have setup full-text search in our Astro project for local development. We need to account for pushing
this FTS5 Virtual Table to production and populating it with data:
Similar to the non-production seed file, we need to prepare the data we want to INSERT or UPDATE into the
Virtual Table. We can place this logic within db/seed.prod.ts :
Compose a batch of SQL queries to do the following:
Create the Virtual Table if it does not exist.
UPDATE existing rows if their content has changed.
INSERT new rows if they do not exist.
Finally, we need a way of executing the db/seed.prod.ts file. This can be done manually by running:
npx astro db execute db/seed.prod.ts --remote . However, it is recommended to automate this process by baking
it into a CI/CD pipeline.
Here is an example using the _studio.yml action that Astro automatically generates when connecting a project
to Astro DB:
By no means is this the only way to implement full-text search within an Astro project. This guide is meant to provide a
starting point for those looking to leverage Astro DB and SQLite’s FTS5 virtual table module. Here are a few additional
libraries to consider which accomplish the same goal:
Pagefind : Fully static search library that aims to perform well on large sites.
Fuse.js : Powerful, lightweight fuzzy-search library, with zero dependencies.
Orama : Full-text search engine entirely written in TypeScript, with zero dependencies.