Serialization API
Functions and types for converting BSON documents to JSON-safe values.
Import
typescript
import { serialize, serializeArray, MonchArray } from '@codician-team/monch';
import type { Serialized, WithSerialize } from '@codician-team/monch';Automatic vs Manual Serialization
Monch documents have both toJSON() and serialize() methods:
| Method | When Called | Use Case |
|---|---|---|
toJSON() | Automatically by JSON.stringify(), res.json() | Express, Fastify, etc. |
serialize() | Manually | Next.js (blocks toJSON) |
typescript
// Express - automatic (toJSON called by res.json)
app.get('/api/user', async (req, res) => {
const user = await Users.findById(id);
res.json(user); // Just works!
});
// Next.js - manual (serialize required)
const user = await Users.findById(id);
return <Component user={user.serialize()} />;serialize()
Serialize a single document:
typescript
function serialize<T>(doc: T): Serialized<T>Example
typescript
const user = await Users.findOne({ email: 'alice@example.com' });
const plain = serialize(user);
// Before: { _id: ObjectId('...'), createdAt: Date }
// After: { _id: '...', createdAt: '2024-01-15T...' }serializeArray()
Serialize an array of documents:
typescript
function serializeArray<T>(docs: T[]): Serialized<T>[]Example
typescript
const users = await Users.find().toArray();
const plain = serializeArray(users);MonchArray
Array subclass with built-in serialization methods:
typescript
class MonchArray<T> extends Array<T> {
serialize(): Serialized<T>[]; // Manual serialization
toJSON(): Serialized<T>[]; // Called by JSON.stringify()
}Returned by .toArray() on cursors:
typescript
const users = await Users.find().toArray();
// users: MonchArray<User>
users.length; // Works (it's an array)
users.map(...); // Works
users.filter(...); // Works
users.serialize(); // Manual: Serialize all documents
JSON.stringify(users); // Automatic: toJSON() calledExpress Example
typescript
app.get('/api/users', async (req, res) => {
const users = await Users.find({}).toArray();
res.json(users); // toJSON() serializes automatically
});Document Methods
Documents returned from queries have both .serialize() and .toJSON() methods:
typescript
const user = await Users.findOne({ email: 'alice@example.com' });
// Manual serialization (for Next.js)
const plain = user?.serialize();
// Automatic serialization (for Express, JSON.stringify)
res.json(user); // toJSON() called automaticallyThese methods are available on documents from:
findOne()findById()insertOne()insertMany()(each document)updateOne()findOneAndUpdate()findOneAndDelete()findOneAndReplace()find().toArray()(each document)
Cursor .serialize() Method
Execute query and serialize in one step:
typescript
const users = await Users.find({ role: 'admin' }).serialize();
// Returns Serialized<User>[] directlyTypes
Serialized<T>
Type that represents the serialized version of a document:
typescript
type User = {
_id: ObjectId;
name: string;
createdAt: Date;
};
type SerializedUser = Serialized<User>;
// {
// _id: string;
// name: string;
// createdAt: string;
// }WithSerialize<T>
Type for objects with serialization methods:
typescript
type WithSerialize<T> = T & {
serialize(): Serialized<T>; // Manual (Next.js)
toJSON(): Serialized<T>; // Automatic (Express, JSON.stringify)
};Conversion Table
| BSON Type | Serialized To | Example |
|---|---|---|
ObjectId | string (hex) | '507f1f77bcf86cd799439011' |
Date | string (ISO 8601) | '2024-01-15T10:30:00.000Z' |
Long | number or bigint* | 9007199254740991 |
Decimal128 | string | '99999999999999.99' |
Binary | string (base64) | 'aGVsbG8=' |
Timestamp | { t: number, i: number } | { t: 1234567890, i: 1 } |
| Nested objects | Recursively serialized | - |
| Arrays | Each element serialized | - |
*Long values serialize to number when within JavaScript's safe integer range (±9,007,199,254,740,991). Values outside this range serialize to bigint.
Usage Patterns
Single Document
typescript
const user = await Users.findOne({ email: 'alice@example.com' });
return user?.serialize() ?? null;Array of Documents
typescript
// Method 1: On cursor
const users = await Users.find({ role: 'admin' }).serialize();
// Method 2: After toArray
const users = await Users.find({ role: 'admin' }).toArray();
const plain = users.serialize();With Pagination
typescript
const result = await Users.find()
.sort({ createdAt: -1 })
.paginate({ page: 1, limit: 20 });
return {
...result,
data: result.data.map(user => user.serialize()),
};Conditional Serialization
typescript
async function getUser(id: string, serialize = true) {
const user = await Users.findById(id);
if (!user) return null;
return serialize ? user.serialize() : user;
}Enriched Serialization
typescript
const user = await Users.findById(id);
return user ? {
...user.serialize(),
fullName: `${user.firstName} ${user.lastName}`,
isActive: user.status === 'active',
} : null;