Skip to content

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:

MethodWhen CalledUse Case
toJSON()Automatically by JSON.stringify(), res.json()Express, Fastify, etc.
serialize()ManuallyNext.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() called

Express 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 automatically

These 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>[] directly

Types

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 TypeSerialized ToExample
ObjectIdstring (hex)'507f1f77bcf86cd799439011'
Datestring (ISO 8601)'2024-01-15T10:30:00.000Z'
Longnumber or bigint*9007199254740991
Decimal128string'99999999999999.99'
Binarystring (base64)'aGVsbG8='
Timestamp{ t: number, i: number }{ t: 1234567890, i: 1 }
Nested objectsRecursively serialized-
ArraysEach 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;

Released under the MIT License.