DEV Community

MUHAMMED SHAFEEQUE P
MUHAMMED SHAFEEQUE P

Posted on

Introducing arango-typed: The TypeScript-First ORM/ODM/OGM for ArangoDB

If you're building with ArangoDB and TypeScript, you've probably wished for a Mongoose-like experience with full type safety. Meet arango-typed — a production-ready ORM/ODM/OGM that brings a familiar API to ArangoDB with enterprise-grade features.

What is arango-typed?

arango-typed is a comprehensive, type-safe database abstraction layer for ArangoDB that combines:

  • ORM (Object Relational Mapping)
  • ODM (Object Document Mapping)
  • OGM (Object Graph Mapper)

All in one package, with end-to-end type safety and a Mongoose-like API that makes ArangoDB development as intuitive as working with MongoDB.

Why arango-typed?

Working with ArangoDB's native driver can be verbose and lacks the developer experience of popular ORMs. arango-typed solves this by providing:

Full TypeScript Support - Type-safe from database to API

Mongoose-like API - Familiar developer experience

Production-Ready Features - Multi-tenancy, audit tracking, soft delete

Graph Database Native - First-class support for edges, vertices, and relationships

Vector Search & AI - Built-in LangChain integration for RAG applications

Performance Optimized - Only ~10-15% overhead vs raw driver

Framework Agnostic - Works with Express, Fastify, Koa, NestJS, Next.js, Hono

Quick Start

Installation

npm install arango-typed arangojs
Enter fullscreen mode Exit fullscreen mode

Basic Usage

import { connect, Schema, model } from 'arango-typed';

// Connect to ArangoDB (Mongoose-like API)
await connect('http://localhost:8529/myapp', { 
  username: 'root', 
  password: '' 
});

// Define Schema (Mongoose-like shorthand)
const userSchema = new Schema({
  name: String,                          // Simple shorthand
  email: { type: String, required: true, unique: true }, // With options
  age: Number,
  createdAt: { type: Date, default: () => new Date() }
});

// Create Model
const User = model('users', userSchema);

// Use the Model
const user = await User.create({
  name: 'John Doe',
  email: 'john@example.com',
  age: 30
});

console.log(user.name); // TypeScript knows this is a string!
Enter fullscreen mode Exit fullscreen mode

Key Features

1. Production-Ready Multi-Tenancy

Build SaaS applications with automatic tenant isolation:

import express from 'express';
import { tenantMiddleware } from 'arango-typed/integrations/express';

const app = express();

// Extract tenant from header (default: 'x-tenant-id')
app.use(tenantMiddleware({ extractFrom: 'header' }));

// Enable multi-tenancy on model
const User = model('users', userSchema, { tenantEnabled: true });

// Automatically filtered by tenant!
app.get('/users', async (req, res) => {
  const users = await User.find({}); // Only current tenant's users
  res.json(users);
});

app.post('/users', async (req, res) => {
  // Tenant ID automatically injected
  const user = await User.create(req.body);
  res.json(user);
});
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Automatic tenant filtering on all queries
  • Automatic tenant injection on create
  • Works seamlessly with Express, Fastify, Koa, and more
  • Zero configuration needed

2. Comprehensive Audit Functionality

Track who created, updated, or deleted documents with automatic audit logging:

import { AuditContext } from 'arango-typed';

// Set user context (typically in Express middleware)
AuditContext.set('user123', { 
  ip: '192.168.1.1',
  userAgent: 'Mozilla/5.0'
});

// Enable audit on model
const User = model('users', userSchema, {
  auditEnabled: true
});

// Create - automatically adds createdBy, createdAt, updatedBy, updatedAt
const user = await User.create({ name: 'John' });
// user.createdBy = 'user123'
// user.createdAt = Date
// user.updatedBy = 'user123'
// user.updatedAt = Date

// Update - automatically updates updatedBy, updatedAt and logs changes
await user.update({ name: 'Jane' });

// Get audit logs
const logs = await User.getAuditLogs(user._id);
const userLogs = await User.getAuditLogsByUser('user123');
const createLogs = await User.getAuditLogsByAction('create');
Enter fullscreen mode Exit fullscreen mode

Use Cases:

  • Compliance and regulatory requirements
  • Debugging and troubleshooting
  • User activity tracking
  • Change history and rollback

3. Soft Delete with Restore

Mark documents as deleted without actually removing them:

// Enable soft delete
const User = model('users', userSchema, {
  softDeleteEnabled: true
});

// Create a user
const user = await User.create({
  name: 'John Doe',
  email: 'john@example.com'
});

// Soft delete (sets isDeleted: true, deletedAt: Date)
await user.remove();

// User is excluded from normal queries
const users = await User.find().all(); // Won't include deleted user

// Find including soft-deleted
const allUsers = await User.findWithDeleted().all();

// Find only soft-deleted
const deletedUsers = await User.findDeleted().all();

// Restore the user
await User.restore(user._id);

// User is now back in normal queries
const users = await User.find().all(); // Includes restored user
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Data recovery capabilities
  • Audit trails
  • Analytics on deleted data
  • Compliance requirements

4. Automatic Partial Text Search

No regex needed for partial matching:

// Automatic partial text search
const users = await User.find({
  nameContains: 'john',    // Searches name field (case-insensitive)
  emailContains: 'gmail'    // Searches email field (case-insensitive)
}).all();

// Works with nested fields too
const posts = await Post.find({
  'user.emailContains': 'gmail'
}).all();
Enter fullscreen mode Exit fullscreen mode

The library automatically detects fields ending with "Contains" and generates case-insensitive LIKE queries.

5. Graph Database Support (OGM)

Work with graphs naturally using the Object Graph Mapper:

import { graphModel } from 'arango-typed';

// Create a graph model
const User = graphModel(db, 'social_graph', 'users', userSchema);

// Create relationships (edges)
await User.createRelationship('users/alice', 'users/bob', 'friends', {
  since: new Date(),
  strength: 0.9
});

// Traverse graph (BFS)
const friends = await User.traverse('users/alice', {
  direction: 'outbound',
  edgeCollection: 'friends',
  maxDepth: 2,
  strategy: 'bfs'
});

// Find shortest path
const path = await User.shortestPath('users/alice', 'users/charlie');

// Get all paths
const allPaths = await User.getAllPaths('users/alice', 'users/charlie', {
  maxDepth: 3
});

// Count relationships
const friendCount = await User.countRelationships('users/alice', 'friends', 'outbound');
Enter fullscreen mode Exit fullscreen mode

Graph Features:

  • Relationship access like object properties
  • BFS and DFS traversals
  • Shortest path queries
  • K-shortest paths
  • Path existence checks
  • Relationship counting

6. Enhanced Many-to-Many Relationships

Use native graph edges for many-to-many relationships:

import { BelongsToMany } from 'arango-typed';

// Define many-to-many using graph edges (recommended)
const userRolesRelation = new BelongsToMany(User, Role, {
  through: 'user_roles',           // Edge collection
  useGraph: true,                  // Use graph edges
  graphName: 'app_graph',          // Graph name
  direction: 'outbound'            // Direction
});

// Get related documents
const roles = await userRolesRelation.getRelated(user);

// Associate documents
await userRolesRelation.associate(user, role);

// Disassociate
await userRolesRelation.disassociate(user, role);
Enter fullscreen mode Exit fullscreen mode

7. Vector Search & LangChain Integration

Build RAG (Retrieval-Augmented Generation) applications easily:

import { ArangoRAG } from 'arango-typed/integrations/langchain';

// Create RAG instance
const rag = new ArangoRAG(db, {
  collection: 'documents',
  embeddingField: 'embedding',
  textField: 'content'
});

