MongoDB Test Data: Extended JSON Fixtures for mongoimport and insertMany
MongoDB test data has a problem that plain JSON hides. A fixture can
say "createdAt": "2026-03-31T15:05:59Z" and look correct, but
mongoimport stores it as a string, your $jsonSchema validator expects
a date, and a query that sorts by createdAt quietly compares text.
The same happens to money stored as a double and to an _id that's a
24-character string instead of an ObjectId. In MongoDB, the types are
part of the test data.
This post builds one seeded JsonFabrica template that writes MongoDB
Extended JSON directly: nested customer subdocuments, an items array,
and $oid, $date and $numberDecimal where the type matters. It
also plants four documents that drift from the schema on purpose. You
load the file into a throwaway database with mongoimport, mongosh or
the Node.js driver's insertMany, then test a $jsonSchema validator
and your own parsing code against it.
To be clear about the boundaries: JsonFabrica is a JSON-over-HTTP API. There is no MongoDB package, plugin or connector, and it never touches your database. A script you own calls the API and commits a JSON file; MongoDB's own tools load it.
Everything below was run against MongoDB 8.0.34 (from the mongo:8.0
image), mongoimport 100.20.0, mongosh 2.14.0 and the Node.js driver
7.7.0. Behavior can differ between versions, so check the docs for the
ones you run.
Why plain JSON makes poor MongoDB test data
JSON has strings, numbers, booleans, null, objects and arrays. BSON also has ObjectIds, dates, 32-bit and 64-bit integers, doubles and Decimal128. When a loader reads plain JSON, it has to guess the BSON type, and different loaders guess differently. Here's what I got when I loaded the same values three ways:
| Value in the file | mongoimport | Driver + JSON.parse |
Driver + EJSON.parse |
|---|---|---|---|
{"$date": "2026-…Z"} |
date | embedded doc | date |
20 |
int | int | int |
20.0 |
double | int | int |
3000000000 |
long | double | double |
{"$numberLong": "5"} |
long | embedded doc | int |
{"$oid": "…"} as _id |
ObjectId | rejected | ObjectId |
(The driver columns are the Node.js driver, with EJSON.parse in its
default relaxed mode. With relaxed: false, $numberLong stays a
long.)
Two things follow. First, anything that isn't a string, a boolean or
a small whole number needs its type written into the file, which is
what Extended JSON is for. Second, the loader has to read Extended
JSON. mongoimport does by default; in mongosh and the Node.js driver
you have to parse the file with EJSON.parse.
MongoDB Extended JSON test data: canonical vs relaxed
Extended JSON v2 has two modes, and parsers accept both, mixed freely in one file:
- Canonical wraps every typed value, numbers included:
{"$numberInt": "2"},{"$date": {"$numberLong": "1774969559653"}}. Nothing is lost on a round trip, but the file is hard to read. - Relaxed writes numbers as plain JSON numbers and dates as an
ISO-8601 string inside
$date. It reads much better, but a long and an int look identical. Dates before 1970 or after 9999 still use the canonical form.
mongoexport writes relaxed mode by default. Here is one document exported both ways (each reformatted onto several lines):
{
"_id": 1, "qty": 2, "views": 5, "price": 24.99,
"createdAt": { "$date": "2026-03-31T15:05:59.653Z" }
}
{
"_id": { "$numberInt": "1" },
"qty": { "$numberInt": "2" },
"views": { "$numberLong": "5" },
"price": { "$numberDouble": "24.99" },
"createdAt": { "$date": { "$numberLong": "1774969559653" } }
}
views was stored as a long. The relaxed export prints it as 5, so
if you import that file again it comes back as an int. For test
fixtures, that's an argument for spelling out types instead of relying
on a loader's guess. The template below mixes the two modes, using
whichever form is clearest for each field:
_idis{"$oid": "…"}, which is the same in both modes.- Dates are relaxed
{"$date": "<ISO-8601>"}. That's the formatgetRandomDatereturns. - Money is
{"$numberDecimal": "91.98"}, also the same in both modes. The value is a string, so the exact digits survive. - Quantities are plain whole numbers. Every loader in the table
above stores a small whole number as an int, which matches
bsonType: "int". If a field needs to be a long, write{"$numberLong": "…"}and parse withrelaxed: falsein Node.
A seeded template for nested MongoDB documents
The template produces a top-level array of orders, the shape
mongoimport --jsonArray and insertMany both take. Each order has a
customer subdocument with a nested address, and an array of one to
three line items:
[
<for(i, 1, getParam('orders', 20))><if(getVar('i') > 1)>,<endIf>
{
"_id": { "$oid": "6710c0de<appendBefore('0', getVar('i'), 16)>" },
"customer": {
"name": "<getRandomFullName()>"<if(getVar('i') != 2)>,
"email": "<getRandomEmail('example.com')>"<endIf>,
"address": {
"street": "<getRandomStreet()>",
"city": "<getRandomCity()>",
"country": "<getRandomCountry()>"
}
},
"items": [
<for(j, 1, getRandomNumber(1, 3))><if(getVar('j') > 1)>,<endIf>
{
"sku": "SKU-<getRandomNumber(10000, 99999)>",
"qty": <getRandomNumber(1, 5)>,
"price": <if(getVar('i') == 4)>24.99<else>{
"$numberDecimal": "<formatNumber(getRandomNumber(5, 250, 2), 2)>"
}<endIf>
}<end_for>
],
<setVar('status', getRandomElement('pending', 'paid', 'shipped'))>
"status": "<getVar('status')>",
"createdAt": <if(getVar('i') == 3)>
"<getRandomDate('2026-01-01', '2026-09-30')>"
<else>{ "$date": "<getRandomDate('2026-01-01', '2026-09-30')>" }<endIf>,
"shippedAt": <if(getVar('status') == 'shipped')>{
"$date": "<getRandomDate('2026-10-01', '2026-10-20')>"
}<else>null<endIf><if(getVar('i') == 5)>,
"legacyCustomerId": <getRandomNumber(100000, 999999)><endIf>
}<end_for>
]
How it fits together:
- ObjectIds come from the loop index. A fixed eight-character
prefix plus
appendBeforepadding the index to 16 characters gives a 24-character id such as6710c0de0000000000000003. Decimal digits are valid hex, so these are real ObjectIds, and they're the same on every run, so tests can refer to order 3 by its id. The first four bytes of an ObjectId are a timestamp, sogetTimestamp()returns 17 October 2024 for every one of them. UsecreatedAtfor time, not_id. - Nested arrays use a nested loop. The items loop uses its own
variable,
j, and callsgetRandomNumberdirectly as its upper bound. Each loop has its own comma guard,<if(getVar('j') > 1)>,<endIf>, so both arrays are valid JSON at any length.forbounds are inclusive. - Prices are decimal strings.
formatNumberalways prints two decimal places, so a price of 162.5 comes out as"162.50". - One variable, because it's read twice.
setVarstoresstatus, which is printed and also decides whethershippedAtis a date ornull. Everything else is called inline. - No order total. Template expressions support comparisons and
&&/||but no arithmetic, so the template can't add up line items. Your app or a$sumin an aggregation computes the total, and that's code you want to test against this data anyway.
The control flow docs cover for and
if/elseIf/else.
Planted schema-drift documents
Orders 2 to 5 are deliberately wrong, each in a different way that real collections drift. Each one is selected by its loop index, so it is in the file on every regeneration:
- Order 2, missing field. The customer has no
email. The comma and the field sit inside oneif, so the JSON stays valid. - Order 3, wrong type: string date.
createdAtis the ISO string without the$datewrapper. That's what you get from a writer that ran its documents throughJSON.stringify. - Order 4, wrong type: double price. Every line item has the
plain number
24.99, which every loader stores as a double. It's a fixed literal because a random price could come out whole, and a whole number is stored as an int. (Values that pass through a JSON parser, including the API response, lose the difference between20and20.0.) - Order 5, extra field. A top-level
legacyCustomerIdleft over from an old import. MongoDB accepts it happily unless a validator says otherwise.
Order 1 is a normal document, so the first record in the file is a
clean example. If you lower the orders parameter, keep it at 5 or
more, or some drift documents drop out.
Rendered with seed 20261028 and the default 20 orders, the
template produces this as the first document:
[
{
"_id": {
"$oid": "6710c0de0000000000000001"
},
"customer": {
"name": "Andrea Aldbury",
"email": "[email protected]",
"address": {
"street": "Sage Close 139",
"city": "Ankara",
"country": "Canada"
}
},
"items": [
{
"sku": "SKU-43573",
"qty": 2,
"price": {
"$numberDecimal": "91.98"
}
},
{
"sku": "SKU-68409",
"qty": 1,
"price": {
"$numberDecimal": "104.11"
}
}
],
"status": "shipped",
"createdAt": {
"$date": "2026-03-31T15:05:59.653Z"
},
"shippedAt": {
"$date": "2026-10-19T04:54:47.126Z"
}
},
With the same seed, order 3's createdAt is the bare string
"2026-05-08T19:26:25.064Z" and order 5's legacyCustomerId is
582519. The address fields are drawn independently, which is why
a city like Ankara can sit next to Canada.
Regenerating the fixture from a seed
The regen script is the only code that talks to JsonFabrica. It sends
the template and a seed to
POST /v1/templates/generate, which
renders a raw template body without saving anything, so the template
stays a file in your repo. It's plain curl and jq, because
mongoimport users have a shell anyway:
#!/usr/bin/env bash
# Rebuild test/fixtures/orders.json from orders.tmpl via the JsonFabrica
# API. Run by hand, review the diff, commit the template and the JSON.
set -euo pipefail
api="${JSONFABRICA_URL:-https://api.jsonfabrica.com}"
seed="${SEED:-20261028}"
dir=test/fixtures
response=$(jq -n --rawfile body "$dir/orders.tmpl" --argjson seed "$seed" \
'{body: $body, seed: $seed, params: {orders: 20}}' |
curl -sS --fail-with-body "$api/v1/templates/generate" \
-H "Authorization: Bearer $JSONFABRICA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @-) || { echo "$response" >&2; exit 1; }
jq '.data' <<<"$response" > "$dir/orders.json"
echo "wrote $dir/orders.json (seed $(jq '.meta.seed' <<<"$response"))"
What it relies on:
- The response is
{ data, meta }.datais the generated array;meta.seedis the seed that was used. The script writesdataunchanged, because mongoimport needs a top-level array, and it prints the seed. The default seed lives in the committed script. - The same template, seed and params give the same file. Change any of them and the random draws change, so commit the template and the JSON together.
- Errors stop the script.
--fail-with-body(curl 7.76 or later) turns an HTTP error into a non-zero exit. The script then prints the API's error body, for exampleINVALID_TEMPLATEorGENERATION_FAILED, and exits before it touchesorders.json.--rawfileneeds jq 1.6 or later.
The API key goes in an Authorization: Bearer header; see the
auth reference. Run it with
JSONFABRICA_API_KEY=... ./scripts/regen-orders.sh, then review
git diff test/fixtures/ and commit.
How to mongoimport a JSON array into a throwaway database
mongoimport is one of the MongoDB Database Tools, which are versioned
(100.x) and installed separately from the server; mongosh is a
separate download too. The official mongo Docker image includes
both. To load the fixture into a database you can throw away
afterwards:
mongoimport --uri "mongodb://localhost:27017/shop_fixture" \
--collection orders --drop --jsonArray \
--file test/fixtures/orders.json
The output, with the leading timestamps removed:
connected to: mongodb://localhost:27017/shop_fixture
dropping: `shop_fixture.orders`
20 document(s) imported successfully. 0 document(s) failed to import.
The types survived. Of 20 orders, 19 have a real date in createdAt
(order 3 is the string), exactly one has double prices (order 4), and
every qty is an int:
mongosh shop_fixture --quiet --eval '
print(db.orders.countDocuments({ createdAt: { $type: "date" } }));
print(db.orders.countDocuments({ "items.price": { $type: "double" } }));
print(db.orders.countDocuments({ "items.qty": { $not: { $type: "int" } } }));'
19
1
0
When you're done, mongosh shop_fixture --eval 'db.dropDatabase()'
removes it. A few behaviors worth knowing:
--jsonArrayis required for a top-level array. Without it, mongoimport expects a stream of documents and fails withcannot decode array into a D.- Size. The mongoimport docs describe
--jsonArrayas limited to imports of 16 MB or smaller. In my test a 20 MB array imported fine with 100.20.0, but don't rely on that. For large fixtures, convert to one document per line withjq -c '.[]' orders.json > orders.ndjsonand drop the flag. - Order isn't preserved. By default the documents are inserted in
an arbitrary order (the
--helptext says so), and in my runsfind()didn't return them in file order. Look documents up by_idin tests, not by position.
mongoimport, validators and --drop
Three things caught me out when importing into a collection that has a validator:
--dropremoves the validator. It drops the whole collection, including its options, and the import then creates a plain collection. After an import with--drop,getCollectionInfos()showed empty options. If a validator matters, create the collection first and import without--drop.Rejected documents don't change the exit code. Imported into a validated collection, the fixture gives this (timestamps removed again):
continuing through error: Document failed validation continuing through error: Document failed validation continuing through error: Document failed validation continuing through error: Document failed validation 16 document(s) imported successfully. 4 document(s) failed to import.The exit code was 0. Add
--stopOnErrorand it exits 1, which is what a CI step needs. It doesn't keep the collection clean, though: in my run the 16 valid documents were still imported. Adding--maintainInsertionOrdermakes it stop right after the first failure, as the help text describes; that run imported one document and reported one failure.--bypassDocumentValidationskips the validator. That's useful when you want legacy-shaped data in a validated collection on purpose.
Testing a MongoDB $jsonSchema validator with drift documents
The schema lives in its own file, db/orders.schema.json, so mongosh
and the tests read the same rules:
{
"bsonType": "object",
"required": ["_id", "customer", "items", "status", "createdAt"],
"additionalProperties": false,
"properties": {
"_id": { "bsonType": "objectId" },
"customer": {
"bsonType": "object",
"required": ["name", "email", "address"],
"properties": {
"name": { "bsonType": "string" },
"email": { "bsonType": "string" },
"address": { "bsonType": "object" }
}
},
"items": {
"bsonType": "array",
"minItems": 1,
"items": {
"bsonType": "object",
"required": ["sku", "qty", "price"],
"properties": {
"sku": { "bsonType": "string" },
"qty": { "bsonType": "int", "minimum": 1 },
"price": { "bsonType": "decimal" }
}
}
},
"status": { "enum": ["pending", "paid", "shipped"] },
"createdAt": { "bsonType": "date" },
"shippedAt": { "bsonType": ["date", "null"] }
}
}
MongoDB's $jsonSchema is based on an older JSON Schema draft, with
some keywords left out and bsonType added for BSON types like
objectId, date, decimal and int. If you also validate plain
JSON elsewhere, see
JSON Schema validation for test data generation;
this post sticks to the collection validator. Things to know about it:
additionalProperties: falseincludes_id. If_idisn't listed underproperties, every insert fails, and the error lists"additionalProperties": ["_id"]. The nestedcustomerobject has noadditionalPropertiesrule, so extra fields are allowed there.validationLeveldecides which writes are checked.strict, the default, checks every insert and update.moderateskips updates to documents that already break the schema. In my test, an update to order 3 went through undermoderateand was rejected understrict.offdisables validation.validationActiondecides what happens to a failing write.error, the default, rejects it with error code 121.warnlets it through and writes a "Document would fail validation" entry to the server log. MongoDB 8.1 added a third value,errorAndLog, which rejects the write and also logs it; 8.0 rejects it as invalid.- Existing documents aren't checked. Adding a validator to a
collection that already holds the fixture left all 20 documents in
place. To find the ones that break it, wrap the schema in
$nor:
// mongosh
const fs = require("fs");
const schema = JSON.parse(fs.readFileSync("db/orders.schema.json", "utf8"));
db.runCommand({
collMod: "orders",
validator: { $jsonSchema: schema },
validationLevel: "moderate",
});
db.orders.find({ $nor: [{ $jsonSchema: schema }] }, { _id: 1 });
That query returns exactly orders 2 to 5. Fixing them is a data migration, and the same idea of planting the rows that break it is covered in test data for database migrations.
When a write is rejected, the error's errInfo names the failing
property, the rule and the value it considered; for order 3 that's
createdAt, bsonType: date and the bare string.
Loading MongoDB seed data with mongosh insertMany
For a local dev database, mongosh can create the validated collection
and load the file in one script. mongosh provides EJSON as a global
and supports require("fs"), so the Extended JSON types come through:
// scripts/load-dev.js
// Usage: mongosh "mongodb://localhost:27017/shop_dev" \
// --file scripts/load-dev.js
const fs = require("fs");
const read = (path) => fs.readFileSync(path, "utf8");
db.orders.drop();
db.createCollection("orders", {
validator: { $jsonSchema: JSON.parse(read("db/orders.schema.json")) },
validationLevel: "strict",
validationAction: "error",
});
const docs = EJSON.parse(read("test/fixtures/orders.json"));
try {
db.orders.insertMany(docs, { ordered: false });
} catch (err) {
// Expected: the four planted drift documents fail validation.
print(`${err.writeErrors?.length ?? "?"} rejected: ${err.message}`);
}
print(`${db.orders.countDocuments()} orders loaded`);
4 rejected: Document failed validation
16 orders loaded
ordered: false keeps inserting past failures and throws once at the
end, with one entry per failure in writeErrors. The default,
ordered: true, stops at the first failure. The script drops the
collection first, so it's for dev databases only. For seeding a
local database in general, whatever the engine, see
how to seed a database with test data for local development.
Inserting the fixture with the Node.js driver's insertMany
The Node.js driver doesn't interpret Extended JSON on insert. It
serializes whatever JavaScript values you give it. If you read the
fixture with JSON.parse, {"$date": "…"} is just an object with a
$date key, and in my test the server stored it as an embedded
document without complaint. Only _id was refused (message wrapped
to fit):
MongoServerError: _id fields may not contain '$'-prefixed fields:
$oid is not valid for storage.
So parse with EJSON.parse instead. The mongodb package re-exports
the bson library as BSON, so BSON.EJSON.parse turns $oid,
$date and $numberDecimal into ObjectId, Date and Decimal128
values. It uses relaxed mode by default, which is right for this
fixture. Pass { relaxed: false } if your file uses $numberLong or
$numberDouble and the exact type matters.
The app has its own read-side check, separate from the validator:
// src/parse-order.mjs: the app's read-side check on stored orders.
import { Decimal128 } from "mongodb";
export function parseOrder(doc) {
const fail = (why) => {
throw new Error(`order ${doc._id}: ${why}`);
};
if (typeof doc.customer?.email !== "string") fail("no customer.email");
if (!(doc.createdAt instanceof Date)) fail("createdAt is not a date");
for (const item of doc.items) {
if (!(item.price instanceof Decimal128)) fail("price is not decimal");
}
// Unknown fields are ignored on purpose (tolerant reader).
return {
id: doc._id.toHexString(),
email: doc.customer.email,
status: doc.status,
createdAt: doc.createdAt,
items: doc.items.map(({ sku, qty, price }) => ({
sku,
qty,
price: price.toString(),
})),
};
}
The test uses Node's built-in test runner and creates two collections
in a database named for the process: orders, with the validator,
and legacy, with no validator, standing in for data written before
the schema existed.
// test/orders.test.mjs: run with `node --test`
import { test, before, after } from "node:test";
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { MongoClient, BSON } from "mongodb";
import { parseOrder } from "../src/parse-order.mjs";
const uri = process.env.MONGO_URL ?? "mongodb://localhost:27017";
const client = new MongoClient(uri, { directConnection: true });
const db = client.db(`orders_test_${process.pid}`); // throwaway
const read = (path) => readFile(path, "utf8");
const schema = JSON.parse(await read("db/orders.schema.json"));
const docs = BSON.EJSON.parse(await read("test/fixtures/orders.json"));
// Planted by the template at loop indexes 2-5.
const oid = (n) => new BSON.ObjectId(`6710c0de${String(n).padStart(16, "0")}`);
const DRIFT = {
missingEmail: oid(2),
stringDate: oid(3),
doublePrice: oid(4),
extraField: oid(5),
};
const driftIds = Object.values(DRIFT);
const isDrift = (doc) => driftIds.some((id) => id.equals(doc._id));
const byId = (id) => docs.find((doc) => id.equals(doc._id));
before(async () => {
await db.createCollection("orders", {
validator: { $jsonSchema: schema },
validationLevel: "strict",
validationAction: "error",
});
// No validator here: data written before the schema existed.
await db.collection("legacy").insertMany(docs);
});
after(async () => {
await db.dropDatabase();
await client.close();
});
test("validator accepts every conforming order", async () => {
const valid = docs.filter((doc) => !isDrift(doc));
const res = await db.collection("orders").insertMany(valid);
assert.equal(res.insertedCount, docs.length - driftIds.length);
});
for (const [name, id] of Object.entries(DRIFT)) {
test(`validator rejects ${name}`, async () => {
await assert.rejects(
db.collection("orders").insertOne(byId(id)),
(err) => err.code === 121, // DocumentValidationFailure
);
});
}
test("$jsonSchema query finds exactly the drift documents", async () => {
const bad = await db
.collection("legacy")
.find({ $nor: [{ $jsonSchema: schema }] })
.toArray();
assert.deepEqual(
bad.map((doc) => doc._id.toHexString()).sort(),
driftIds.map((id) => id.toHexString()).sort(),
);
});
test("parseOrder handles each drift document as stored", async () => {
const load = (id) => db.collection("legacy").findOne({ _id: id });
const [email, date, price, extra] = await Promise.all(driftIds.map(load));
assert.throws(() => parseOrder(email), /customer\.email/);
assert.throws(() => parseOrder(date), /createdAt/);
assert.throws(() => parseOrder(price), /price/);
assert.equal(parseOrder(extra).id, DRIFT.extraField.toHexString());
});
Against MongoDB 8.0.34 (timings removed):
✔ validator accepts every conforming order
✔ validator rejects missingEmail
✔ validator rejects stringDate
✔ validator rejects doublePrice
✔ validator rejects extraField
✔ $jsonSchema query finds exactly the drift documents
✔ parseOrder handles each drift document as stored
ℹ tests 7
ℹ pass 7
ℹ fail 0
Two of these tests need explaining:
- The
$norquery finds exactly the four. Add a drift document to the template without updatingDRIFT, or make a normal order accidentally invalid, and this fails. - The app and the validator disagree on purpose.
parseOrderrejects the missing email, the string date and the double price, but accepts the extra field, because it ignores unknown fields. The validator rejects all four. The test records that difference, so changing either side is a visible decision.
Running the same fixture against Testcontainers MongoDB
The test only needs a connection string, so the same fixture runs
against a container started per test run. With the
@testcontainers/mongodb module, the top of the test file becomes:
import { MongoDBContainer } from "@testcontainers/mongodb";
// Start a throwaway container unless MONGO_URL points at a server.
const container = process.env.MONGO_URL
? undefined
: await new MongoDBContainer("mongo:8.0").start();
const uri = process.env.MONGO_URL ?? container.getConnectionString();
const client = new MongoClient(uri, { directConnection: true });
Add await container?.stop(); to after(). The module starts MongoDB
as a single-node replica set, which is why the client sets
directConnection: true; without it, the driver tries to reach the
replica set member by its container hostname. Pin the image tag to the
server version you run in production. I ran this test file against a
server via MONGO_URL; the container branch wasn't run for this post.
For Testcontainers in general, including
JUnit 5 and Vitest setups, see
Testcontainers test data.
FAQ
How do I import a JSON array into MongoDB?
Use mongoimport with the --jsonArray flag, for example
mongoimport --uri mongodb://localhost:27017/shop_test --collection orders --jsonArray --file orders.json.
Without the flag, mongoimport expects a stream of documents and fails
on a top-level array. The mongoimport docs describe --jsonArray as
limited to imports of 16 MB or smaller, so for large files write one
document per line and drop the flag.
What is the difference between canonical and relaxed Extended JSON?
Both are modes of MongoDB Extended JSON v2. Canonical mode wraps every
typed value, including numbers, so ints, longs and doubles survive a
round trip exactly. Relaxed mode writes numbers as plain JSON numbers
and most dates as ISO-8601 strings inside $date, which is easier to
read but loses the difference between int, long and double. ObjectId
and Decimal128 values look the same in both modes.
Why does mongoimport store my dates as strings?
Because the file contains plain strings. mongoimport only creates a
BSON date when the value is wrapped in an Extended JSON $date
object. A bare ISO-8601 string is imported as a string, so date range
queries and sorts compare text, and a $jsonSchema rule with
bsonType date rejects the document.
Does a MongoDB $jsonSchema validator check existing documents?
No. Adding or changing a validator with collMod does not check the
documents already in the collection. With validationLevel strict,
the default, every later insert and update is validated; with
moderate, updates to documents that already fail the schema are not
checked. To find existing documents that fail, query with $nor
around a $jsonSchema expression.
How do I insert Extended JSON with the MongoDB Node.js driver?
Parse the file with EJSON.parse, which the mongodb package exposes
as BSON.EJSON, and pass the result to insertMany. Plain
JSON.parse leaves $date and $numberDecimal as ordinary objects,
which MongoDB stores as embedded documents, and an $oid inside _id
is rejected. EJSON.parse uses relaxed mode by default; pass
relaxed: false if the file uses $numberLong or $numberDouble and
the exact type matters.
Does JsonFabrica have a MongoDB package, plugin or SDK?
No. JsonFabrica is a JSON-over-HTTP API with no MongoDB package, plugin, SDK or connector, and it never connects to your database. A small script you own sends the template and a seed to the API and saves the generated JSON file. mongoimport, mongosh or your driver then loads that file like any other.
One seeded template gives MongoDB a fixture whose dates are dates,
whose money is Decimal128, and whose drift documents sit at known
ObjectIds, rebuilt identically from the same seed. Rendering a raw
template is a single request to the JsonFabrica API; the
templates API reference documents the
request body and the { data, meta } response.
Generate realistic test data with JsonFabrica
Describe the shape of your data once, then generate as many fresh, realistic JSON documents as you need via a simple API call.