typesense/typesense-rust

Rust client for Typesense | Work In Progress & Help Wanted

Rust

78

80 commits

updated Jul 27, 2026

See the code

README

Typesense (rust library)

Crate API

Community-maintained Rust client library for Typesense | Work In Progress & Help Wanted!

Collection schema derive

Apply #[derive(Typesense)] to any struct you want to index in Typesense. The macro generates:

  • a collection_schema() definition based on your struct fields and attributes.
  • a {struct_name}Partial struct for partial updates of Typesense documents.

Quick example

#[derive(Typesense, Serialize, Deserialize)]
#[typesense(
    collection_name = "mega_products",
    default_sorting_field = "price",
    symbols_to_index = ["+", "-"]
)]
struct MegaProduct {
    id: String,

    #[typesense(infix, stem)]
    title: String,

    #[typesense(facet)]
    brand: String,

    #[typesense(sort)]
    price: f32,

    #[typesense(rename = "product_name", sort = true)]
    #[serde(rename = "product_name")]
    official_name: String,
}

// update a Typesense document using the generated partial struct
let update_payload = MegaProductPartial {
    price: Some(25.99),
    ..Default::default()
};

let result = client
    .collection::<MegaProduct>()
    .document("product-1")
    .update(&update_payload, None)
    .await;

Supported collection parameters

KeyTypeDescription / Notes
collection_namestringDefaults to the struct name in lowercase
default_sorting_fieldstringMust match the field name after rename if used
enable_nested_fieldsboolEnables Typesense nested field support
token_separatorslist of stringsList of symbols or special characters to be used for splitting the text into individual words in addition to space and new-line characters.
symbols_to_indexlist of stringsList of symbols or special characters to be indexed.

Supported field parameter

AttributeTypeDescription / Notes
facetboolEnable faceting for the field
sortboolMarks the field as sortable
indexboolWhether to index the field in memmory
storeboolWhether to store the field on disk
infixboolEnables infix search
stemboolValues are stemmed before indexing in-memory.
range_indexboolEnables an index optimized for range filtering on numerical fields
optionalboolFields with type Option<T> are optional in the generated Typesense schema. Setting this attribute will override it.
num_dimintegerSet this to a non-zero value to treat a field of type float[] as a vector field.
localestringLocale for text processing
vec_diststringDistance metric to be used for vector search
referencestringName of a field in another collection to be used for JOINs
typestringOverride the field type in Typesense
renamestringRename the field in the Typesense schema
flatten--Generate Typesense field schemas for a nested struct
skip--Skips this field in the Typesense schema

All boolean attributes can be either set to true using shorthand flags or explicitly set a value =true/false. Example:

#[typesense(facet)]
brand: String,

#[typesense(facet = false)]
weight: f32,

Indexing nested objects

When you have fields that are also structs, you need to mark all structs with #[derive(Typesense)]. The generated Typesense schema for those fields will have type of object (or object[] if the field is a vector).

Applying #[typesense(flatten)] on a field will expand the nested field schemas into the parent.

#[typesense(flatten)]
supplier: SupplierInfo,

If the field has a rename:

#[typesense(flatten, rename = "logistics_data")]
logistics: Logistics,

flattened fields become logistics_data.field_name.

#[typesense(flatten, skip)] produces only the flattened fields and omits the parent object field.

Nested objects example:

#[derive(Typesense, Serialize, Deserialize)]
struct ProductDetails {
    #[typesense(facet)]
    part_number: String,
    #[typesense(skip)]
    description: String,
}

#[derive(Typesense, Serialize, Deserialize)]
#[typesense(
    collection_name = "mega_products",
)]
struct MegaProduct {
    #[typesense(flatten)]
    details: ProductDetails,
}

Will generate this schema:

{
    "name": "mega_products"
    "fields": [
        {"name": "details", "type": "object"}, // <-- `#[typesense(flatten, skip)]` will omit this field
        {"name": "details.part_number", "type": "string", "facet": true}
        // `description` is skipped
    ],
}

Development

When updating or adding new parameters and endpoints, make changes directly in the Typesense API spec repository.

Once your changes are merged, you can update this project as follows (you can also run tasks individually):

cargo xtask fetch preprocess code-gen

This will:

The preprocessing step does two things:

  • Flatten the URL params defined as objects into individual URL parameters (in preprocess_openapi.rs)
  • Inject OpenAPI vendor attributes (e.g., generic parameters, schema builders) into the spec before code generation (in add_vendor_attributes.rs)

You can also run code-gen directly through Docker:

docker run --rm \
    -v $PWD:/local openapitools/openapi-generator-cli generate \
    -i /local/preprocessed_openapi.yml \
    -g rust \
    -o /local/typesense_codegen \
    -t /local/openapi-generator-template \
    --additional-properties library=reqwest \
    --additional-properties supportMiddleware=true \
    --additional-properties useSingleRequestParameter=true

Testing

Make sure you have a Typesense server up and running:

docker compose up

Then run this command in the root folder to run the integration tests:

cargo test-clean -- --all-features

This is an alias command which will run a script to clean up your Typesense server after the tests finish. You can pass any arguments of cargo test after the --.

To run test for wasm (chrome, headless):

cargo test-clean --wasm

If you'd like to contribute, please join our Slack Community and say hello!

hacktoberfest
rust
typesense

typesense/typesense-rust

Rust client for Typesense | Work In Progress & Help Wanted

Rust

78

80 commits

updated Jul 27, 2026