// Add documents with embeddings
await rag.addDocuments([
  { 
    pageContent: 'Hello world', 
    metadata: { source: 'doc1' },
    embedding: [0.1, 0.2, 0.3, ...] // Your embedding vector
  }
]);

// Retrieve context for LLM
const context = await rag.retrieve('What is hello?', {
  topK: 5,
  scoreThreshold: 0.7
});

// Use with LangChain
const retriever = rag.asRetriever();
const chain = RetrievalQAChain.fromLLM(llm, retriever);
const answer = await chain.call({ query: 'What is hello?' });
Enter fullscreen mode Exit fullscreen mode

LangChain Features:

  • VectorStore implementation
  • RAG support
  • MCP (Model Context Protocol) integration
  • Hybrid search with reranking
  • Multi-tenancy support

8. Query Builder

Chainable query builder with powerful operators:

// Complex queries
const users = await User.find({
  age: { $gte: 18, $lte: 65 },
  email: { $regex: /@gmail\.com$/ },
  status: { $in: ['active', 'pending'] },
  $or: [
    { role: 'admin' },
    { role: 'moderator' }
  ]
})
.sort({ createdAt: -1 })
.limit(10)
.skip(20)
.all();

// Lean queries (returns plain objects, faster)
const leanUsers = await User.findLean({ active: true }).all();

// Count
const count = await User.count({ active: true });
Enter fullscreen mode Exit fullscreen mode

Real-World Use Cases

Multi-Tenant SaaS Applications

Perfect for building SaaS platforms with automatic tenant isolation:

// Automatic tenant filtering
const User = model('users', userSchema, {
  tenantEnabled: true,
  auditEnabled: true,
  softDeleteEnabled: true
});

// All operations automatically respect tenant boundaries
app.get('/users', async (req, res) => {
  const users = await User.find({}); // Only current tenant
  res.json(users);
});
Enter fullscreen mode Exit fullscreen mode

Social Networks & Recommendations

Leverage graph capabilities for social features:

// Friend recommendations
const recommendations = await User.traverse(currentUserId, {
  direction: 'outbound',
  edgeCollection: 'friends',
  maxDepth: 2,
  filter: { status: 'active' }
});

// Find mutual connections
const mutual = await User.findMutualConnections(user1Id, user2Id);
Enter fullscreen mode Exit fullscreen mode

AI/ML Applications

Build RAG systems and semantic search:

// Semantic search
const results = await vectorSearch.similaritySearch(
  'documents',
  queryEmbedding,
  { topK: 10, scoreThreshold: 0.8 }
);

// Hybrid search (vector + keyword)
const hybridResults = await rag.hybridSearch(query, {
  vectorWeight: 0.7,
  keywordWeight: 0.3
});
Enter fullscreen mode Exit fullscreen mode

Enterprise Applications

Complete audit trails and compliance:

// Track all changes
const User = model('users', userSchema, {
  auditEnabled: true
});

// Get complete audit history
const auditLog = await User.getAuditLogs(userId);
// Returns: [{ action: 'create', userId: '...', timestamp: ..., changes: {...} }, ...]
Enter fullscreen mode Exit fullscreen mode

Performance

arango-typed is optimized for performance:

  • Query Caching - Compiled queries are cached for reuse
  • Direct DB Access - Bypasses Document wrapper when hooks aren't needed
  • Compiled Validators - Validators are compiled and cached
  • Batch Operations - Efficient bulk inserts and updates
  • Lean Queries - Return plain objects for better performance

Result: Only ~10-15% overhead compared to using the raw ArangoDB driver, while providing a much better developer experience.

Framework Support

arango-typed works with all major Node.js frameworks:

  • Express.js - Full middleware support
  • Fastify - Adapter available
  • Koa - Adapter available
  • NestJS - Adapter available
  • Next.js - Full support
  • Hono - Adapter available

Documentation

Comprehensive documentation is available with interactive examples:

📖 Full HTML Documentation - Beautiful, interactive documentation site

Key Documentation Pages

Latest Updates

v1.6.0 (Latest)

New Features:

  • ✨ Comprehensive audit functionality with automatic tracking
  • 🔍 Automatic audit fields (createdBy, createdAt, updatedBy, updatedAt, deletedBy, deletedAt)
  • 📊 Audit log retrieval methods (by document, user, or action)
  • 🔗 Integration with multi-tenancy and soft delete

v1.5.0

New Features:

  • 🗑️ Soft delete with restore capabilities
  • 🔄 Automatic filtering of soft-deleted documents
  • 📝 Enhanced documentation

v1.4.0

New Features:

  • 🔍 Automatic partial text search (nameContains, emailContains, etc.)
  • 📚 Comprehensive LangChain integration documentation
  • 🎨 Enhanced documentation site

Installation & Links

Install from npm

npm install arango-typed arangojs
Enter fullscreen mode Exit fullscreen mode

Links

Contributing

Contributions are welcome! Whether it's:

  • 🐛 Bug reports
  • 💡 Feature suggestions
  • 📝 Documentation improvements
  • 💻 Code contributions

Check out the Contributing Guide to get started.

Example: Complete Express.js Application

Here's a complete example of a multi-tenant SaaS application:

import express from 'express';
import { connect, Schema, model, tenantMiddleware, AuditContext } from 'arango-typed';

// Connect to database
await connect('http://localhost:8529/myapp', {
  username: 'root',
  password: ''
});

// Define schema
const userSchema = new Schema({
  name: String,
  email: { type: String, required: true, unique: true },
  role: { type: String, enum: ['user', 'admin'], default: 'user' },
  active: { type: Boolean, default: true }
});

// Create model with all features enabled
const User = model('users', userSchema, {
  tenantEnabled: true,
  auditEnabled: true,
  softDeleteEnabled: true
});

// Express app
const app = express();
app.use(express.json());

// Middleware: Extract tenant and set audit context
app.use(tenantMiddleware({ extractFrom: 'header' }));
app.use((req, res, next) => {
  const userId = req.headers['x-user-id'] as string;
  if (userId) {
    AuditContext.set(userId, {
      ip: req.ip,
      userAgent: req.get('user-agent')
    });
  }
  next();
});

// Routes
app.get('/users', async (req, res) => {
  // Automatically filtered by tenant
  const users = await User.find({ active: true }).all();
  res.json(users);
});

app.post('/users', async (req, res) => {
  // Tenant and audit fields automatically added
  const user = await User.create(req.body);
  res.json(user);
});

app.put('/users/:id', async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) return res.status(404).json({ error: 'Not found' });

  // Audit fields automatically updated
  await user.update(req.body);
  res.json(user);
});

app.delete('/users/:id', async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) return res.status(404).json({ error: 'Not found' });

  // Soft delete (sets isDeleted: true)
  await user.remove();
  res.json({ success: true });
});

app.get('/users/:id/audit', async (req, res) => {
  const logs = await User.getAuditLogs(req.params.id);
  res.json(logs);
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Conclusion

arango-typed brings a Mongoose-like experience to ArangoDB with full TypeScript support, production-ready features, and excellent performance. Whether you're building:

  • 🏢 Multi-tenant SaaS applications
  • 🌐 Social networks with graph features
  • 🤖 AI/ML applications with RAG
  • 🏛️ Enterprise applications with audit requirements

arango-typed has you covered with features like multi-tenancy, audit tracking, soft delete, graph support, and vector search — all with a familiar API and full type safety.

Get started today:

npm install arango-typed arangojs
Enter fullscreen mode Exit fullscreen mode

Give it a try and let me know what you think! ⭐


Tags: #typescript #arangodb #nodejs #orm #graphdatabase #opensource #webdev #programming #database #express #langchain #vectorsearch #multitenancy #audit #softdelete #typescript #mongodb #mongoose #backend #api

Top comments (0)