writePathPolicy lets a runtime decide whether writes may use both collection() and model(), or whether a namespace must be written through the Model layer.
The default is intentionally permissive: when writePathPolicy is omitted, collection APIs and Model APIs are both allowed. Enable this policy only for applications that want the runtime to enforce a stronger write boundary around schema defaults, hooks, timestamps, optimistic locking, soft delete, and other Model mutation rules.
import MonSQLize from 'monsqlize';
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'app',
config: { uri: 'mongodb://localhost:27017' },
writePathPolicy: {
default: 'model-only',
namespaces: {
'app.audit_logs': 'allow-both',
'analytics:app.reports': {
mode: 'allow-both',
raw: 'block',
management: 'allow'
}
}
}
});type WritePathPolicyMode = 'allow-both' | 'model-only';
type WritePathPolicyRule = {
mode?: WritePathPolicyMode;
raw?: 'inherit' | 'allow' | 'block';
management?: 'inherit' | 'allow' | 'block';
onViolation?: 'throw' | 'warn';
};
type WritePathPolicyOptions = {
default?: WritePathPolicyMode | WritePathPolicyRule;
namespaces?: Record<string, WritePathPolicyMode | WritePathPolicyRule>;
};| Field | Default | Meaning |
|---|---|---|
mode |
allow-both |
allow-both allows collection and Model writes. model-only blocks direct collection, db, and legacy writes unless overridden. |
raw |
inherit |
Controls collection.raw(), db.raw(), and db command access. Inherits block from model-only and allow from allow-both. |
management |
inherit |
Controls index and collection management operations. In model-only, Model management methods are allowed while direct collection management is blocked. |
onViolation |
throw |
throw rejects the operation. warn logs a warning and allows the operation. |
Namespace rules are matched from most specific to least specific:
- Internal instance namespace, when present.
- Pool-scoped namespace:
poolName:dbName.collectionName. - Database namespace:
dbName.collectionName. - Collection name only.
default.
Prefer poolName:dbName.collectionName or dbName.collectionName in user configuration. They are stable across runtime instance IDs.
Inside namespaces, the key default is reserved for the fallback matcher and is rejected at construction time; use the top-level writePathPolicy.default field instead. If an actual collection is named default, configure it with a qualified key such as dbName.default or poolName:dbName.default. Namespace keys must not include leading or trailing whitespace.
writePathPolicy applies to write-capable paths:
- Collection writes:
insertOne,insertMany,updateOne,updateMany,replaceOne,findOneAndUpdate,findOneAndReplace,findOneAndDelete,upsertOne,deleteOne,deleteMany. - Batch helpers:
insertBatch,updateBatch,deleteBatch,incrementOne. - Collection management: index creation/drop, collection creation/drop, validators,
renameCollection,collMod, and capped conversion. - Raw/db/legacy write surfaces:
collection.raw(),db.raw(),db.runCommand(),dropDatabase(), and legacy adapter writes. - Aggregation pipelines whose final stage writes with
$outor$merge; the policy is checked against the write target namespace.
Read-only queries are not governed by this policy.
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'app',
config: { uri: 'mongodb://localhost:27017' },
writePathPolicy: { default: 'model-only' }
});
MonSQLize.Model.define('users', {
schema: {},
options: {
timestamps: true,
version: true,
softDelete: true
}
});
await msq.connect();
await msq.model('users').insertOne({ name: 'Ada' }); // allowed
await msq.collection('users').insertOne({ name: 'Ada' }); // throwsUse namespace overrides for operational collections that intentionally remain native:
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'app',
config: { uri: 'mongodb://localhost:27017' },
writePathPolicy: {
default: 'model-only',
namespaces: {
'app.audit_logs': 'allow-both'
}
}
});This policy controls the API path used to issue writes. It does not make cache invalidation transaction-atomic, does not make Change Stream sync exactly-once, and does not replace application-level idempotency or authorization.