Skip to content

tharropoulos/typesense-ts

Repository files navigation

typesense-ts 🔍

A fully type-safe, unofficial Typesense client for Node.js written in TypeScript that provides compile-time validation on almost every parameter.

 

showcase.mp4

  Note: Although I maintain both this library and the main client, this library is unofficial and a passion project. For official support, please refer to the Typesense documentation.

Features

  • 🔒 Fully Type-Safe: Leverage TypeScript's type system for safer API interactions with compile-time validation
  • 🧠 Advanced Type Inference: Collection schemas, search parameters, and responses are strongly typed
  • ✅ Built-in Validation: Validate filter queries, sort expressions, and field configurations at compile-time
  • 🎯 Roadmap:
    • Collections management (create, update, delete, retrieve)Done!
    • Document operations (CRUD, bulk import)Done!
    • Search and multi-search with full parameter validationDone!
    • Faceting, groupingDone!
    • AliasesDone!
    • Curation & OverridesDone!
    • StopwordsDone!
    • AnalyticsDone!
    • Document exporting
    • Conversational Search
    • Api Key Management
    • Synonyms
  • ⚡ Hassle-Free: Efficient request handling with automatic node failover and health checking

Installation

# Using npm npm install typesense-ts@latest # Using pnpm pnpm add typesense-ts@latest # Using yarn yarn add typesense-ts@latest

Quick Start

1. Configure the Client

import { configure, setDefaultConfiguration } from "typesense-ts"; // Configure and set as default setDefaultConfiguration({ apiKey: "xyz", nodes: [ { url: "http://localhost:8108" }, // Or specify host/port/protocol separately: { host: "example.com", port: 8108, protocol: "https", path: "/typesense" }, ], // Optional parameters retryIntervalSeconds: 2, numRetries: 3, healthcheckIntervalSeconds: 30, additionalHeaders: { "Custom-Header": "value" }, });

2. Define a Collection Schema

import { collection } from "typesense-ts"; // Define a type-safe collection schema const booksSchema = collection({ name: "books", fields: [ { name: "title", type: "string" }, { name: "authors", type: "string[]" }, { name: "publication_year", type: "int32", sort: true }, { name: "ratings_count", type: "int32", facet: true }, { name: "average_rating", type: "float", facet: true }, { name: "categories", type: "string[]", facet: true }, ], default_sorting_field: "publication_year", }); // Register the collection globally for type safety declare module "typesense-ts" { interface Collections { books: typeof booksSchema.schema; } }

3. Create the Collection

// Create the collection in Typesense await booksSchema.create();

4. Perform Type-Safe Search

// Type-safe search with full autocomplete and validation const searchResults = await booksSchema.search({ q: "harry potter", query_by: ["title", "authors", "categories"], // ✅ Fully typed field names sort_by: "average_rating:desc", // ✅ Compile-time sort validation filter_by: "publication_year:>=2000 && average_rating:>=4", // ✅ Filter validation facet_by: ["categories", "average_rating"], // ✅ Only facetable fields allowed group_by: ["categories"], // ✅ Only facetable fields allowed page: 1, per_page: 10, highlight_fields: ["title", "authors"], // ✅ Only searchable fields allowed }); // Access strongly-typed results searchResults.hits.forEach((hit) => { console.log(hit.document.title); // ✅ Typed as string console.log(hit.document.publication_year); // ✅ Typed as number console.log(hit.highlight.title); // ✅ Access highlighted snippets });

Advanced Usage

Multi-Search Operations

import { multisearch, multisearchEntry } from "typesense-ts"; const { results } = await multisearch({ searches: [ multisearchEntry({ collection: "books", q: "harry", query_by: ["title", "authors"], filter_by: "average_rating:>=4", }), multisearchEntry({ collection: "books", q: "potter", query_by: ["title", "authors"], filter_by: "average_rating:>=4.5", }), ], }); // Each result is fully typed based on its collection schema const firstResults = results[0]; // ✅ Type-safe access

Document Operations

// Create documents with type safety await booksSchema.documents.create({ title: "The TypeScript Handbook", authors: ["Microsoft Team"], publication_year: 2023, ratings_count: 1250, average_rating: 4.8, categories: ["Programming", "Web Development"], }); // Bulk import with error handling try { const results = await booksSchema.documents.import( [ { title: "Book 1", authors: ["Author 1"], publication_year: 2023, ratings_count: 100, average_rating: 4.5, categories: ["Fiction"], }, { title: "Book 2", authors: ["Author 2"], publication_year: 2024, ratings_count: 200, average_rating: 4.2, categories: ["Non-Fiction"], }, ], { return_doc: true }, ); console.log(`Imported ${results.length} documents`); } catch (error) { if (error.name === "DocumentImportError") { console.log(`Failed documents:`, error.failedDocuments); } }

Collection Management

