@pawells/nestjs-qdrant
Description
NestJS integration for the Qdrant vector database. Provides collection management, semantic vector search, and a schema-based ORM for building AI-powered applications with NestJS dependency injection.
The package exposes two programming models:
- Service model — inject
QdrantServiceand callCollection(name)to get a lightweight, collection-scoped service (QdrantCollection) withSearch,Upsert,Delete, andGetInfooperations that pass parameters directly to the Qdrant JS client. - ORM model — extend the abstract
QdrantModel<TPayload>base class to define a typed schema (IQdrantSchema) for a collection. Each model instance manages its own collection lifecycle on application bootstrap and exposes higher-level CRUD and search methods (Create,FindById,Find,Search,UpdateOne,DeleteOne,DeleteMany).
Both models are registered through QdrantModule, which supports synchronous (forRoot) and asynchronous (forRootAsync) configuration. Multiple named Qdrant client instances are supported for multi-tenant scenarios via forRoot/forRootAsync with a name option and forFeature for model binding.
Requirements
- Node.js
>=22.0.0 - NestJS peer dependencies (installed separately):
@nestjs/common ^11.0.0@nestjs/core ^11.0.0
- Qdrant JS client (installed as a direct dependency — no separate install needed):
@qdrant/js-client-rest ^1.13.0
A running Qdrant instance is required at runtime. The default connection URL is http://localhost:6333.
Installation
Install the package and its required peers:
yarn add @pawells/nestjs-qdrant @nestjs/common @nestjs/coreCommonJS note: All
@pawells/packages are published as ESM ("type": "module"). If your runtime requires CommonJS, configure a bundler or use a dynamicimport()call.
Quick Start
1. Configure the connection
Set the environment variables before starting the application:
QDRANT_URL=http://localhost:6333
QDRANT_API_KEY=your-api-key # optional; omit for unauthenticated clusters2. Register the module
Register QdrantModule in your root AppModule using forRootAsync with ConfigService (recommended) or forRoot for synchronous configuration:
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { QdrantModule } from '@pawells/nestjs-qdrant';
@Module({
imports: [
ConfigModule.forRoot(),
QdrantModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
url: config.get<string>('QDRANT_URL', 'http://localhost:6333'),
apiKey: config.get<string>('QDRANT_API_KEY'),
}),
}),
],
})
export class AppModule {}3a. Semantic search via QdrantService (service model)
Inject QdrantService and call Collection(name) to run a vector similarity search:
import { Injectable } from '@nestjs/common';
import { QdrantService } from '@pawells/nestjs-qdrant';
@Injectable()
export class SearchService {
constructor(private readonly qdrantService: QdrantService) {}
async Search(embedding: number[]) {
const collection = this.qdrantService.Collection('documents');
return collection.Search({
vector: embedding,
limit: 10,
score_threshold: 0.7,
with_payload: true,
});
}
}3b. Typed ORM model (ORM model)
Extend QdrantModel to define a schema and bind it with forFeature:
import { Injectable } from '@nestjs/common';
import { QdrantModel, IQdrantSchema } from '@pawells/nestjs-qdrant';
interface DocumentPayload {
title: string;
source: string;
}
@Injectable()
export class DocumentModel extends QdrantModel<DocumentPayload> {
override readonly Collection = 'documents';
override readonly Schema: IQdrantSchema<DocumentPayload> = {
vectorSize: 1536,
distance: 'Cosine',
onCollectionExists: 'skip',
fields: {
title: { type: 'keyword', index: true },
source: { type: 'keyword', index: true },
},
};
}Register the model with forFeature in the module that needs it:
import { Module } from '@nestjs/common';
import { QdrantModule } from '@pawells/nestjs-qdrant';
import { DocumentModel } from './document.model.js';
import { DocumentService } from './document.service.js';
@Module({
imports: [QdrantModule.forFeature([DocumentModel])],
providers: [DocumentService],
})
export class DocumentModule {}Inject the model with @InjectQdrantModel:
import { Injectable } from '@nestjs/common';
import { InjectQdrantModel } from '@pawells/nestjs-qdrant';
import { DocumentModel } from './document.model.js';
@Injectable()
export class DocumentService {
constructor(
@InjectQdrantModel(DocumentModel) private readonly Documents: DocumentModel,
) {}
async SearchDocuments(embedding: number[]) {
return this.Documents.Search({
vector: embedding,
limit: 5,
scoreThreshold: 0.7,
});
}
}API Reference
Module
| Symbol | Kind | Description |
|---|---|---|
QdrantModule |
@Module class |
Dynamic NestJS module. Call forRoot(options, isGlobal?), forRootAsync(options, isGlobal?), or forFeature(models, clientName?). Registers globally by default (isGlobal = true). |
QdrantModule.forRoot(options, isGlobal?)
| Parameter | Type | Description |
|---|---|---|
options |
TQdrantModuleOptions |
Qdrant client options (extends QdrantClientParams). Accepts url, apiKey, name, and any other @qdrant/js-client-rest client params. |
isGlobal |
boolean |
Register as a global module (default: true). |
QdrantModule.forRootAsync(options, isGlobal?)
| Parameter | Type | Description |
|---|---|---|
options |
IQdrantModuleAsyncOptions |
Async configuration. Supports useFactory, useClass, or useExisting. Includes imports, inject, and optional name for multi-tenant setups. |
isGlobal |
boolean |
Register as a global module (default: true). |
QdrantModule.forFeature(models, clientName?)
| Parameter | Type | Description |
|---|---|---|
models |
Type<unknown>[] |
Array of QdrantModel subclasses to register as providers. |
clientName |
string |
Name of the Qdrant client to bind models to (optional; defaults to the default client). |
Services
| Symbol | Kind | Description |
|---|---|---|
QdrantService |
Injectable class | Facade over the Qdrant client. Call Collection(name) to get a QdrantCollection scoped to a named collection. Collection instances are cached after first access. |
QdrantCollection |
Class | Non-injectable, collection-scoped wrapper. Obtain instances via QdrantService.Collection(name). Exposes Search, Upsert, Delete, and GetInfo. |
QdrantService methods
| Method | Signature | Description |
|---|---|---|
Collection |
(collectionName: string) => QdrantCollection |
Returns (and caches) a QdrantCollection instance. Throws BadRequestException if the name is invalid (>255 chars or non-alphanumeric/hyphen/underscore). |
QdrantCollection methods
| Method | Signature | Description |
|---|---|---|
Search |
(params: SearchParameters) => Promise<SearchResult> |
Vector similarity search. params are passed directly to QdrantClient.search(). |
Upsert |
(params: UpsertParameters) => Promise<UpsertResult> |
Insert or update points. params are passed directly to QdrantClient.upsert(). |
Delete |
(params: DeleteParameters) => Promise<DeleteResult> |
Delete points by selector. params are passed directly to QdrantClient.delete(). |
GetInfo |
() => ReturnType<QdrantClient['getCollection']> |
Retrieve collection metadata including point count and configuration. |
ORM Model
| Symbol | Kind | Description |
|---|---|---|
QdrantModel<TPayload> |
Abstract injectable class | Base class for typed collection models. Subclass must define Collection: string and Schema: IQdrantSchema<TPayload>. Implements OnApplicationBootstrap to ensure the collection exists on startup. |
QdrantModel<TPayload> methods
| Method | Signature | Description |
|---|---|---|
Create |
(point: IQdrantPoint<TPayload>) => Promise<void> |
Insert a single point (upsert). |
FindById |
(id: string) => Promise<IQdrantPoint<TPayload> | null> |
Retrieve a single point by ID. Returns null if not found. |
Find |
(options?: IQdrantFindOptions<TPayload>) => Promise<IQdrantPoint<TPayload>[]> |
Scroll through collection with optional filter and pagination. Default limit: 100. |
Search |
(options: IQdrantSearchOptions<TPayload>) => Promise<IQdrantSearchResult<TPayload>[]> |
Vector similarity search. Default limit: 10. |
UpdateOne |
(id: string, updates: Partial<TPayload>) => Promise<void> |
Merge partial payload updates into an existing point. Vector is preserved. |
DeleteOne |
(id: string) => Promise<void> |
Delete a single point by ID. |
DeleteMany |
(filter: TQdrantPayloadFilter<TPayload>) => Promise<void> |
Delete all points matching a payload filter. |
Decorators
| Symbol | Kind | Description |
|---|---|---|
InjectQdrantClient |
Parameter decorator factory | Inject a QdrantClient instance. Accepts an optional name string for named multi-tenant clients. Usage: @InjectQdrantClient() or @InjectQdrantClient('archive'). |
InjectQdrantModel |
Parameter decorator factory | Inject a QdrantModel subclass. Accepts the model class as its argument. Usage: @InjectQdrantModel(DocumentModel). |
Types and Interfaces
| Symbol | Kind | Description |
|---|---|---|
TQdrantModuleOptions |
Type alias | Extends QdrantClientParams with an optional name field for multi-tenant client registration. |
IQdrantModuleAsyncOptions |
Interface | Async module options. Supports useFactory, useClass, useExisting, inject, imports, and name. |
IQdrantOptionsFactory |
Interface | Implement to provide a class-based async options factory. Must implement CreateQdrantOptions(): Promise<TQdrantModuleOptions> | TQdrantModuleOptions. |
IQdrantSchema<TPayload> |
Interface | Collection schema definition. Fields: vectorSize: number, distance: 'Cosine' | 'Dot' | 'Euclid' | 'Manhattan', fields, onCollectionExists?: 'skip' | 'recreate' | 'error'. |
IQdrantPoint<TPayload> |
Interface | A single vector point: id: string, vector: number[], payload: TPayload. |
IQdrantSearchResult<TPayload> |
Interface | Search result: id: string, score: number, payload: TPayload. |
IQdrantSearchOptions<TPayload> |
Interface | Search options: vector: number[], limit?: number, filter?: TQdrantPayloadFilter<TPayload>, scoreThreshold?: number. |
IQdrantFindOptions<TPayload> |
Interface | Find/scroll options: filter?: TQdrantPayloadFilter<TPayload>, limit?: number, offset?: number. |
IQdrantScrollResult<TPayload> |
Interface | Scroll result: points: IQdrantPoint<TPayload>[], nextPageOffset?: string | number. |
IQdrantFieldDefinition |
Interface | Schema field definition: type: TQdrantFieldType, index?: boolean, nullable?: boolean, description?: string. |
TQdrantPayloadFilter<TPayload> |
Type alias | Partial payload used as a filter: Partial<TPayload>. |
TQdrantFieldType |
Type alias | Payload field types: 'keyword' | 'integer' | 'float' | 'geo' | 'text' | 'bool' | 'json'. |
Constants and Token Helpers
| Symbol | Kind | Description |
|---|---|---|
QDRANT_CLIENT_TOKEN |
string constant |
DI token for the default Qdrant client ('QDRANT_CLIENT'). |
QDRANT_MODULE_OPTIONS |
string constant |
DI token for the default module options ('QDRANT_MODULE_OPTIONS'). API key is stripped from this token. |
DEFAULT_QDRANT_CLIENT_NAME |
string constant |
Default client name ('default'). |
MAX_COLLECTION_NAME_LENGTH |
number constant |
Maximum allowed collection name length (255). |
GetQdrantClientToken |
Function | (name?: string) => string — Returns the DI token for a named client. No name or 'default' returns 'QDRANT_CLIENT'; named returns 'QDRANT_CLIENT:{name}'. |
GetQdrantModuleOptionsToken |
Function | (name?: string) => string — Returns the DI token for a named client's options. |
Errors
| Symbol | Kind | Description |
|---|---|---|
QdrantOperationError |
Error class | Thrown when a Qdrant operation fails. Extends BaseError from @pawells/typescript-common. Default error code: 'QDRANT_OPERATION_FAILED'. Accepts { code?: string; cause?: Error } in the constructor options. |
Configuration (Environment Variables)
| Variable | Default | Required | Description |
|---|---|---|---|
QDRANT_URL |
http://localhost:6333 |
No | Qdrant server URL. Use HTTPS in production. Consumed by QdrantConfig.Get('URL'). |
QDRANT_API_KEY |
— | No | API key for authenticated Qdrant clusters. Consumed by QdrantConfig.Get('API_KEY'). |
QdrantConfig is a @pawells/config schema accessor. Register a provider (e.g., ConfigEnvironmentProvider.Register()) before reading values via QdrantConfig.Get('URL') or QdrantConfig.Get('API_KEY'). The recommended alternative is to pass values directly through QdrantModule.forRootAsync() using NestJS ConfigService — see Quick Start above.
License
MIT — Copyright (c) Phillip Aaron Wells. See LICENSE for details.