See the code

README

Typesense (rust library)

Crate API

Community-maintained Rust client library for Typesense | Work In Progress & Help Wanted!

Collection schema derive

Apply #[derive(Typesense)] to any struct you want to index in Typesense. The macro generates:

  • a collection_schema() definition based on your struct fields and attributes.
  • a {struct_name}Partial struct for partial updates of Typesense documents.

Quick example

#[derive(Typesense, Serialize, Deserialize)]
#[typesense(
    collection_name = "mega_products",
    default_sorting_field = "price",
    symbols_to_index = ["+", "-"]
)]
struct MegaProduct {
    id: String,

    #[typesense(infix, stem)]
    title: String,

    #[typesense(facet)]
    brand: String,

    #[typesense(sort)]
    price: f32,

    #[typesense(rename = "product_name", sort = true)]
    #[serde(rename = "product_name")]
    official_name: String,
}

// update a Typesense document using the generated partial struct
let update_payload = MegaProductPartial {
    price: Some(25.99),
    ..Default::default()
};

let result = client
    .collection::<MegaProduct>()
    .document("product-1")
    .update(&update_payload, None)
    .await;

Supported collection parameters

KeyTypeDescription / Notes
collection_namestringDefaults to the struct name in lowercase
default_sorting_fieldstringMust match the field name after rename if used
enable_nested_fieldsboolEnables Typesense nested field support
token_separatorslist of stringsList of symbols or special characters to be used for splitting the text into individual words in addition to space and new-line characters.
symbols_to_indexlist of stringsList of symbols or special characters to be indexed.

Supported field parameter

AttributeTypeDescription / Notes
facetboolEnable faceting for the field
sortboolMarks the field as sortable
indexboolWhether to index the field in memmory
storeboolWhether to store the field on disk
infixboolEnables infix search
stemboolValues are stemmed before indexing in-memory.
range_indexboolEnables an index optimized for range filtering on numerical fields
optionalboolFields with type Option<T> are optional in the generated Typesense schema. Setting this attribute will override it.
num_dimintegerSet this to a non-zero value to treat a field of type float[] as a vector field.
localestringLocale for text processing
vec_diststringDistance metric to be used for vector search
referencestringName of a field in another collection to be used for JOINs
typestringOverride the field type in Typesense
renamestringRename the field in the Typesense schema
flatten--Generate Typesense field schemas for a nested struct
skip--Skips this field in the Typesense schema

All boolean attributes can be either set to true using shorthand flags or explicitly set a value =true/false. Example:

#[typesense(facet)]
brand: String,

#[typesense(facet = false)]
weight: f32,

Indexing nested objects

When you have fields that are also structs, you need to mark all structs with #[derive(Typesense)]. The generated Typesense schema for those fields will have type of object (or object[] if the field is a vector).

Applying #[typesense(flatten)] on a field will expand the nested field schemas into the parent.

#[typesense(flatten)]
supplier: SupplierInfo,

If the field has a rename:

#[typesense(flatten, rename = "logistics_data")]
logistics: Logistics,

flattened fields become logistics_data.field_name.

#[typesense(flatten, skip)] produces only the flattened fields and omits the parent object field.

Nested objects example:

#[derive(Typesense, Serialize, Deserialize)]
struct ProductDetails {
    #[typesense(facet)]
    part_number: String,
    #[typesense(skip)]
    description: String,
}

#[derive(Typesense, Serialize, Deserialize)]
#[typesense(
    collection_name = "mega_products",
)]
struct MegaProduct {
    #[typesense(flatten)]
    details: ProductDetails,
}

Will generate this schema:

{
    "name": "mega_products"
    "fields": [
        {"name": "details", "type": "object"}, // <-- `#[typesense(flatten, skip)]` will omit this field
        {"name": "details.part_number", "type": "string", "facet": true}
        // `description` is skipped
    ],
}

Development

When updating or adding new parameters and endpoints, make changes directly in the Typesense API spec repository.

Once your changes are merged, you can update this project as follows (you can also run tasks individually):

cargo xtask fetch preprocess code-gen

This will:

The preprocessing step does two things:

  • Flatten the URL params defined as objects into individual URL parameters (in preprocess_openapi.rs)
  • Inject OpenAPI vendor attributes (e.g., generic parameters, schema builders) into the spec before code generation (in add_vendor_attributes.rs)

You can also run code-gen directly through Docker:

docker run --rm \
    -v $PWD:/local openapitools/openapi-generator-cli generate \
    -i /local/preprocessed_openapi.yml \
    -g rust \
    -o /local/typesense_codegen \
    -t /local/openapi-generator-template \
    --additional-properties library=reqwest \
    --additional-properties supportMiddleware=true \
    --additional-properties useSingleRequestParameter=true

Testing

Make sure you have a Typesense server up and running:

docker compose up

Then run this command in the root folder to run the integration tests:

cargo test-clean -- --all-features

This is an alias command which will run a script to clean up your Typesense server after the tests finish. You can pass any arguments of cargo test after the --.

To run test for wasm (chrome, headless):

cargo test-clean --wasm

If you'd like to contribute, please join our Slack Community and say hello!

hacktoberfest
rust
typesense

Project health

Overall

4.8/10

Maintained

2/10

Code review

9/10
OpenSSF Scorecard

crates.io

Latest release

0.4.0 · Mar 23, 2026

Releases this year

1

Significant stargazers

Pradyot Ranjan

40 followers · starred Dec 2025