Skip to content

Instantly share code, notes, and snippets.

@jdnichollsc
Last active June 17, 2025 18:54
Show Gist options
  • Save jdnichollsc/752a5246bee09ab2faf82841e5defb42 to your computer and use it in GitHub Desktop.
Save jdnichollsc/752a5246bee09ab2faf82841e5defb42 to your computer and use it in GitHub Desktop.
NodeJS MongoDB/Mongoose UUID conversion for .NET Guid compatibility using BSON Binary (subtype 3) - https://replit.com/@jdnichollsc/MongoDB-Guid-conversion-for-NodeJSNET-compatibility?v=1
import mongoose from "mongoose";
// Converts a legacy MongoDB Binary (subtype 3, UUID_OLD) to a UUID string.
// Note: The issue is not with .NET's Guid type (which is just a 128-bit integer),
// but with how the legacy MongoDB driver (and the old BinData subtype 3) handled byte order.
// The legacy driver stored UUIDs with a mixed-endian format due to a lack of specification in early MongoDB versions.
export function legacyBinaryToUuidString(binaryData: mongoose.mongo.Binary | null) {
if (!binaryData?.buffer) return undefined;
const buffer = binaryData.buffer;
// Legacy MongoDB UUID (subtype 3) uses little-endian for the first 3 components
// and big-endian for the rest, due to the old driver implementation.
const part1 = Buffer.from(buffer.subarray(0, 4)).reverse().toString("hex"); // First 4 bytes reversed
const part2 = Buffer.from(buffer.subarray(4, 6)).reverse().toString("hex"); // Next 2 bytes reversed
const part3 = Buffer.from(buffer.subarray(6, 8)).reverse().toString("hex"); // Next 2 bytes reversed
const part4 = Buffer.from(buffer.subarray(8, 10)).toString("hex"); // Next 2 bytes as-is
const part5 = Buffer.from(buffer.subarray(10, 16)).toString("hex"); // Last 6 bytes as-is
return `${part1}-${part2}-${part3}-${part4}-${part5}`;
}
// Converts a UUID string to a legacy MongoDB Binary (subtype 3, UUID_OLD).
// This matches the byte order used by the old MongoDB .NET driver (subtype 3).
export function uuidStringToLegacyBinary(uuidStr: string) {
if (!uuidStr || typeof uuidStr !== "string") return undefined;
const hex = uuidStr.replace(/-/g, "");
// To reverse the conversion, we need to reverse the first 3 parts back to little-endian
const part1 = Buffer.from(hex.slice(0, 8), "hex").reverse(); // First 4 bytes - reverse back
const part2 = Buffer.from(hex.slice(8, 12), "hex").reverse(); // Next 2 bytes - reverse back
const part3 = Buffer.from(hex.slice(12, 16), "hex").reverse(); // Next 2 bytes - reverse back
const part4 = Buffer.from(hex.slice(16, 20), "hex"); // Next 2 bytes - keep as-is
const part5 = Buffer.from(hex.slice(20, 32), "hex"); // Last 6 bytes - keep as-is
const legacyBuffer = Buffer.concat([part1, part2, part3, part4, part5]);
return new mongoose.mongo.Binary(legacyBuffer, mongoose.mongo.Binary.SUBTYPE_UUID_OLD);
}
// Normalizes a GUID value to a legacy MongoDB Binary (subtype 3) if needed.
// Accepts a UUID string, Buffer, or Binary. Throws for invalid input.
export function normalizeGuidValue(val: string | Buffer | mongoose.mongo.Binary) {
if (typeof val === "string") {
return uuidStringToLegacyBinary(val);
}
if (Buffer.isBuffer(val)) {
return new mongoose.mongo.Binary(val, 3);
}
if (val instanceof mongoose.mongo.Binary) {
return val;
}
throw new Error("Invalid CountryID format for query");
}
import mongoose, { Schema } from "mongoose";
import { legacyBinaryToUuidString, normalizeGuidValue } from "./helpers";
const addressSchema = new mongoose.Schema(
{
Country: { type: String },
CountryCode: { type: String },
CountryID: {
type: Schema.Types.Mixed,
get: (val: string | mongoose.mongo.Binary | Buffer | undefined) => {
if (!val) return undefined;
if (typeof val === "string") return val;
if (Buffer.isBuffer(val)) {
const binary = new mongoose.mongo.Binary(val, 3);
return legacyBinaryToUuidString(binary);
}
if (val instanceof mongoose.mongo.Binary) {
return legacyBinaryToUuidString(val);
}
throw new Error("Unsupported format for CountryID");
},
set: (val: string | mongoose.mongo.Binary | Buffer | undefined) => {
if (!val) return undefined;
return normalizeGuidValue(val);
},
},
},
{
_id: false,
toObject: { getters: true },
toJSON: { getters: true },
},
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment