An SQL-friendly ORM for Node.js built on Knex.js with powerful query building, relationship handling, and JSON Schema validation
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Insert, update, and upsert operations for complex nested data structures with relationship handling.
Insert a model graph with nested relations in a single operation.
/**
* Insert a graph of models with their relations
* @param graph - Object or array of objects representing the model graph
* @param options - Insert graph options
* @returns QueryBuilder instance
*/
insertGraph(graph: object | object[], options?: InsertGraphOptions): QueryBuilder;
/**
* Insert graph and return the inserted models
* @param graph - Object or array representing the model graph
* @param options - Insert graph options
* @returns QueryBuilder instance
*/
insertGraphAndFetch(graph: object | object[], options?: InsertGraphOptions): QueryBuilder;
interface InsertGraphOptions {
relate?: boolean | string[];
allowRefs?: boolean;
}Usage Examples:
// Insert person with pets
const graph = {
firstName: 'John',
lastName: 'Doe',
pets: [
{ name: 'Fluffy', species: 'cat' },
{ name: 'Buddy', species: 'dog' }
]
};
const inserted = await Person.query()
.insertGraphAndFetch(graph);
// Insert with existing relations (relate option)
const graphWithExistingMovie = {
firstName: 'Jane',
lastName: 'Smith',
movies: [
{ id: 1 }, // Existing movie to relate
{ title: 'New Movie', year: 2023 } // New movie to insert
]
};
await Person.query()
.insertGraph(graphWithExistingMovie, { relate: true });
// Insert with references
const graphWithRefs = {
firstName: 'Alice',
lastName: 'Johnson',
pets: [
{ '#id': 'fluffy', name: 'Fluffy', species: 'cat' },
{ name: 'Buddy', species: 'dog', bestFriend: { '#ref': 'fluffy' } }
]
};
await Person.query()
.insertGraph(graphWithRefs, { allowRefs: true });Insert or update a model graph, handling both new and existing models.
/**
* Upsert (insert or update) a graph of models
* @param graph - Object or array representing the model graph
* @param options - Upsert graph options
* @returns QueryBuilder instance
*/
upsertGraph(graph: object | object[], options?: UpsertGraphOptions): QueryBuilder;
/**
* Upsert graph and return the resulting models
* @param graph - Object or array representing the model graph
* @param options - Upsert graph options
* @returns QueryBuilder instance
*/
upsertGraphAndFetch(graph: object | object[], options?: UpsertGraphOptions): QueryBuilder;
interface UpsertGraphOptions {
relate?: boolean | string[];
unrelate?: boolean | string[];
insertMissing?: boolean | string[];
update?: boolean | string[];
noInsert?: boolean | string[];
noUpdate?: boolean | string[];
noDelete?: boolean | string[];
noRelate?: boolean | string[];
noUnrelate?: boolean | string[];
allowRefs?: boolean;
}Usage Examples:
// Basic upsert - update person, insert/update pets
const graph = {
id: 1, // Existing person
firstName: 'John Updated',
pets: [
{ id: 1, name: 'Fluffy Updated' }, // Update existing pet
{ name: 'New Pet', species: 'bird' } // Insert new pet
]
};
const result = await Person.query()
.upsertGraphAndFetch(graph);
// Upsert with relation control
const graphWithOptions = {
id: 1,
firstName: 'John',
pets: [
{ id: 1, name: 'Fluffy' },
{ id: 2, name: 'Buddy' }
],
movies: [
{ id: 5 } // Existing movie to relate
]
};
await Person.query()
.upsertGraph(graphWithOptions, {
relate: ['movies'], // Only relate movies, don't update them
unrelate: ['movies'], // Unrelate movies not in the graph
update: ['pets'], // Allow updating pets
insertMissing: ['pets'] // Insert missing pets
});
// Prevent certain operations
await Person.query()
.upsertGraph(graph, {
noDelete: true, // Don't delete related models not in graph
noUpdate: ['movies'], // Don't update movies
noInsert: ['movies'] // Don't insert new movies
});Convert relation expressions to object format for programmatic manipulation.
/**
* Get the graph expression as an object
* @returns Object representation of the graph expression
*/
graphExpressionObject(): object;Special properties for creating references within graphs.
// Special graph properties
interface GraphParameters {
'#dbRef'?: any; // Reference to existing database record
'#ref'?: string; // Reference to another object in the graph
'#id'?: string; // ID for this object to be referenced by others
}Usage Examples:
// Using #dbRef to reference existing records
const graph = {
firstName: 'John',
pets: [
{ '#dbRef': 1 }, // Reference existing pet with ID 1
{ name: 'New Pet', species: 'cat' }
]
};
// Using #ref and #id for internal references
const graph = {
firstName: 'Jane',
pets: [
{
'#id': 'pet1',
name: 'Fluffy',
species: 'cat'
},
{
name: 'Buddy',
species: 'dog',
playmate: { '#ref': 'pet1' } // Reference to Fluffy
}
]
};Restrict which relations can be inserted/updated in graph operations.
/**
* Allow only specific relations in graph operations
* @param expression - Relation expression specifying allowed relations
* @returns QueryBuilder instance
*/
allowGraph(expression: string): QueryBuilder;Usage Examples:
// Only allow inserting pets, not movies
await Person.query()
.allowGraph('pets')
.insertGraph({
firstName: 'John',
pets: [{ name: 'Fluffy', species: 'cat' }],
movies: [{ title: 'Movie' }] // This will be ignored
});
// Allow nested relations
await Person.query()
.allowGraph('pets.owner.movies')
.insertGraph(complexGraph);
// Allow multiple relations
await Person.query()
.allowGraph('[pets, movies.actors]')
.insertGraph(graph);Control validation during graph operations.
// Options that affect validation
interface GraphOperationOptions {
skipValidation?: boolean; // Skip JSON schema validation
}Usage Examples:
// Skip validation for performance
await Person.query()
.insertGraph(largeGraph, {
skipValidation: true
});Graph operations work seamlessly with transactions.
Usage Examples:
await Person.transaction(async trx => {
// All graph operations will be in the same transaction
const person = await Person.query(trx)
.insertGraphAndFetch(personGraph);
const updated = await Person.query(trx)
.upsertGraphAndFetch(updateGraph);
// If any operation fails, all will be rolled back
});Graph operations can throw various errors for constraint violations.
Usage Examples:
const { ValidationError, DBError } = require('objection');
try {
await Person.query().insertGraph(invalidGraph);
} catch (error) {
if (error instanceof ValidationError) {
console.log('Validation failed:', error.data);
} else if (error instanceof DBError) {
console.log('Database constraint violation:', error.message);
}
}interface InsertGraphOptions {
relate?: boolean | string[];
allowRefs?: boolean;
}
interface UpsertGraphOptions {
relate?: boolean | string[];
unrelate?: boolean | string[];
insertMissing?: boolean | string[];
update?: boolean | string[];
noInsert?: boolean | string[];
noUpdate?: boolean | string[];
noDelete?: boolean | string[];
noRelate?: boolean | string[];
noUnrelate?: boolean | string[];
allowRefs?: boolean;
}
interface GraphParameters {
'#dbRef'?: any;
'#ref'?: string;
'#id'?: string;
}
type PartialModelGraph<M> = Partial<M> & GraphParameters & {
[K in keyof M]?: M[K] extends Model
? PartialModelGraph<M[K]>
: M[K] extends Array<infer U>
? U extends Model
? PartialModelGraph<U>[]
: M[K]
: M[K];
};Install with Tessl CLI
npx tessl i tessl/npm-objection