How To Use Typesense with Node.js

Ever wondered how to supercharge your Node.js application with lightning-fast search capabilities? While traditional database queries struggle with complex search requirements, Typesense offers a powerful solution. This open-source search engine rivals Elasticsearch and Algolia, perfectly suited for Node.js applications.
With basic Node.js knowledge, you'll discover how to implement sophisticated search features including faceted search, geo-search, and real-time indexing. Perfect for building e-commerce platforms, content repositories, or any application needing robust search capabilities. Ready to transform your user experience? Let's explore how to integrate Typesense with Node.js step by step.
What is Typesense?
Typesense is an open-source search engine built for speed and ease of use. It fixes typos automatically and works in real-time with fast response times. As an alternative to Elasticsearch and Algolia, it offers features like faceted search, geo-search, and instant results. Its simple setup and clear API make it a good choice for adding search to modern applications.
Whether you're building an e-commerce site, content platform, or any app needing quick and accurate search results, Typesense helps you deliver a better search experience to your users.
Why Choose Typesense for Your Search Needs?
- Speed: Designed for low-latency and high-performance searches. Typesense delivers results in milliseconds, even with millions of records, making it perfect for real-time search experiences.
- Easy to Use: Simple API with minimal configuration needs. You can set up and integrate Typesense in minutes, not hours, with clear documentation and straightforward REST APIs that work across any programming language.
- Typo Tolerance: Handles misspellings effectively by automatically detecting and correcting user typos. Even if users type "laptop" instead of "laptop," they'll still find what they're looking for.
- Self-hosted & Cloud Options: Gives flexibility based on requirements. Run Typesense on your own servers for complete control, or use Typesense Cloud for a fully-managed solution with zero maintenance overhead.
- Affordable: No complex licensing fees, unlike Algolia. With transparent pricing and an open-source core, you can start for free and scale without worrying about unexpected costs or vendor lock-in.
Use Cases of Typesense
Typesense powers search across various industries and applications, helping businesses deliver fast and accurate results to their users. Here are some key areas where Typesense excels:
- E-commerce Search: Helps customers find products quickly. When shoppers search for "blue running shoes size 10," Typesense returns relevant results instantly, handling filters for size, color, price ranges, and even showing suggestions for misspelled product names.
- Document Search: Useful in knowledge bases, research portals, and more. Organizations can index thousands of documents, PDFs, and articles, allowing users to search through content with filters for date, category, or department while getting instant results as they type.
- Media & Content Search: Video, audio, and blog search optimization. Content platforms can help users find specific videos, podcasts, or articles by searching through titles, descriptions, tags, and transcripts, with results ranked by relevance.
- Geo-search: Location-based searches for applications like food delivery. Apps can help users find nearby restaurants, stores, or services by combining location data with other filters like ratings, cuisine type, or operating hours.
How Typesense Processes Data: A Complete Flow
- Data Ingestion:
- This is the initial step where data is imported into Typesense.
- Data can be added from various sources like databases, APIs, or directly uploaded files.
- Indexing:
- Once data is ingested, Typesense creates an index.
- This index is a data structure optimized for fast search.
- It organizes the data in a way that allows for quick and efficient retrieval of relevant information.
- Query Processing:
- When a user performs a search, Typesense processes the query.
- It analyzes the search terms and compares them against the indexed data.
- Filtering & Faceting:
- Typesense allows for filtering and faceting of search results.
- Filters narrow down the results based on specific criteria, while facets provide a way to explore the data from different angles.
- Results Retrieval:
- The final step is retrieving the search results.
- Typesense returns the most relevant results based on the query and any applied filters or facets.
- The results are optimized for speed and relevance.
Integrating Typesense with Node.js
1. Install Typesense SDK
npm install typesense
Just use this command in terminal to install typesense in your machine
2. Initialize Typesense Client
const Typesense = require('typesense');
const client = new Typesense.Client({
nodes: [{ host: 'localhost', port: 8108, protocol: 'http' }],
apiKey: 'YOUR_API_KEY',
connectionTimeoutSeconds: 2,
});
For initialization of typesense, we need to create typesense instances. For the same, we need apiKey, protocol and host.
It is always better to save these credentials using .env and we can reference it here.
3. Check Connection
client.collections().retrieve()
.then(response => console.log("Connected to Typesense", response))
.catch(error => console.error("Connection failed", error));
This command is used to verify the operational status of your Typesense instance. If the connection is established and active, it will return a list of available collections.
4. Collection CRUD Operations
a. Creating a Collection
const schema = {
name: "products",
fields: [
{ name: "name", type: "string", facet: true },
{ name: "price", type: "number", facet: true }
] };
client.collections().create(schema);
This code will create a new collection named "products" with two fields: "name" (string type) and "price" (number type). Both fields are marked as facets, enabling filtering and faceting operations on them.
b. Fetching Collections
client.collections().retrieve().then(console.log);
This code will retrieve metadata about all the collections present in the database and display the information in the console. This metadata may include collection names, schemas, and other relevant details.
c. Updating a Collection
const collection = client.collections('products');
const schema = collection.schema;
schema.fields.push({ name: "description", type: "string" });
collection.update(schema);
d. Deleting a Collection
client.collections('products').delete().then(console.log)
This code will permanently delete the "products" collection and all its data from the database. Exercise caution when using this operation, as data deletion is irreversible.
5. CRUD Operations for Documents
a. Inserting Bulk Data
const products = [
{ id: "1", title: "Laptop", price: 1000 },
{ id: "2", title: "Phone", price: 500 }
];
client.collections('products').documents().import(products);
This code demonstrates how to insert multiple documents (bulk data) into a Firestore collection named 'products'. In this example, an array called "products" is defined containing two product objects, each with an id, title, and price. The 'import' function is then used to add these product objects as documents into the specified collection.
b. Fetching a Document
client.collections('products').documents('1').retrieve().then(console.log);
This code retrieves a document with the ID '1' from the 'products' collection and then passes the retrieved document data to the console.log function, which will display the data in the console.
c. Updating a Document
client.collections('products').documents('1').update({ price: 1200 });
This code targets the document with the ID '1' within the 'products' collection and updates its 'price' field to the value 1200.
client.collections('products').documents('1').delete().then(console.log);
This code deletes the document with the ID '1' from the 'products' collection. The .then(console.log) portion indicates that once the deletion is successful, a success message will be logged to the console.
6. Advanced Filtering & Searching
a. Simple Search
client.collections('products').documents().search({
q: "Laptop",
query_by: "title"
}).then(console.log);Let's break down the code snippet and explain the roles of `q` and `query_by` within a search operation.
Explanation
`q`: This parameter represents the search query term itself. In the given example, the value of `q` is "Laptop", indicating that you're searching for documents containing the term "Laptop".
`query_by`: This parameter specifies the field or fields within your documents that you want to search against. In this case, `query_by` is set to "title", meaning the search will be performed specifically on the "title" field of your documents.
Why They Are Used
Using `q` allows you to define the keyword or phrase you're looking for.
Using `query_by` focuses the search on specific fields, making it more efficient and targeted. If you don't use `query_by`, the search might be performed across all fields, which could be slower and return less relevant results.
How to Perform the Search
The provided code snippet demonstrates how to execute this search using a client library (presumably for a database or search service). Here's a breakdown of the steps:
Specify the Collection: `client.collections('products')` indicates that you're searching within a collection named "products".
Search the Documents: `.documents().search()` initiates the search operation on the documents within the collection.
Provide Search Parameters: The object `{ q: "Laptop", query_by: "title" }` is passed as an argument to the `search` function. This object contains the search query ("Laptop") and the field to search against ("title").
Handle Results: `.then(console.log)` is used to handle the search results. In this case, the results are simply logged to the console.
Key Point: This search will find and return documents within the "products" collection where the "title" field contains the term "Laptop".
b. Multi-keyword Search
client.collections('products').documents().search({
q: "Laptop Phone",
query_by: "title"
}).then(console.log);
Here in this example, we are searching for multiple keywords, now the q is having Laptop and phone. So here the result should contain both the keywords present
c. Faceting (Filter by Price)
client.collections('products').documents().search({
q: "*",
query_by: "title",
facet_by: "price"
}).then(console.log);
- `q: "*"`: This part of the query specifies the search term. In this case, the asterisk (*) is a wildcard character, meaning it will match any and all search terms. Effectively, it's performing a search without any specific keyword, returning all products.
- `query_by: "title"`: This indicates that the search results should be sorted based on the "title" field of the products.
- `facet_by: "price"`: This is where faceting comes into play. Faceting is a technique used to categorize search results based on specific attributes or properties. In this example, the search results will be categorized by the "price" of the products. This allows for filtering or refining the search results based on different price ranges.
- Faceting is crucial in e-commerce and other search-intensive applications because it enables users to quickly narrow down their search results based on their specific needs and preferences. For instance, in the context of this product search, faceting by price allows users to see how many products fall within different price ranges (e.g., $0-$10, $10-$20, $20-$30, and so on). Users can then click on a specific price range to filter the results and only see products within that price range.
- For example, let's say you have an online store with a wide range of products. A user searches for "shoes". Without faceting, the user would see a long list of all shoes in the store. With faceting by price, the user would see the price ranges and the number of shoes within each range. They could then easily filter the results to only see shoes within their budget.
d. Numeric Filters
client.collections('products').documents().search({
q: "*",
query_by: "title",
filter_by: "price:>500"
}).then(console.log);
Here, the filter_by is used to filter the documents by conditions such as >, <, >=,<=,:= with numerical values. This will be applicable for single numeric values.
e. ID-Based Filtering (Multiple Values)
client.collections('products').documents().search({
q: "*",
query_by: "title",
filter_by: "id:=[1,2]"
}).then(console.log);
Here, the filter_by is used to filter the documents by conditions such as >, <, >=,<=,:= with numerical values. This will be applicable for single numeric values.
f. Exclusion Filters
client.collections('products').documents().search({
q: "*",
query_by: "title",
filter_by: "id!=1"
}).then(console.log);
Similarly, If we would like to exclude some values, then we should use this != operator.
Our Final Words
I hope you now have a clear understanding of Typesense's capabilities and how it can enhance your Node.js applications. From basic setup to advanced features like faceting and filtering, we've covered the essential tools for building powerful search experiences.
Typesense stands out with its speed, typo tolerance, and simple integration, making it ideal for applications requiring robust search. Whether you're working on an e-commerce platform or content system, you now have the knowledge to implement fast, accurate search that your users will love.