// Update collection schema import { validateCollectionUpdate } from "typesense-ts"; const updateFields = validateCollectionUpdate(booksSchema.schema, { fields: [ { name: "publisher", type: "string" }, // To drop a field: { name: "old_field", drop: true }, ], }); await booksSchema.update(updateFields); // Retrieve collection info const collectionInfo = await booksSchema.retrieve(); console.log(`Collection has ${collectionInfo.num_documents} documents`);

Aliases and Overrides

import { alias, override } from "typesense-ts"; // Create an alias const booksAlias = alias({ name: "popular_books", collection_name: "books", }); await booksAlias.upsert(); // Create search overrides const topBooksOverride = override("featured_books", { collection: "books", rule: { query: "bestseller", match: "exact" }, includes: [{ id: "book_123", position: 1 }], remove_matched_tokens: false, }); await topBooksOverride.upsert();

Analytics and Stopwords

import { analyticsRule, stopword } from "typesense-ts"; // Create analytics rules const popularQueriesRule = analyticsRule({ name: "popular_searches", type: "popular_queries", params: { source: { collections: ["books"] }, destination: { collection: "analytics" }, }, }); await popularQueriesRule.upsert(); // Manage stopwords const commonStopwords = stopword("english_stopwords", { stopwords: ["the", "and", "or", "but"], locale: "en", }); await commonStopwords.upsert();

Configuration Options

Node Configuration

const config = configure({ apiKey: "your-api-key", nodes: [ { url: "http://node1:8108" }, { host: "node2.example.com", port: 8108, protocol: "https" }, ], nearestNode: { url: "http://nearest:8108" }, // Optional for geo-distributed setups numRetries: 5, // Number of retries (default: nodes.length + 1) retryIntervalSeconds: 2, // Delay between retries (default: 1) healthcheckIntervalSeconds: 60, // Health check interval (default: 60) connectionTimeoutSeconds: 10, // Connection timeout (default: system) timeoutSeconds: 30, // Request timeout (default: system) additionalHeaders: { // Custom headers Authorization: "Bearer token", }, });

Advanced Schema Features

// Embedding fields for vector search const articlesSchema = collection({ name: "articles", fields: [ { name: "title", type: "string" }, { name: "content", type: "string" }, { name: "title_embedding", type: "float[]", embed: { from: ["title"], model_config: { model_name: "ts/e5-small" }, }, }, ], }); // Nested objects const usersSchema = collection({ name: "users", fields: [ { name: "name", type: "string" }, { name: "profile", type: "object" }, { name: "profile.age", type: "int32" }, { name: "profile.location", type: "geopoint" }, ], enable_nested_fields: true, }); // Reference fields for JOINs const ordersSchema = collection({ name: "orders", fields: [ { name: "order_id", type: "string" }, { name: "user_id", type: "string", reference: "users.id" }, { name: "total", type: "float" }, ], });

Development

Prerequisites

  • Node.js 18+
  • pnpm 8+
  • Docker (for running tests with Typesense instance)

Setup

# Clone the repository git clone https://github.com/yourusername/typesense-ts.git cd typesense-ts # Install dependencies pnpm install # Start Typesense for development docker-compose up -d

Available Scripts

# Build the library pnpm build # Run tests pnpm test # Run tests with coverage pnpm test --coverage # Type checking pnpm type-check # Linting pnpm lint # Format code pnpm format # Development mode with watch pnpm dev

Testing

The test suite includes integration tests that run against a real Typesense instance:

# Run all tests (starts Typesense automatically) pnpm test # Run specific test file pnpm test tests/search.test.ts # Run tests in CI mode (skips Docker setup) CI=true pnpm test

Project Structure

src/ ├── collection/ # Collection schema and operations ├── document/ # Document CRUD operations ├── analytics/ # Analytics rules and events ├── lexer/ # Type-level parsers for filters, sorts, etc. ├── http/ # HTTP client and request handling ├── config/ # Configuration management └── index.ts # Main exports tests/ # Test suite docker-compose.yml # Typesense development instance tsup.config.ts # Build configuration 

Contributing

We welcome contributions! Please follow these guidelines:

  1. Fork the repository and create a feature branch
  2. Write tests for new functionality
  3. Follow TypeScript best practices and maintain type safety
  4. Run the full test suite before submitting
  5. Update documentation for new features

Development Workflow

# Create a feature branch git checkout -b feature/amazing-feature # Make your changes and add tests # ... # Run tests and type checking pnpm test pnpm type-check pnpm lint # Commit your changes git commit -m "Add amazing feature" # Push and create a pull request git push origin feature/amazing-feature

Code Style

  • Enforce type-level programming for validation
  • Write tests for new features
  • Follow the existing code organization patterns

License

This project is licensed under the Apache 2.0 License - see the LICENSE file for details.

Acknowledgements

  • Typesense - The amazing search engine this client is built for
  • TypeScript Team - For the powerful type system that makes this library possible
  • Contributors - Thank you to everyone who helps improve this library