[Package] Typesense Search Engine - Fast, Typo-Tolerant Search
-
Sure, i’m using webhooks from Ghost (Settings >Integrations > Custom) to n8n when a post is created, updated, unpublished, or deleted and then n8n updates Typesense.
-
Will share more later, if you’d like more detail.
-
More details:
My integration focuses on two main parts: (1) setting up webhooks in Ghost to trigger real-time sync via n8n to Typesense, and (2) embedding the search UI directly in the Ghost theme for the frontend.
Ghost Webhook Setup (for Real-Time Sync)
In Ghost Admin (yourblog.com/ghost/#/integrations), I created a Custom Integration called "Typesense n8n Integration." This gives you a Content API key (used in n8n to fetch full post/page data).
Then, add webhooks for key events (publish, update, unpublish, delete). Each points to an n8n webhook URL:
-
Post published or Post updated:
https://n8n.yourdomain.com/webhook/ghost-typesense
(n8n workflow: Receives minimal payload with post ID, fetches full data via Ghost Content API/posts/{id}?key=CONTENT_API_KEY&include=tags,authors
, transforms to Typesense schema, upserts.) -
Post unpublished or Post deleted:
https://n8n.yourdomain.com/webhook/ghost-typesense-delete
(n8n: Extracts ID, sends DELETE to Typesense/collections/ghost/documents/{id}
.)
Ghost webhooks send limited data (e.g., just ID/slug), so n8n always fetches the full object. This keeps Typesense updated in near real-time (n8n adds ~30-60s delay, but it's seamless for users).
For initial bulk sync: Separate n8n workflows paginate Ghost API (
/posts/?key=CONTENT_API_KEY&limit=100&page=N&filter=status:published
), transform to NDJSON, and bulk import to Typesense withaction=upsert
.Frontend UI Integration in Ghost Theme
I used the official Typesense InstantSearch Adapter. In your custom theme's
default.hbs
(head section):<script src="https://cdn.jsdelivr.net/npm/typesense-instantsearch-adapter@2/dist/typesense-instantsearch-adapter.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4"></script> <script src="{{asset 'js/search.js'}}"></script> <!-- Custom init script --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@8/themes/reset-min.css"> <link rel="stylesheet" href="{{asset 'css/search.css'}}"> <!-- Custom styles -->
In
assets/js/search.js
, initialize InstantSearch with your search-only Typesense key, query params (e.g.,query_by: 'title,excerpt,plaintext,tags.name,authors.name'
), and a custom hit template to display tags/authors:// Simplified excerpt const search = instantsearch({ indexName: 'ghost', searchClient: typesenseInstantsearchAdapter.searchClient }); search.addWidgets([ instantsearch.widgets.searchBox({ container: '.js-search-input' }), instantsearch.widgets.hits({ container: '.js-search-results', templates: { item: hit => ` <article> <h2><a href="${hit.url}">${hit.title}</a></h2> <p>${hit.excerpt}</p> <div>By ${hit.authors?.map(a => a.name).join(', ') || 'Unknown'}</div> <div>${hit.tags?.map(t => `<span class="tag">#${t.name}</span>`).join(' ') || ''}</div> </article> ` } }), // Add refinementList for facets (tags, authors, type) ]); search.start(); // Bind to theme's search button/modal
Styles in
search.css
for cards, facets sidebar, etc. Trigger the modal via a button in your header partial (e.g.,<button class="js-search-button">Search</button>
).This gives full control—e.g., facets for filtering by tags.
After making changes, Zip your theme and upload it in Ghost Admin and restart Ghost.
Hope this helps as an example!
-
-
Thanks James -- I'm confused though as both of those files were included.
-
@Dave-Swift
Yes they are in included in the git repo, indeed.
But your code block for copy-paste installing was only:cloudron install --image clientamp/typesense-cloudron:29.0.0 --location $YOUR_CUSTOM_LOCATION
Without knowing that one needs the other files locally, just this command will fail the installation.
It is just a failsafe to prevent posts like "the provided command failed with xyz" if people simple copy-paste. -
@vladimir.d has been cleaning this up. Unfortunately, we hit a road block - https://typesense.org/docs/guide/backups.html#backup-steps . This means Cloudron backup strategy as it is right now won't work since Typesense is like a database.
-
@vladimir.d has been cleaning this up. Unfortunately, we hit a road block - https://typesense.org/docs/guide/backups.html#backup-steps . This means Cloudron backup strategy as it is right now won't work since Typesense is like a database.
@girish why is that a blocker? As it points out it can generate a snapshot from an api call, drop it in /app/data and then you can ignore the data dir during backup since it would be redundant and potentially inconsistent.
But even if you leave it, you would just untar that snapshot over it to make it consistent.
-
the blocker here is, that Cloudron has no way currently to trigger app internal scripts at the point of creating a backup. For database addons, there are special hooks to do so. For sqlite (which is using /app/data via localStorage addon) we have added specific code to issue a db dump. So something like this is required for typesense also then.
-
Hi @girish, @nebulon, and team,
I've updated the Typesense package to address the backup roadblock from the docs (https://typesense.org/docs/guide/backups.html#backup-steps). This should make it compatible with Cloudron's strategy for database-like apps.
Key changes in v29.0.1:
- Added "localstorage" addon for persistent /app/data (includes DB files and snapshots).
- Defaulted data-dir to /app/data in start.sh.
- Added backup.sh to trigger a full server snapshot via /operations/snapshot API, saving to snapshots/latest.tar (relative to /app/data). It backs up all collections automatically and checks for success.
- Added restore.sh to untar latest.tar on startup if present, ensuring consistent restores.
- Improved logging for the API key in start.sh.
Tested on my live install (search.daveswift.com with existing collections)—backup creates the snapshot file, and restore loads it on restart without issues. The snapshot is a complete dump of all data, per Typesense docs.
Repo: https://github.com/clientamp/typesense-cloudron (changes on main, see CHANGELOG.md). Latest image: clientamp/typesense-cloudron:29.0.1 (or timestamped builds like 20250929-111100).
For integration, Cloudron could run backup.sh via a hook before filesystem backup, then ignore raw DB files. Happy to add more (e.g., compression).
Thanks!