This guide shows you how to set up Postgres change data capture (CDC) and stream changes to Typesense using Sequin. With Postgres data streaming to Typesense, you can build real-time search experiences, maintain up-to-date search indexes, and provide fast, typo-tolerant search for your users. By the end of this how-to, you’ll have a configured Typesense cluster that stays in sync with your database.
This is the how-to guide for streaming Postgres to Typesense. See the quickstart for a step-by-step walkthrough or the reference for details on all configuration options.

Prerequisites

If you’re self-hosting Sequin, you’ll need:
  1. Sequin installed
  2. A database connected
If you’re using Sequin Cloud, you’ll need:
  1. A Sequin Cloud account
  2. A database connected

Basic setup

Prepare your Typesense cluster

Sequin converts Postgres changes and rows into JSON documents and sends them to your Typesense cluster. You’ll need to have a Typesense cluster running and accessible to Sequin. You can run Typesense locally for development or use a hosted instance.
If you’re using Sequin Cloud and developing locally, you can use the Sequin CLI’s tunnel command to connect Sequin’s servers to a local Typesense instance. So, no need to deploy your Typesense instance just yet!

Mapping Postgres tables to Typesense collections

From the Typesense docs:
In general, we recommend that you create one collection per type of document / record you have.
Similarly, Sequin recommends that you sink one Postgres table to one Typesense collection using a routing function. When significantly scaling Typesense, you may want to shard collections. For instance, you may shard your users collection by region into users_us, users_eu, users_asia, etc.

Create a Typesense sink

Navigate to the “Sinks” tab, click “Create Sink”, and select “Typesense Sink”.

Configure the source

1

Select source table or schema

Under “Source”, select the table(s) or schema(s) you want to stream data from.
2

Specify filters

Indicate whether you want to receive insert, update, and/or delete actions. We recommend selecting all three, which will ensure that your Typesense collection is kept in sync with your Postgres table.You can for instance unselect deletes if you want to exclude deleted rows from your Typesense collection. This means that records deleted from Postgres will not be removed from your Typesense collection and will still be searchable.You can also specify filter functions to narrow down the documents you want to index. For example, if you only want to index products that are currently in_stock, you can add a filter on in_stock = true. This is also useful for sharding or multi-tenancy.
3

Specify message grouping

Under “Message grouping”, we strongly recommend enabling message grouping to ensure that changes are processed in the correct order.While your Typesense collection is processing a message, if a new change or version related to the same row is captured, Sequin will hold the message back until the first message is processed. This is almost always the desired behavior.
4

Enrichment

If your sink includes a single table, you can optionally enrich your documents with additional data from your database using an enrichment function. This can add helpful metadata and context to your documents to increase the relevance of your search results.For example, if you want to enrich your products collection with the category field from your categories table, you can add a function enrichment that returns the category field for each document:
SELECT
  p.id, -- you must select all primary keys for Sequin to associate the enrichment with the message
  pc.name as category_name,
  pc.additional_info-- example of an enrichment
FROM
  product p
JOIN
  product_category pc on p.category_id = pc.id
WHERE
  p.id = ANY($1)
5

Transforms

Typesense collections expect documents to have a specific schema, including:
  1. An id field, which must be a string.
  2. The additional fields that are searchable.
You can map your Postgres table columns to Typesense fields using a functional transform.We recommend mapping your table to a map literal, such as:
def transform(record) do
  %{
    id: record["id"],
    name: record["name"],
    description: record["description"],
    price: record["price"],
    in_stock: record["in_stock"]
  }
end
This ensures that the schema of your Typesense collection is well defined.If instead you want any changes in your underlying table schema to be immediately reflected in your Typesense collection, you can return the record directly, perhaps only modifying the id field to ensure it exists and is a string.
# Return the record directly
def transform(record) do
  record
end
# Set the id field to the product_id field
def transform(record) do
  Map.put(record, "id", record["product_id"])
end
6

Specify backfill

You can optionally indicate if you want to seed your Typesense collection with a backfill of all or a portion of the table’s existing data.Backfills are useful if you want Sequin to populate your Typesense collection with existing data from your Postgres table. If you choose not to backfill on creation, you can backfill at any time.

Configure delivery

1

Typesense configuration

Under “Typesense configuration”, you’ll enter the URL for your Typesense cluster and provide an API key.Additionally, under “Advanced Typesense settings”, you can specify a request timeout (in seconds) for Typesense requests. You may want to increase the timeout if you have a slow network connection or are indexing a large number of documents per batch.
2

Routing

In the “Routing” section, you can specify how Sequin should route documents to your Typesense collection.If you are sinking a single table, you don’t need to apply any routing and simply enter the collection Sequin should deliver documents to.If you are sinking multiple tables, you can use a routing function to route documents to the correct collection.Additionally, you can customize actions in Typesense routing functions. For example, you can specify that a soft delete in Postgres should be reflected as a hard delete in Typesense.
3

Advanced configuration

In the “Sink settings” section, you can specify advanced configurations like batch size and timestamp format.In most cases, you can leave the default values - which for Typesense is optimized to the default batch size of 40 documents. However, if you’ve configured your Typesense cluster to process more documents per batch, you can increase the batch size here.
4

Create the sink

Give your sink a name, then click “Create Typesense Sink”.

Verify & debug

To verify that your Typesense sink is working:
  1. Make some changes in your source table.
  2. Verify that the count of messages for your sink increases in the Sequin web console.
  3. Verify receipt of documents in your Typesense collection by searching for them.
If documents don’t seem to be flowing:
  1. Click the “Messages” tab to view the state of messages for your sink.
  2. Click any failed message.
  3. Check the delivery logs for error details, including the response from Typesense.

Next steps

Assuming you’ve followed the steps above for your local environment, “How to deploy to production” will show you how to deploy your implementation to production.