Global Search
[Live] — Fully implemented April 2026. Typesense container, search-indexer worker, Fastify routers, Next.js API routes, and GlobalSearch UI are all running in production.
Overview
Global Search provides a unified, typo-tolerant, cross-entity search experience across all three portals (Dashboard, DM, Manage). Users invoke it with Ctrl+K from anywhere in the application and get instant results across blogs, contacts, campaigns, activities, and twelve other entity types.
The search engine is Typesense — a self-hosted, open-source engine that stores its own indexed copy of the data and returns ranked results in under 10ms. PostgreSQL remains the source of truth; Typesense is a read replica for search only.
Documents in This Section
| File | Contents |
|---|---|
| architecture.md | Collections schema, sync strategy, multi-tenant isolation, API contract |
| implementation.md | Packages to create, file structures, code patterns, where to add sync calls |
| ui.md | GlobalSearch component, Ctrl+K modal design, portal integration |
Design Decisions
| Decision | Choice | Reason |
|---|---|---|
| Search engine | Typesense | Typo-tolerance, ranked results, federation search, simple Docker deploy |
| Sync strategy | BullMQ (event-driven) | Fits existing queue architecture; retries handle Typesense downtime |
| Initial index | Fresh writes only | Avoids bulk-import complexity at launch; historical data indexed as records are updated |
| API layer | Fastify proxy | JWT validated server-side; tenantId filter injected by API — Typesense key never exposed to browser |
| Multi-tenant isolation | tenantId filter field on every document | Single collection per entity type; tenant scope enforced at query time |
| Hosting | Self-hosted Docker on DigitalOcean | Same infra as everything else; Typesense Cloud can replace later with zero code changes |
Scope per Portal
| Portal | Who uses it | Search scope |
|---|---|---|
| Dashboard | Client user | Their tenant’s entities only |
| DM | Reviewer | Active tenant’s entities (cookie-gated) |
| Manage | Super admin | Cross-tenant — all entities, all tenants; each result shows tenant name |
Entity Coverage
Thirteen entity types are indexed across both tenant-scoped portals and Manage:
| Entity | Collection | Key text fields |
|---|---|---|
| Blog post | blogs | title, metaDescription |
| Social post | social_posts | bodyText, engagementHook |
| Landing page | landing_pages | title, metaDescription |
| Email newsletter | newsletters | subject, previewText |
| Activity | activities | label, notes |
| Campaign | campaigns | name |
| Content brief | content_briefs | title, topic, angle |
| Contact | contacts | name, email, company |
| Lead | leads | name, company, jobTitle |
| Keyword | keywords | keyword |
| Report | reports | label |
| Backlink | backlinks | sourceDomain, anchorText |
| Tenant | tenants | name, website, pocName, industry |
tenants is returned in Manage searches only. All other collections are available in
Dashboard, DM, and Manage.
High-Level Flow
WRITE PATH
──────────
API router / server action
→ writes record to Postgres (source of truth)
→ enqueueSearchSync({ collection, operation, recordId, tenantId })
↓
apps/servers/search-indexer (BullMQ worker)
→ reads full record from Postgres
→ upserts / deletes document in Typesense
→ eventual consistency: ~100–500ms after write
READ PATH
─────────
User presses Ctrl+K → types query
→ POST /v1/search (tenant-scoped) [Dashboard / DM]
→ POST /v1/admin/search [Manage]
↓
Fastify search router
→ validates JWT, resolves tenantId
→ calls Typesense multi-search (1 request across all collections)
→ injects filter_by: "tenantId:=<id>" (tenant-scoped routes)
→ returns unified ranked results with deep-link hrefs
↓
GlobalSearch modal renders grouped resultsInfrastructure
Typesense runs as a Docker container alongside the existing services. Add to
docker-compose.yml:
typesense:
image: typesense/typesense:27.1
restart: unless-stopped
ports:
- "8108:8108"
volumes:
- typesense-data:/data
command: >
--data-dir /data
--api-key=${TYPESENSE_ADMIN_API_KEY}
--enable-corsTwo environment variables are required across all services that touch Typesense:
TYPESENSE_URL=http://localhost:8108
TYPESENSE_ADMIN_API_KEY=your-admin-key-hereSee env-vars.md for the full per-service breakdown.