@chickyky/arangoose-nestjs
v0.0.5
Published
NestJS integration for Arangoose
Readme
@arangoose/nestjs
NestJS integration for arangoose. Provides a forRoot/forFeature module pair plus @InjectModel/@InjectRepository decorators, mirroring @nestjs/mongoose.
Install
Not published to npm — this package is consumed through the workspace:
// your app's package.json
"dependencies": {
"arangoose": "workspace:*",
"@arangoose/nestjs": "workspace:*"
}@nestjs/common and @nestjs/core are peer dependencies (^10.0.0); arangojs (>=8) is a peer of arangoose.
Setup
1. Connect once, at the app root
// app.module.ts
import { Module } from '@nestjs/common';
import { ArangooseModule } from '@arangoose/nestjs';
@Module({
imports: [
ArangooseModule.forRoot({
url: process.env.ARANGO_URL ?? 'http://localhost:8529',
database: process.env.ARANGO_DB ?? 'mydb',
username: process.env.ARANGO_USER,
password: process.env.ARANGO_PASSWORD,
}),
UserModule,
],
})
export class AppModule {}forRoot() is @Global(), so the connection is available everywhere without re-importing ArangooseModule. Call it exactly once.
2. Register models (and optionally repositories) per feature module
// user/user.schema.ts
import { Schema } from 'arangoose';
export interface User {
_key?: string;
email: string;
name?: string;
}
export const UserSchema = new Schema<User>({
email: { type: String, required: true, unique: true },
name: String,
});// user/user.repository.ts
import { BaseRepository } from 'arangoose';
import type { User } from './user.schema';
export class UserRepository extends BaseRepository<User> {
findByEmail(email: string) {
return this.findOne({ email });
}
}// user/user.module.ts
import { Module } from '@nestjs/common';
import { ArangooseModule } from '@arangoose/nestjs';
import { UserSchema } from './user.schema';
import { UserRepository } from './user.repository';
import { UserService } from './user.service';
@Module({
imports: [
ArangooseModule.forFeature([{ name: 'User', schema: UserSchema, repository: UserRepository }]),
],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}forFeature() creates the Model via model()/edgeModel() (set isEdge: true for edge collections) and, when repository is given, instantiates it as new UserRepository(model). Both are registered as providers in the importing module — no manual factory wiring needed.
3. Inject the model or the repository
// user/user.service.ts
import { Injectable } from '@nestjs/common';
import type { Model } from 'arangoose';
import { InjectModel, InjectRepository } from '@arangoose/nestjs';
import type { User } from './user.schema';
import { UserRepository } from './user.repository';
@Injectable()
export class UserService {
constructor(
@InjectModel('User') private readonly userModel: Model<User>,
@InjectRepository(UserRepository) private readonly userRepository: UserRepository
) {}
create(email: string, name?: string) {
return this.userModel.create({ email, name });
}
findByEmail(email: string) {
return this.userRepository.findByEmail(email);
}
}@InjectModel(name) and @InjectRepository(RepositoryClass) resolve the same tokens that forFeature() provides (getModelToken(name) / getRepositoryToken(RepositoryClass)), so they only work for names/classes that were actually passed to forFeature() in an imported module.
Edge collections
ArangooseModule.forFeature([
{ name: 'Follow', schema: FollowSchema, collection: 'follows', isEdge: true },
]);@InjectModel('Follow') private readonly followModel: Model<EdgeDocument>;Full sample app
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();// user/user.controller.ts
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
create(@Body() body: { email: string; name?: string }) {
return this.userService.create(body.email, body.name);
}
@Get(':email')
findByEmail(@Param('email') email: string) {
return this.userService.findByEmail(email);
}
}// user/user.module.ts (with the controller wired in)
import { Module } from '@nestjs/common';
import { ArangooseModule } from '@arangoose/nestjs';
import { UserSchema } from './user.schema';
import { UserRepository } from './user.repository';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({
imports: [
ArangooseModule.forFeature([{ name: 'User', schema: UserSchema, repository: UserRepository }]),
],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}API reference
ArangooseModule.forRoot(options: ConnectionOptions): DynamicModule
Opens the default arangoose connection (same ConnectionOptions as connect() from arangoose) and provides it globally under the ARANGOOSE_CONNECTION token. Call once per application.
ArangooseModule.forFeature(definitions: ArangooseFeatureDefinition[]): DynamicModule
interface ArangooseFeatureDefinition<T extends Document = Document> {
name: string;
schema: Schema<T>;
collection?: string;
isEdge?: boolean;
repository?: new (model: Model<T>) => unknown;
}Registers one provider per definition under getModelToken(name), and, if repository is set, a second provider under getRepositoryToken(repository). Import the result in whichever module needs that model/repository.
@InjectModel(name: string)
Parameter decorator. Injects the Model<T> registered under that name by forFeature().
@InjectRepository(repository: { name: string })
Parameter decorator. Injects the repository instance registered for that class by forFeature().
getModelToken(name: string) / getRepositoryToken(repository)
Token helpers, exported in case you need to wire a provider manually (e.g. useExisting, testing overrides).
Not covered
No
forRootAsync.forRoottakes plain options, so config that is only known at runtime has to be resolved beforeAppModuleis defined:const config = await loadConfig(); @Module({ imports: [ArangooseModule.forRoot(config)] }) export class AppModule {}No automatic collection or index creation.
forFeature()builds theModeland nothing else.No shutdown hook. Nothing calls
disconnect()when the app stops.
Both of the latter are a few lines in a provider:
import { Injectable, OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common';
import { disconnect, type Model } from 'arangoose';
import { InjectModel } from '@arangoose/nestjs';
import type { User } from './user.schema';
@Injectable()
export class ArangoLifecycle implements OnApplicationBootstrap, OnApplicationShutdown {
constructor(@InjectModel('User') private readonly userModel: Model<User>) {}
onApplicationBootstrap() {
return this.userModel.ensureCollection(); // collection + declared indexes
}
onApplicationShutdown() {
return disconnect();
}
}Testing
Override the model/repository token in a Test.createTestingModule to avoid touching a real database:
import { Test } from '@nestjs/testing';
import { getModelToken } from '@arangoose/nestjs';
const moduleRef = await Test.createTestingModule({
providers: [UserService, { provide: getModelToken('User'), useValue: { create: vi.fn() } }],
}).compile();