How to Set Unsigned Integer Field in Mongoose

Updated: January 3, 2024 By: Guest Contributor Post a comment

Overview

Mongoose is a robust Node.js library that provides a schema-based solution to model application data with MongoDB. Using unsigned integers can be crucial for some database schemas, as they allow storage of only non-negative numbers, thus ensuring data integrity. This tutorial will guide you through setting up unsigned integers in Mongoose schemas.

Understanding Mongoose Schemas

Before diving into unsigned integers, let’s revise how Mongoose schemas work. A schema in Mongoose is an outline of the structure of the document, including default values, validators, etc. Here is a basic example:

const mongoose = require('mongoose');
const { Schema } = mongoose;

const userSchema = new Schema({
  name: String,
  age: Number,
});

const User = mongoose.model('User', userSchema);

Setting Up Unsigned Integer

In Mongoose, there isn’t an explicit ‘unsigned’ type like in SQL-based databases. However, you can mimic the behavior using validators like so:

const userSchema = new Schema({
  positiveAge: {
    type: Number,
    min: [0, 'Age must be a positive number.']
  }
});

This will effectively create an unsigned integer field, as any negative number will fail validation.

Defining Custom Validators

For more control or complex validation logic, you can define custom validators. Here’s one for ensuring a number is an unsigned integer:

function isUnsignedInteger(value) {
  return Number.isInteger(value) && value ">= 0;
}

const productSchema = new Schema({
  stock: {
    type: Number,
    validate: [isUnsignedInteger, 'Stock must be an unsigned integer.']
  }
});

Handling Large Unsigned Integers

JavaScript’s Number type can only safely represent integers up to 2^53 – 1. If you need to handle larger unsigned integers, consider using the ‘bigint’ package and the following schema:

const bigint = require('bigint');

const highValueSchema = new Schema({
  highValue: {
    type: mongoose.Schema.Types.Decimal128,
    validate: {
      validator: function(v) {
        return bigint(v.toString()).ge(0);
      },
      message: 'highValue must be an unsigned integer.'
    }
  }
});

Note that ‘Decimal128’ is a BSON type that can handle big integers at the cost of some arithmetic convenience.

Advance Use Case: Schema Pre-hooks

Sometimes you might want to ensure an unsigned field also meets other criteria before saving. Pre-hooks allow you to run code prior to an event, in this case, a ‘save’ event:

productSchema.pre('save', function(next) {
  if (this.stock < 0) {
    next(new Error('Stock cannot be negative'));
  } else if (!Number.isInteger(this.stock)) {
    next(new Error('Stock must be an integer'));
  } else {
    next();
  }
});

Integrating Unsigned Integer Fields with Asynchronous Validators

If you have an asynchronous operation to perform as part of your validation, you’ll need to adapt your strategy. Here’s how to do it with async/await:

productSchema.path('stock').validate(async function(value) {
  if (!Number.isInteger(value)) {
    throw new Error('Stock must be an integer');
  }
  if (value < 0) {
    throw new Error('Stock cannot be negative');
  }
  // Add your asynchronous validations here
}, 'Invalid stock');

Conclusion

In this tutorial, we’ve explored how to work with unsigned integers in Mongoose, though they are not natively supported like in SQL databases. We covered validators, custom validation functions, handling large unsigned integers, pre-hooks, and async validators. Armed with this knowledge, you can now confidently use unsigned integers in your Mongoose schemas.