Sling Academy
Home/Node.js/How to use Mongoose without a schema

How to use Mongoose without a schema

Last updated: December 30, 2023

Introduction

Mongoose is a popular Object-Document Mapping (ODM) library for MongoDB and Node.js that simplifies interactions with the database by providing a straight-forward, schema-based solution to model application data. However, there are situations when you may want to forgo the rigid structure of a schema, favoring a more flexible approach. This is where schemaless usage of Mongoose comes in handy, especially when dealing with variable data structures that don’t fit well into a defined schema, or when rapidly prototyping. In this tutorial, we’ll explore how to harness the full potential of Mongoose without a strict schema definition.

Getting Started

First, you’ll need to have Node.js and MongoDB installed on your system. Ensure they are both accessible from the command line. Then, create a new directory for our project, navigate to it, and initialize a new Node.js project:

mkdir my-mongoose-noschema

cd my-mongoose-noschema

npm init -y

Now, install Mongoose:

npm install mongoose

Connecting to MongoDB

To connect to MongoDB using Mongoose:

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/my_database', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

Defining the Model

Rather than defining a strict schema, you’ll use the Schema-less feature of MongoDB through Mongoose:

const Any = new mongoose.Mongoose().model('Any', new mongoose.Schema({}, { strict: false }));

Basic CRUD Operations

Creating a Record

Without a strict schema, you can insert documents that don’t adhere to a specific structure:

const dynamicDocument = new Any({ name: 'John Doe', hobbies: ['skydiving', 'chess'] });

(async () => {
  try {
    await dynamicDocument.save();
    console.log('Document saved successfully.');
  } catch (error) {
    console.error('Error saving document:', error);
  }
})();

Reading Documents

You can also fetch records without needing them to conform to a schema:

(async () => {
  try {
    const documents = await Any.find();
    console.log(documents);
  } catch (error) {
    console.error('Error fetching documents:', error);
  }
})();

Updating Records

Even with schema-less models, updating is done in much the same way:

(async () => {
  try {
    const result = await Any.updateOne({ name: 'John Doe' }, { $set: { age: 30 } });
    console.log('Document updated:', result);
  } catch (error) {
    console.error('Error updating document:', error);
  }
})();

Deleting Records

Deletion remains uncomplicated:

(async () => {
  try {
    const result = await Any.deleteOne({ name: 'John Doe' });
    console.log('Document deleted:', result);
  } catch (error) {
    console.error('Error deleting document:', error);
  }
})();

Advanced Usage

When considering advanced usage, think about using discriminators for documents with certain common fields, or hybrid schema design which allows defined structure for commonly used fields, while still permitting arbitrary fields.

Handling Data Validation

Even without a schema, you may want to validate your data before saving to MongoDB:

Any.validate(dynamicDocument, (error) => {
  if (error) {
    console.error('Validation error:', error);
  }
});

Indexing for Performance

Indexes can still be added for a performance boost, particularly on fields that will be queried frequently:

Any.createIndexes({ 'hobbies': 1 });

Conclusion

While schemas provide a critical underpinning of structure to Mongoose and MongoDB interactions, schemaless operation is a valid and powerful approach when flexibility is paramount. Through strategic thought and an understanding of how to appropriately use this unstructured data type, you can make full use of Mongoose’s dynamic and powerful features.

Remember that with great power comes great responsibility, and operating without schemas should be done with care to prevent potential issues like data inconsistency and difficulty in later attempting to enforce a schema on data that was hitherto unstructured. Happy coding!

Next Article: Fix MongooseServerSelectionError: connect ECONNREFUSED

Previous Article: How to create a fixed-size collection with MongooseJS

Series: Mongoose.js Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates