Skip to content

bobosneefdev/ZongoDB

Repository files navigation

ZongoDB

Zod schemas + MongoDB = <3

About

Important notes:

  • I am always trying to improve/tweak things, breaking changes occur often.
  • I have no background in security.
  • Zod V3 is slow, this wrapper will hurt performance.

Paths

  • Collection schemas are traversed and flattened into a map used for accessing nested schemas.
  • Here's how the traverser creates the map:
    • Unwrap schema (get main schema within ZodOptional, ZodNullable, and ZodEffects)
    • If the schema is a ZodObject or ZodTuple, all nested schemas will be traversed.
    • If the schema is a ZodUnion, all possible schemas will be traversed.
    • If a path for some reason has duplicate schemas, a ZodUnion will be created.
  • Curious about what paths are available in your instance? Just log the "flattenedSchemas" property.
  • Zongo uses this map to verify:
    • Queried values (when using operators only existence is verified)
    • Update values
    • Transform function return data
  • When trying to query, update, or transform, provided paths MUST exist in the map.
    • If it doesn't Zongo will throw an error.

Setup

Install

npm install @bobosneefdev/zongodb

Config

Create and configure zongo_config.json or add to your .env in your cwd according to this Zod schema:

// Your config file/env variables must be parsable by this Zod schema.
z.object({
    ZONGO_MONGO_URI: z.string()
        .default("mongodb://localhost:27017"),
    ZONGO_BACKUP_DIR: z.string()
        .default("./ZongoDB/backups"),
    ZONGO_LOG_LEVEL: z.enum([
        "error",
        "warn",
        "info",
        "debug"
    ]),
});

Examples

enum State {
    CA = "CA"
}

enum CarMake {
    VOLKSWAGEN = "Volkswagen",
    FORD = "Ford"
}

const testDatabase = new ZongoDB(
    "ZongoTest",
    {
        people: z.object({
            created_at: z.date(),
            name: z.literal("John Doe"),
            property: z.object({
                cars: z.array(z.object({
                    state_registered: z.nativeEnum(State),
                    license_plate: z.string(),
                    make: z.nativeEnum(CarMake),
                    model: z.string(),
                    year: z.number()
                        .int()
                })).optional(),
                homes: z.array(z.object({
                    address_1: z.string(),
                    address_2: z.string()
                        .nullable(),
                    city: z.string(),
                    state: z.nativeEnum(State),
                    zip: z.number()
                        .int()
                        .min(10000)
                        .max(99999)
                })).optional(),
                cryptocurrency: z.object({
                    btc_address: z.string()
                        .regex(/^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}$/),
                }).optional(),
            })
        }),
    },
    {
        initIndexes: {
            people: {
                "created_at": 1,
            }
        }
    }
);

// ✔️ VALID
await testDatabase.insertOne(
    "people",
    { // complete, parsable document :)
        created_at: new Date(),
        name: "John Doe",
        property: {
            cars: [{
                state_registered: State.CA,
                license_plate: "7TJF456",
                make: CarMake.VOLKSWAGEN,
                model: "Passat",
                year: 2001
            }],
        }
    },
);

// ❌ INVALID DATA
await testDatabase.updateOne(
    "people",
    {
        created_at: new Date(),
        name: "John Doe",
    },
    {
        // upsert requires fully parsable document ("created_at" and "name" are missing)
        property: {
            cars: [{
                state_registered: State.CA,
                license_plate: "7TJF456",
                make: CarMake.VOLKSWAGEN,
                model: "Passat",
                year: 2001
            }],
        }
    },
    {
        upsert: true
    }
);

// ❌ INVALID QUERY
await database.findMany(
    "people",
    {
        // fails regex
        "property.crypto.btc_address": "totally-not-a-real-wallet",
    }
);

// ❌ INVALID QUERY
await database.deleteOne(
    "people",
    {
        // invalid literal
        name: "John Deer"
    }
);

// ✔️ VALID
await database.transformOne(
    "people",
    {
        name: "John Doe",
    },
    {
        "property.cars": function (cars) {
            if (!cars) {
                cars = [];
            }
            cars.push({
                state_registered: State.CA,
                license_plate: "3LTO552",
                make: CarMake.FORD,
                model: "Focus RS",
                year: 2016
            });
            // returned value in transform must be parsable by path at schema
            return cars;
        }
    }
)

About

Zod schemas + MongoDB

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •