diff --git a/.github/workflows/relayer-build.yml b/.github/workflows/relayer-build.yml
index 143484ed0..6a5c76490 100644
--- a/.github/workflows/relayer-build.yml
+++ b/.github/workflows/relayer-build.yml
@@ -6,7 +6,13 @@ on:
   pull_request:
 
 env:
-  RPC_URL: "http://localhost:8545"
+  RELAYER_RPC_URL: "http://localhost:8545"
+  TTL: ${{ vars.RELAYER_TTL }}
+  LIMIT: ${{ vars.RELAYER_LIMIT }}
+  MONGO_DB_URI: ${{ secrets.RELAYER_MONGO_DB_URI }}
+  MONGODB_USER: ${{ secrets.MONGODB_USER }}
+  MONGODB_PASSWORD: ${{ secrets.MONGODB_PASSWORD }}
+  MONGODB_DATABASE: ${{ secrets.MONGODB_DATABASE }}
 
 concurrency:
   group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
diff --git a/apps/relayer/.env.example b/apps/relayer/.env.example
index a6389442a..b0628a7ce 100644
--- a/apps/relayer/.env.example
+++ b/apps/relayer/.env.example
@@ -5,6 +5,12 @@ LIMIT=10
 # Coordinator RPC url
 RELAYER_RPC_URL=http://localhost:8545
 
+# MongoDB configuration
+MONGO_DB_URI=mongodb://localhost
+MONGODB_USER=maci
+MONGODB_PASSWORD=
+MONGODB_DATABASE=maci-relayer
+
 # Allowed origin host, use comma to separate each of them
 ALLOWED_ORIGINS=
 
diff --git a/apps/relayer/package.json b/apps/relayer/package.json
index b5aa86a57..e22ff6d7d 100644
--- a/apps/relayer/package.json
+++ b/apps/relayer/package.json
@@ -26,6 +26,7 @@
   "dependencies": {
     "@nestjs/common": "^10.4.7",
     "@nestjs/core": "^10.4.7",
+    "@nestjs/mongoose": "^10.1.0",
     "@nestjs/platform-express": "^10.4.7",
     "@nestjs/platform-socket.io": "^10.3.10",
     "@nestjs/swagger": "^8.0.3",
@@ -42,6 +43,7 @@
     "helmet": "^8.0.0",
     "maci-contracts": "workspace:^2.5.0",
     "maci-domainobjs": "workspace:^2.5.0",
+    "mongoose": "^8.9.3",
     "mustache": "^4.2.0",
     "reflect-metadata": "^0.2.0",
     "rxjs": "^7.8.1",
@@ -57,6 +59,7 @@
     "@types/supertest": "^6.0.2",
     "fast-check": "^3.23.1",
     "jest": "^29.5.0",
+    "mongodb-memory-server": "^10.1.3",
     "supertest": "^7.0.0",
     "ts-jest": "^29.2.5",
     "typescript": "^5.7.2"
diff --git a/apps/relayer/tests/app.test.ts b/apps/relayer/tests/app.test.ts
index 26c6a977d..75194e545 100644
--- a/apps/relayer/tests/app.test.ts
+++ b/apps/relayer/tests/app.test.ts
@@ -6,7 +6,7 @@ import type { App } from "supertest/types";
 
 import { AppModule } from "../ts/app.module";
 
-describe("e2e", () => {
+describe("Integration", () => {
   let app: INestApplication;
 
   beforeAll(async () => {
diff --git a/apps/relayer/tests/messages.test.ts b/apps/relayer/tests/messages.test.ts
index 72d59ac63..401d3971a 100644
--- a/apps/relayer/tests/messages.test.ts
+++ b/apps/relayer/tests/messages.test.ts
@@ -8,7 +8,7 @@ import type { App } from "supertest/types";
 
 import { AppModule } from "../ts/app.module";
 
-describe("e2e messages", () => {
+describe("Integration messages", () => {
   let app: INestApplication;
 
   beforeAll(async () => {
diff --git a/apps/relayer/ts/app.module.ts b/apps/relayer/ts/app.module.ts
index fdc8e1fa6..752e77a5f 100644
--- a/apps/relayer/ts/app.module.ts
+++ b/apps/relayer/ts/app.module.ts
@@ -1,7 +1,9 @@
 import { Module } from "@nestjs/common";
+import { MongooseModule } from "@nestjs/mongoose";
 import { ThrottlerModule } from "@nestjs/throttler";
 
 import { MessageModule } from "./message/message.module";
+import { MessageBatchModule } from "./messageBatch/messageBatch.module";
 
 @Module({
   imports: [
@@ -11,7 +13,26 @@ import { MessageModule } from "./message/message.module";
         limit: Number(process.env.LIMIT),
       },
     ]),
+    MongooseModule.forRootAsync({
+      useFactory: async () => {
+        if (process.env.NODE_ENV === "test") {
+          const { getTestMongooseModuleOptions } = await import("./jest/mongo");
+
+          return getTestMongooseModuleOptions();
+        }
+
+        return {
+          uri: process.env.MONGO_DB_URI,
+          auth: {
+            username: process.env.MONGODB_USER,
+            password: process.env.MONGODB_PASSWORD,
+          },
+          dbName: process.env.MONGODB_DATABASE,
+        };
+      },
+    }),
     MessageModule,
+    MessageBatchModule,
   ],
 })
 export class AppModule {}
diff --git a/apps/relayer/ts/jest/mongo.ts b/apps/relayer/ts/jest/mongo.ts
new file mode 100644
index 000000000..074162357
--- /dev/null
+++ b/apps/relayer/ts/jest/mongo.ts
@@ -0,0 +1,20 @@
+import type { MongooseModuleOptions } from "@nestjs/mongoose";
+
+/**
+ * Get test mongoose module options
+ *
+ * @param options mongoose module options
+ * @returns mongoose module options for testing
+ */
+export const getTestMongooseModuleOptions = async (
+  options: MongooseModuleOptions = {},
+): Promise<MongooseModuleOptions> => {
+  // eslint-disable-next-line import/no-extraneous-dependencies
+  const { MongoMemoryServer } = await import("mongodb-memory-server");
+  const mongod = await MongoMemoryServer.create();
+
+  return {
+    uri: mongod.getUri(),
+    ...options,
+  };
+};
diff --git a/apps/relayer/ts/message/__tests__/message.repository.test.ts b/apps/relayer/ts/message/__tests__/message.repository.test.ts
new file mode 100644
index 000000000..d43c8a28b
--- /dev/null
+++ b/apps/relayer/ts/message/__tests__/message.repository.test.ts
@@ -0,0 +1,77 @@
+import { ZeroAddress } from "ethers";
+import { Keypair } from "maci-domainobjs";
+import { Model } from "mongoose";
+
+import { MessageRepository } from "../message.repository";
+import { Message } from "../message.schema";
+
+import { defaultSaveMessagesArgs } from "./utils";
+
+describe("MessageRepository", () => {
+  const defaultMessages: Message[] = [
+    {
+      publicKey: new Keypair().pubKey.serialize(),
+      data: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
+      maciContractAddress: ZeroAddress,
+      poll: 0,
+    },
+  ];
+
+  const mockMessageModel = {
+    find: jest
+      .fn()
+      .mockReturnValue({ limit: jest.fn().mockReturnValue({ exec: jest.fn().mockResolvedValue(defaultMessages) }) }),
+    insertMany: jest.fn().mockResolvedValue(defaultMessages),
+  } as unknown as Model<Message>;
+
+  beforeEach(() => {
+    mockMessageModel.find = jest
+      .fn()
+      .mockReturnValue({ limit: jest.fn().mockReturnValue({ exec: jest.fn().mockResolvedValue(defaultMessages) }) });
+    mockMessageModel.insertMany = jest.fn().mockResolvedValue(defaultMessages);
+  });
+
+  afterEach(() => {
+    jest.clearAllMocks();
+  });
+
+  test("should create messages properly", async () => {
+    const repository = new MessageRepository(mockMessageModel);
+
+    const result = await repository.create(defaultSaveMessagesArgs);
+
+    expect(result).toStrictEqual(defaultMessages);
+  });
+
+  test("should throw an error if creation is failed", async () => {
+    const error = new Error("error");
+
+    (mockMessageModel.insertMany as jest.Mock).mockRejectedValue(error);
+
+    const repository = new MessageRepository(mockMessageModel);
+
+    await expect(repository.create(defaultSaveMessagesArgs)).rejects.toThrow(error);
+  });
+
+  test("should find messages properly", async () => {
+    const repository = new MessageRepository(mockMessageModel);
+
+    const result = await repository.find({});
+
+    expect(result).toStrictEqual(defaultMessages);
+  });
+
+  test("should throw an error if find is failed", async () => {
+    const error = new Error("error");
+
+    (mockMessageModel.find as jest.Mock).mockReturnValue({
+      limit: jest.fn().mockReturnValue({
+        exec: jest.fn().mockRejectedValue(error),
+      }),
+    });
+
+    const repository = new MessageRepository(mockMessageModel);
+
+    await expect(repository.find({})).rejects.toThrow(error);
+  });
+});
diff --git a/apps/relayer/ts/message/__tests__/message.service.test.ts b/apps/relayer/ts/message/__tests__/message.service.test.ts
index 114b8d326..6a4ecd74e 100644
--- a/apps/relayer/ts/message/__tests__/message.service.test.ts
+++ b/apps/relayer/ts/message/__tests__/message.service.test.ts
@@ -1,21 +1,64 @@
+import type { MessageBatchService } from "../../messageBatch/messageBatch.service";
+import type { MessageRepository } from "../message.repository";
+
 import { MessageService } from "../message.service";
 
-import { defaultSaveMessagesArgs } from "./utils";
+import { defaultMessages, defaultSaveMessagesArgs } from "./utils";
 
 describe("MessageService", () => {
+  const mockMessageBatchService = {
+    saveMessageBatches: jest.fn().mockImplementation((args) => Promise.resolve(args)),
+  } as unknown as MessageBatchService;
+
+  const mockRepository = {
+    create: jest.fn().mockResolvedValue(defaultMessages),
+    find: jest.fn().mockResolvedValue(defaultMessages),
+  } as unknown as MessageRepository;
+
+  beforeEach(() => {
+    mockMessageBatchService.saveMessageBatches = jest.fn().mockImplementation((args) => Promise.resolve(args));
+
+    mockRepository.create = jest.fn().mockResolvedValue(defaultMessages);
+    mockRepository.find = jest.fn().mockResolvedValue(defaultMessages);
+  });
+
+  afterEach(() => {
+    jest.clearAllMocks();
+  });
+
   test("should save messages properly", async () => {
-    const service = new MessageService();
+    const service = new MessageService(mockMessageBatchService, mockRepository);
 
     const result = await service.saveMessages(defaultSaveMessagesArgs);
 
-    expect(result).toBe(true);
+    expect(result).toStrictEqual(defaultMessages);
+  });
+
+  test("should throw an error if can't save messages", async () => {
+    const error = new Error("error");
+
+    (mockRepository.create as jest.Mock).mockRejectedValue(error);
+
+    const service = new MessageService(mockMessageBatchService, mockRepository);
+
+    await expect(service.saveMessages(defaultSaveMessagesArgs)).rejects.toThrow(error);
   });
 
   test("should publish messages properly", async () => {
-    const service = new MessageService();
+    const service = new MessageService(mockMessageBatchService, mockRepository);
 
-    const result = await service.publishMessages(defaultSaveMessagesArgs);
+    const result = await service.publishMessages();
 
     expect(result).toStrictEqual({ hash: "", ipfsHash: "" });
   });
+
+  test("should throw an error if can't save message batch", async () => {
+    const error = new Error("error");
+
+    (mockMessageBatchService.saveMessageBatches as jest.Mock).mockRejectedValue(error);
+
+    const service = new MessageService(mockMessageBatchService, mockRepository);
+
+    await expect(service.publishMessages()).rejects.toThrow(error);
+  });
 });
diff --git a/apps/relayer/ts/message/__tests__/utils.ts b/apps/relayer/ts/message/__tests__/utils.ts
index 3852210ea..2aeac0278 100644
--- a/apps/relayer/ts/message/__tests__/utils.ts
+++ b/apps/relayer/ts/message/__tests__/utils.ts
@@ -1,15 +1,19 @@
 import { ZeroAddress } from "ethers";
 import { Keypair } from "maci-domainobjs";
 
+import { defaultMessageBatches } from "../../messageBatch/__tests__/utils";
+import { PublishMessagesDto } from "../dto";
+
 const keypair = new Keypair();
 
-export const defaultSaveMessagesArgs = {
-  maciContractAddress: ZeroAddress,
-  poll: 0,
-  messages: [
-    {
-      data: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
-      publicKey: keypair.pubKey.serialize(),
-    },
-  ],
-};
+export const defaultMessages = defaultMessageBatches[0].messages;
+
+export const defaultSaveMessagesArgs = new PublishMessagesDto();
+defaultSaveMessagesArgs.maciContractAddress = ZeroAddress;
+defaultSaveMessagesArgs.poll = 0;
+defaultSaveMessagesArgs.messages = [
+  {
+    data: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
+    publicKey: keypair.pubKey.serialize(),
+  },
+];
diff --git a/apps/relayer/ts/message/message.controller.ts b/apps/relayer/ts/message/message.controller.ts
index 1f2da30dc..5bf1a8a51 100644
--- a/apps/relayer/ts/message/message.controller.ts
+++ b/apps/relayer/ts/message/message.controller.ts
@@ -3,6 +3,7 @@ import { Body, Controller, HttpException, HttpStatus, Logger, Post } from "@nest
 import { ApiBearerAuth, ApiBody, ApiResponse, ApiTags } from "@nestjs/swagger";
 
 import { PublishMessagesDto } from "./dto";
+import { Message } from "./message.schema";
 import { MessageService } from "./message.service";
 
 @ApiTags("v1/messages")
@@ -17,6 +18,7 @@ export class MessageController {
   /**
    * Initialize MessageController
    *
+   * @param messageService message service
    */
   constructor(private readonly messageService: MessageService) {}
 
@@ -24,7 +26,7 @@ export class MessageController {
    * Publish user messages api method.
    * Saves messages batch and then send them onchain by calling `publishMessages` method via cron job.
    *
-   * @param args - publish messages dto
+   * @param args publish messages dto
    * @returns success or not
    */
   @ApiBody({ type: PublishMessagesDto })
@@ -32,7 +34,7 @@ export class MessageController {
   @ApiResponse({ status: HttpStatus.FORBIDDEN, description: "Forbidden" })
   @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: "BadRequest" })
   @Post("publish")
-  async publish(@Body() args: PublishMessagesDto): Promise<boolean> {
+  async publish(@Body() args: PublishMessagesDto): Promise<Message[]> {
     return this.messageService.saveMessages(args).catch((error: Error) => {
       this.logger.error(`Error:`, error);
       throw new HttpException(error.message, HttpStatus.BAD_REQUEST);
diff --git a/apps/relayer/ts/message/message.module.ts b/apps/relayer/ts/message/message.module.ts
index d24abda23..a8cbbf8f4 100644
--- a/apps/relayer/ts/message/message.module.ts
+++ b/apps/relayer/ts/message/message.module.ts
@@ -1,10 +1,16 @@
 import { Module } from "@nestjs/common";
+import { MongooseModule } from "@nestjs/mongoose";
+
+import { MessageBatchModule } from "../messageBatch/messageBatch.module";
 
 import { MessageController } from "./message.controller";
+import { MessageRepository } from "./message.repository";
+import { Message, MessageSchema } from "./message.schema";
 import { MessageService } from "./message.service";
 
 @Module({
+  imports: [MongooseModule.forFeature([{ name: Message.name, schema: MessageSchema }]), MessageBatchModule],
   controllers: [MessageController],
-  providers: [MessageService],
+  providers: [MessageService, MessageRepository],
 })
 export class MessageModule {}
diff --git a/apps/relayer/ts/message/message.repository.ts b/apps/relayer/ts/message/message.repository.ts
new file mode 100644
index 000000000..5b65ac7e2
--- /dev/null
+++ b/apps/relayer/ts/message/message.repository.ts
@@ -0,0 +1,60 @@
+import { Injectable, Logger } from "@nestjs/common";
+import { InjectModel } from "@nestjs/mongoose";
+import { Model, RootFilterQuery } from "mongoose";
+
+import { PublishMessagesDto } from "./dto";
+import { Message, MESSAGES_LIMIT } from "./message.schema";
+
+/**
+ * Message repository is used to interact with the message collection
+ */
+@Injectable()
+export class MessageRepository {
+  /**
+   * Logger
+   */
+  private readonly logger: Logger = new Logger(MessageRepository.name);
+
+  /**
+   * Initializes the message repository
+   *
+   * @param MessageModel message model
+   */
+  constructor(@InjectModel(Message.name) private MessageModel: Model<Message>) {}
+
+  /**
+   * Create messages from dto
+   *
+   * @param dto publish messages dto
+   * @returns inserted messages
+   */
+  async create(dto: PublishMessagesDto): Promise<Message[]> {
+    const messages = dto.messages.map(({ data, publicKey }) => ({
+      data,
+      publicKey,
+      maciContractAddress: dto.maciContractAddress,
+      poll: dto.poll,
+    }));
+
+    return this.MessageModel.insertMany(messages).catch((error) => {
+      this.logger.error(`Create messages error:`, error);
+      throw error;
+    });
+  }
+
+  /**
+   * Find messages with filter query
+   *
+   * @param filter filter query
+   * @returns messages
+   */
+  async find(filter: RootFilterQuery<Message>, limit = MESSAGES_LIMIT): Promise<Message[]> {
+    return this.MessageModel.find(filter)
+      .limit(limit)
+      .exec()
+      .catch((error) => {
+        this.logger.error(`Find messages error:`, error);
+        throw error;
+      });
+  }
+}
diff --git a/apps/relayer/ts/message/message.schema.ts b/apps/relayer/ts/message/message.schema.ts
new file mode 100644
index 000000000..958163c5a
--- /dev/null
+++ b/apps/relayer/ts/message/message.schema.ts
@@ -0,0 +1,55 @@
+import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
+import mongoose, { HydratedDocument } from "mongoose";
+
+import type { MessageBatch } from "../messageBatch/messageBatch.schema";
+
+/**
+ * Message document type
+ */
+export type MessageDocument = HydratedDocument<Message>;
+
+/**
+ * Messages limit
+ */
+export const MESSAGES_LIMIT = 100;
+
+/**
+ * Message model
+ */
+@Schema()
+export class Message {
+  /**
+   * Public key
+   */
+  @Prop({ required: true })
+  publicKey!: string;
+
+  /**
+   * Message data
+   */
+  @Prop({ required: true })
+  data!: string[];
+
+  /**
+   * MACI contract address
+   */
+  @Prop({ required: true })
+  maciContractAddress!: string;
+
+  /**
+   * Poll ID
+   */
+  @Prop({ required: true })
+  poll!: number;
+
+  /**
+   * Message batch
+   */
+  @Prop({ type: mongoose.Schema.Types.ObjectId, required: false })
+  messageBatch?: MessageBatch;
+}
+
+/**
+ * Message schema
+ */
+export const MessageSchema = SchemaFactory.createForClass(Message);
diff --git a/apps/relayer/ts/message/message.service.ts b/apps/relayer/ts/message/message.service.ts
index c554f6a62..a786a5cd7 100644
--- a/apps/relayer/ts/message/message.service.ts
+++ b/apps/relayer/ts/message/message.service.ts
@@ -3,8 +3,13 @@ import { Injectable, Logger } from "@nestjs/common";
 import type { PublishMessagesDto } from "./dto";
 import type { IPublishMessagesReturn } from "./types";
 
+import { MessageBatchService } from "../messageBatch/messageBatch.service";
+
+import { MessageRepository } from "./message.repository";
+import { Message } from "./message.schema";
+
 /**
- * MessageService is responsible for saving message batches and send them onchain
+ * MessageService is responsible for saving messages and send them onchain
  */
 @Injectable()
 export class MessageService {
@@ -14,24 +19,43 @@ export class MessageService {
   private readonly logger: Logger = new Logger(MessageService.name);
 
   /**
-   * Save messages batch
+   * Initialize MessageService
+   *
+   * @param messageBatchService message batch service
+   * @param messageRepository message repository
+   */
+  constructor(
+    private readonly messageBatchService: MessageBatchService,
+    private readonly messageRepository: MessageRepository,
+  ) {}
+
+  /**
+   * Save messages
    *
-   * @param args - publish messages dto
+   * @param args publish messages dto
    * @returns success or not
    */
-  async saveMessages(args: PublishMessagesDto): Promise<boolean> {
-    this.logger.log("Save messages", args);
-    return Promise.resolve(true);
+  async saveMessages(args: PublishMessagesDto): Promise<Message[]> {
+    return this.messageRepository.create(args).catch((error) => {
+      this.logger.error(`Save messages error:`, error);
+      throw error;
+    });
   }
 
   /**
    * Publish messages onchain
    *
-   * @param args - publish messages dto
+   * @param args publish messages dto
    * @returns transaction and ipfs hashes
    */
-  async publishMessages(args: PublishMessagesDto): Promise<IPublishMessagesReturn> {
-    this.logger.log("Publish messages", args);
+  async publishMessages(): Promise<IPublishMessagesReturn> {
+    const messages = await this.messageRepository.find({ messageBatch: { $exists: false } });
+
+    await this.messageBatchService.saveMessageBatches([{ messages, ipfsHash: "" }]).catch((error) => {
+      this.logger.error(`Save message batch error:`, error);
+      throw error;
+    });
+
     return Promise.resolve({ hash: "", ipfsHash: "" });
   }
 }
diff --git a/apps/relayer/ts/message/validation.ts b/apps/relayer/ts/message/validation.ts
index 598910ddf..d14ee8b8f 100644
--- a/apps/relayer/ts/message/validation.ts
+++ b/apps/relayer/ts/message/validation.ts
@@ -9,7 +9,7 @@ export class PublicKeyValidator implements ValidatorConstraintInterface {
   /**
    * Try to deserialize public key from text and return status of validation
    *
-   * @param text - text to validate
+   * @param text text to validate
    * @returns status of validation
    */
   validate(text: string): boolean {
diff --git a/apps/relayer/ts/messageBatch/__tests__/messageBatch.repository.test.ts b/apps/relayer/ts/messageBatch/__tests__/messageBatch.repository.test.ts
new file mode 100644
index 000000000..36be1f3ef
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/__tests__/messageBatch.repository.test.ts
@@ -0,0 +1,66 @@
+import { Model } from "mongoose";
+
+import { MessageBatchRepository } from "../messageBatch.repository";
+import { MessageBatch } from "../messageBatch.schema";
+
+import { defaultMessageBatches } from "./utils";
+
+describe("MessageBatchRepository", () => {
+  const mockMessageBatchModel = {
+    find: jest.fn().mockReturnValue({
+      limit: jest.fn().mockReturnValue({ exec: jest.fn().mockResolvedValue([defaultMessageBatches]) }),
+    }),
+    insertMany: jest.fn().mockResolvedValue(defaultMessageBatches),
+  } as unknown as Model<MessageBatch>;
+
+  beforeEach(() => {
+    mockMessageBatchModel.find = jest.fn().mockReturnValue({
+      limit: jest.fn().mockReturnValue({ exec: jest.fn().mockResolvedValue([defaultMessageBatches]) }),
+    });
+    mockMessageBatchModel.insertMany = jest.fn().mockResolvedValue(defaultMessageBatches);
+  });
+
+  afterEach(() => {
+    jest.clearAllMocks();
+  });
+
+  test("should create message batch properly", async () => {
+    const repository = new MessageBatchRepository(mockMessageBatchModel);
+
+    const result = await repository.create(defaultMessageBatches);
+
+    expect(result).toStrictEqual(defaultMessageBatches);
+  });
+
+  test("should throw an error if creation is failed", async () => {
+    const error = new Error("error");
+
+    (mockMessageBatchModel.insertMany as jest.Mock).mockRejectedValue(error);
+
+    const repository = new MessageBatchRepository(mockMessageBatchModel);
+
+    await expect(repository.create(defaultMessageBatches)).rejects.toThrow(error);
+  });
+
+  test("should find message batches properly", async () => {
+    const repository = new MessageBatchRepository(mockMessageBatchModel);
+
+    const result = await repository.find({});
+
+    expect(result).toStrictEqual([defaultMessageBatches]);
+  });
+
+  test("should throw an error if find is failed", async () => {
+    const error = new Error("error");
+
+    (mockMessageBatchModel.find as jest.Mock).mockReturnValue({
+      limit: jest.fn().mockReturnValue({
+        exec: jest.fn().mockRejectedValue(error),
+      }),
+    });
+
+    const repository = new MessageBatchRepository(mockMessageBatchModel);
+
+    await expect(repository.find({})).rejects.toThrow(error);
+  });
+});
diff --git a/apps/relayer/ts/messageBatch/__tests__/messageBatch.service.test.ts b/apps/relayer/ts/messageBatch/__tests__/messageBatch.service.test.ts
new file mode 100644
index 000000000..9743e592b
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/__tests__/messageBatch.service.test.ts
@@ -0,0 +1,52 @@
+import { MessageBatchDto } from "../dto";
+import { MessageBatchRepository } from "../messageBatch.repository";
+import { MessageBatchService } from "../messageBatch.service";
+
+import { defaultMessageBatches } from "./utils";
+
+describe("MessageBatchService", () => {
+  const mockRepository = {
+    create: jest.fn().mockResolvedValue(defaultMessageBatches),
+  } as unknown as MessageBatchRepository;
+
+  beforeEach(() => {
+    mockRepository.create = jest.fn().mockResolvedValue(defaultMessageBatches);
+  });
+
+  afterEach(() => {
+    jest.clearAllMocks();
+  });
+
+  test("should save message batches properly", async () => {
+    const service = new MessageBatchService(mockRepository);
+
+    const result = await service.saveMessageBatches(defaultMessageBatches);
+
+    expect(result).toStrictEqual(defaultMessageBatches);
+  });
+
+  test("should throw an error if can't save message batches", async () => {
+    const error = new Error("error");
+
+    (mockRepository.create as jest.Mock).mockRejectedValue(error);
+
+    const service = new MessageBatchService(mockRepository);
+
+    await expect(service.saveMessageBatches(defaultMessageBatches)).rejects.toThrow(error);
+  });
+
+  test("should throw an error if validation is failed", async () => {
+    const service = new MessageBatchService(mockRepository);
+
+    const invalidEmptyMessagesArgs = new MessageBatchDto();
+    invalidEmptyMessagesArgs.messages = [];
+    invalidEmptyMessagesArgs.ipfsHash = "invalid";
+
+    const invalidMessageArgs = new MessageBatchDto();
+    invalidMessageArgs.messages = [];
+    invalidMessageArgs.ipfsHash = "invalid";
+
+    await expect(service.saveMessageBatches([invalidEmptyMessagesArgs])).rejects.toThrow("Validation error");
+    await expect(service.saveMessageBatches([invalidMessageArgs])).rejects.toThrow("Validation error");
+  });
+});
diff --git a/apps/relayer/ts/messageBatch/__tests__/utils.ts b/apps/relayer/ts/messageBatch/__tests__/utils.ts
new file mode 100644
index 000000000..c717c8b12
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/__tests__/utils.ts
@@ -0,0 +1,21 @@
+import { ZeroAddress } from "ethers";
+import { Keypair } from "maci-domainobjs";
+
+import { MessageBatchDto } from "../dto";
+
+const keypair = new Keypair();
+
+export const defaultIpfsHash = "QmXj8v1qbwTqVp9RxkQR29Xjc6g5C1KL2m2gZ9b8t8THHj";
+
+const defaultMessageBatch = new MessageBatchDto();
+defaultMessageBatch.messages = [
+  {
+    publicKey: keypair.pubKey.serialize(),
+    data: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
+    maciContractAddress: ZeroAddress,
+    poll: 0,
+  },
+];
+defaultMessageBatch.ipfsHash = defaultIpfsHash;
+
+export const defaultMessageBatches: MessageBatchDto[] = [defaultMessageBatch];
diff --git a/apps/relayer/ts/messageBatch/__tests__/validation.test.ts b/apps/relayer/ts/messageBatch/__tests__/validation.test.ts
new file mode 100644
index 000000000..15d306012
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/__tests__/validation.test.ts
@@ -0,0 +1,29 @@
+import { IpfsHashValidator } from "../validation";
+
+import { defaultIpfsHash } from "./utils";
+
+describe("IpfsHashValidator", () => {
+  test("should validate valid ipfs hash", () => {
+    const validator = new IpfsHashValidator();
+
+    const result = validator.validate(defaultIpfsHash);
+
+    expect(result).toBe(true);
+  });
+
+  test("should validate invalid ipfs hash", () => {
+    const validator = new IpfsHashValidator();
+
+    const result = validator.validate("invalid");
+
+    expect(result).toBe(false);
+  });
+
+  test("should return default message properly", () => {
+    const validator = new IpfsHashValidator();
+
+    const result = validator.defaultMessage();
+
+    expect(result).toBe("IPFS hash ($value) is invalid");
+  });
+});
diff --git a/apps/relayer/ts/messageBatch/dto.ts b/apps/relayer/ts/messageBatch/dto.ts
new file mode 100644
index 000000000..4ad3fdcd7
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/dto.ts
@@ -0,0 +1,39 @@
+import { ApiProperty } from "@nestjs/swagger";
+import { IsArray, ArrayMinSize, ArrayMaxSize, ValidateNested, Validate } from "class-validator";
+
+import { Message } from "../message/message.schema";
+
+import { IpfsHashValidator } from "./validation";
+
+/**
+ * Max messages per batch
+ */
+const MAX_MESSAGES = 100;
+
+/**
+ * Data transfer object for message batch
+ */
+export class MessageBatchDto {
+  /**
+   * Messages
+   */
+  @ApiProperty({
+    description: "Messages",
+    type: [String],
+  })
+  @IsArray()
+  @ArrayMinSize(1)
+  @ArrayMaxSize(MAX_MESSAGES)
+  @ValidateNested({ each: true })
+  messages!: Message[];
+
+  /**
+   * IPFS hash
+   */
+  @ApiProperty({
+    description: "IPFS hash",
+    type: String,
+  })
+  @Validate(IpfsHashValidator)
+  ipfsHash!: string;
+}
diff --git a/apps/relayer/ts/messageBatch/messageBatch.module.ts b/apps/relayer/ts/messageBatch/messageBatch.module.ts
new file mode 100644
index 000000000..ec029fc1a
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/messageBatch.module.ts
@@ -0,0 +1,13 @@
+import { Module } from "@nestjs/common";
+import { MongooseModule } from "@nestjs/mongoose";
+
+import { MessageBatchRepository } from "./messageBatch.repository";
+import { MessageBatch, MessageBatchSchema } from "./messageBatch.schema";
+import { MessageBatchService } from "./messageBatch.service";
+
+@Module({
+  imports: [MongooseModule.forFeature([{ name: MessageBatch.name, schema: MessageBatchSchema }])],
+  exports: [MessageBatchService],
+  providers: [MessageBatchService, MessageBatchRepository],
+})
+export class MessageBatchModule {}
diff --git a/apps/relayer/ts/messageBatch/messageBatch.repository.ts b/apps/relayer/ts/messageBatch/messageBatch.repository.ts
new file mode 100644
index 000000000..bac2e6efc
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/messageBatch.repository.ts
@@ -0,0 +1,54 @@
+import { Injectable, Logger } from "@nestjs/common";
+import { InjectModel } from "@nestjs/mongoose";
+import { Model, RootFilterQuery } from "mongoose";
+
+import { MessageBatchDto } from "./dto";
+import { MESSAGE_BATCHES_LIMIT, MessageBatch } from "./messageBatch.schema";
+
+/**
+ * Message batch repository is used to interact with the message batch collection
+ */
+@Injectable()
+export class MessageBatchRepository {
+  /**
+   * Logger
+   */
+  private readonly logger: Logger = new Logger(MessageBatchRepository.name);
+
+  /**
+   * Initializes the message batch repository
+   *
+   * @param MessageBatchModel message batch model
+   */
+  constructor(@InjectModel(MessageBatch.name) private MessageBatchModel: Model<MessageBatch>) {}
+
+  /**
+   * Create message batch from messages and ipfs hash
+   *
+   * @param messages messages
+   * @param ipfsHash ipfs hash
+   * @returns inserted message batch
+   */
+  async create(dto: MessageBatchDto[]): Promise<MessageBatch[]> {
+    return this.MessageBatchModel.insertMany(dto).catch((error) => {
+      this.logger.error(`Create message batch error:`, error);
+      throw error;
+    });
+  }
+
+  /**
+   * Find message batches with filter query
+   *
+   * @param filter filter query
+   * @returns message batches
+   */
+  async find(filter: RootFilterQuery<MessageBatch>, limit = MESSAGE_BATCHES_LIMIT): Promise<MessageBatch[]> {
+    return this.MessageBatchModel.find(filter)
+      .limit(limit)
+      .exec()
+      .catch((error) => {
+        this.logger.error(`Find message batches error:`, error);
+        throw error;
+      });
+  }
+}
diff --git a/apps/relayer/ts/messageBatch/messageBatch.schema.ts b/apps/relayer/ts/messageBatch/messageBatch.schema.ts
new file mode 100644
index 000000000..986a696d3
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/messageBatch.schema.ts
@@ -0,0 +1,37 @@
+import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
+import mongoose, { HydratedDocument } from "mongoose";
+
+import type { Message } from "../message/message.schema";
+
+/**
+ * Message batch document type
+ */
+export type MessageBatchDocument = HydratedDocument<MessageBatch>;
+
+/**
+ * Message batches limit
+ */
+export const MESSAGE_BATCHES_LIMIT = 1;
+
+/**
+ * Message batch model
+ */
+@Schema()
+export class MessageBatch {
+  /**
+   * Messages
+   */
+  @Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: "Message" }], required: true })
+  messages!: Message[];
+
+  /**
+   * IPFS hash
+   */
+  @Prop({ required: true })
+  ipfsHash!: string;
+}
+
+/**
+ * Message batch schema
+ */
+export const MessageBatchSchema = SchemaFactory.createForClass(MessageBatch);
diff --git a/apps/relayer/ts/messageBatch/messageBatch.service.ts b/apps/relayer/ts/messageBatch/messageBatch.service.ts
new file mode 100644
index 000000000..11c5e9204
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/messageBatch.service.ts
@@ -0,0 +1,51 @@
+import { Injectable, Logger } from "@nestjs/common";
+import { validate } from "class-validator";
+
+import type { MessageBatchDto } from "./dto";
+
+import { MessageBatchRepository } from "./messageBatch.repository";
+import { MessageBatch } from "./messageBatch.schema";
+
+/**
+ * MessageBatchService is responsible for saving message batches and send them to ipfs
+ */
+@Injectable()
+export class MessageBatchService {
+  /**
+   * Logger
+   */
+  private readonly logger: Logger = new Logger(MessageBatchService.name);
+
+  /**
+   * Initialize MessageBatchService
+   *
+   * @param messageBatchRepository message batch repository
+   */
+  constructor(private readonly messageBatchRepository: MessageBatchRepository) {}
+
+  /**
+   * Save messages batch
+   *
+   * @param args publish messages dto
+   * @returns success or not
+   */
+  async saveMessageBatches(args: MessageBatchDto[]): Promise<MessageBatch[]> {
+    const validationErrors = await Promise.all(args.map((values) => validate(values))).then((result) =>
+      result.reduce((acc, errors) => {
+        acc.push(...errors);
+        return acc;
+      }, []),
+    );
+
+    if (validationErrors.length > 0) {
+      this.logger.error(`Validation error:`, validationErrors);
+
+      throw new Error("Validation error");
+    }
+
+    return this.messageBatchRepository.create(args).catch((error) => {
+      this.logger.error(`Save message batch error:`, error);
+      throw error;
+    });
+  }
+}
diff --git a/apps/relayer/ts/messageBatch/validation.ts b/apps/relayer/ts/messageBatch/validation.ts
new file mode 100644
index 000000000..08391abbb
--- /dev/null
+++ b/apps/relayer/ts/messageBatch/validation.ts
@@ -0,0 +1,31 @@
+import { ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";
+
+/**
+ * Check if the string is a valid base58 encoded CIDv1 hash
+ */
+const IPFS_REGEX = /^Qm[a-zA-Z0-9]{44}$/;
+
+/**
+ * Validate public key
+ */
+@ValidatorConstraint({ name: "ipfsHash", async: false })
+export class IpfsHashValidator implements ValidatorConstraintInterface {
+  /**
+   * Validate ipfs hash
+   *
+   * @param text text to validate
+   * @returns status of validation
+   */
+  validate(text: string): boolean {
+    return IPFS_REGEX.test(text);
+  }
+
+  /**
+   * Return default validation message
+   *
+   * @returns default validation message
+   */
+  defaultMessage(): string {
+    return "IPFS hash ($value) is invalid";
+  }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fe81febef..d4b9d6405 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -104,6 +104,9 @@ importers:
       '@nestjs/core':
         specifier: ^10.4.7
         version: 10.4.15(@nestjs/common@10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      '@nestjs/mongoose':
+        specifier: ^10.1.0
+        version: 10.1.0(@nestjs/common@10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.9.3(socks@2.8.3))(rxjs@7.8.1)
       '@nestjs/platform-express':
         specifier: ^10.4.7
         version: 10.4.15(@nestjs/common@10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)
@@ -152,6 +155,9 @@ importers:
       maci-domainobjs:
         specifier: workspace:^2.5.0
         version: link:../../packages/domainobjs
+      mongoose:
+        specifier: ^8.9.3
+        version: 8.9.3(socks@2.8.3)
       mustache:
         specifier: ^4.2.0
         version: 4.2.0
@@ -192,6 +198,9 @@ importers:
       jest:
         specifier: ^29.5.0
         version: 29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.7.2))
+      mongodb-memory-server:
+        specifier: ^10.1.3
+        version: 10.1.3(socks@2.8.3)
       supertest:
         specifier: ^7.0.0
         version: 7.0.0
@@ -2106,6 +2115,9 @@ packages:
   '@microsoft/tsdoc@0.15.1':
     resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==}
 
+  '@mongodb-js/saslprep@1.1.9':
+    resolution: {integrity: sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==}
+
   '@napi-rs/wasm-runtime@0.2.4':
     resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==}
 
@@ -2165,6 +2177,14 @@ packages:
       class-validator:
         optional: true
 
+  '@nestjs/mongoose@10.1.0':
+    resolution: {integrity: sha512-1ExAnZUfh2QffEaGjqYGgVPy/sYBQCVLCLqVgkcClKx/BCd0QNgND8MB70lwyobp3nm/+nbGQqBpu9F3/hgOCw==}
+    peerDependencies:
+      '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0
+      '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0
+      mongoose: ^6.0.2 || ^7.0.0 || ^8.0.0
+      rxjs: ^7.0.0
+
   '@nestjs/platform-express@10.4.15':
     resolution: {integrity: sha512-63ZZPkXHjoDyO7ahGOVcybZCRa7/Scp6mObQKjcX/fTEq1YJeU75ELvMsuQgc8U2opMGOBD7GVuc4DV0oeDHoA==}
     peerDependencies:
@@ -3375,6 +3395,12 @@ packages:
   '@types/validator@13.12.2':
     resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==}
 
+  '@types/webidl-conversions@7.0.3':
+    resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==}
+
+  '@types/whatwg-url@11.0.5':
+    resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==}
+
   '@types/ws@7.4.7':
     resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==}
 
@@ -3657,6 +3683,10 @@ packages:
     resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
     engines: {node: '>= 14'}
 
+  agent-base@7.1.3:
+    resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
+    engines: {node: '>= 14'}
+
   aggregate-error@3.1.0:
     resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
     engines: {node: '>=8'}
@@ -3912,6 +3942,9 @@ packages:
     resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
     hasBin: true
 
+  async-mutex@0.5.0:
+    resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==}
+
   async@1.5.2:
     resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==}
 
@@ -4015,6 +4048,9 @@ packages:
   balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
 
+  bare-events@2.5.3:
+    resolution: {integrity: sha512-pCO3aoRJ0MBiRMu8B7vUga0qL3L7gO1+SW7ku6qlSsMLwuhaawnuvZDyzJY/kyC63Un0XAB0OPUcfF1eTO/V+Q==}
+
   base-x@3.0.9:
     resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==}
 
@@ -4173,12 +4209,19 @@ packages:
   bser@2.1.1:
     resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
 
+  bson@6.10.1:
+    resolution: {integrity: sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==}
+    engines: {node: '>=16.20.1'}
+
   buffer-alloc-unsafe@1.1.0:
     resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==}
 
   buffer-alloc@1.2.0:
     resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==}
 
+  buffer-crc32@0.2.13:
+    resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+
   buffer-fill@1.0.0:
     resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==}
 
@@ -6036,6 +6079,15 @@ packages:
       debug:
         optional: true
 
+  follow-redirects@1.15.9:
+    resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+
   for-each@0.3.3:
     resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
 
@@ -6685,6 +6737,10 @@ packages:
     resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
     engines: {node: '>= 14'}
 
+  https-proxy-agent@7.0.6:
+    resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+    engines: {node: '>= 14'}
+
   human-signals@2.1.0:
     resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
     engines: {node: '>=10.17.0'}
@@ -7522,6 +7578,10 @@ packages:
   just-diff@6.0.2:
     resolution: {integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==}
 
+  kareem@2.6.3:
+    resolution: {integrity: sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==}
+    engines: {node: '>=12.0.0'}
+
   katex@0.16.10:
     resolution: {integrity: sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==}
     hasBin: true
@@ -7952,6 +8012,9 @@ packages:
     resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==}
     engines: {node: '>= 4.0.0'}
 
+  memory-pager@1.5.0:
+    resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
+
   memorystream@0.3.1:
     resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
     engines: {node: '>= 0.10.0'}
@@ -8293,6 +8356,56 @@ packages:
     resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==}
     engines: {node: '>=0.10.0'}
 
+  mongodb-connection-string-url@3.0.1:
+    resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==}
+
+  mongodb-memory-server-core@10.1.3:
+    resolution: {integrity: sha512-ayBQHeV74wRHhgcAKpxHYI4th9Ufidy/m3XhJnLFRufKsOyDsyHYU3Zxv5Fm4hxsWE6wVd0GAVcQ7t7XNkivOg==}
+    engines: {node: '>=16.20.1'}
+
+  mongodb-memory-server@10.1.3:
+    resolution: {integrity: sha512-QCUjsIIXSYv/EgkpDAjfhlqRKo6N+qR6DD43q4lyrCVn24xQmvlArdWHW/Um5RS4LkC9YWC3XveSncJqht2Hbg==}
+    engines: {node: '>=16.20.1'}
+
+  mongodb@6.12.0:
+    resolution: {integrity: sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==}
+    engines: {node: '>=16.20.1'}
+    peerDependencies:
+      '@aws-sdk/credential-providers': ^3.188.0
+      '@mongodb-js/zstd': ^1.1.0 || ^2.0.0
+      gcp-metadata: ^5.2.0
+      kerberos: ^2.0.1
+      mongodb-client-encryption: '>=6.0.0 <7'
+      snappy: ^7.2.2
+      socks: ^2.7.1
+    peerDependenciesMeta:
+      '@aws-sdk/credential-providers':
+        optional: true
+      '@mongodb-js/zstd':
+        optional: true
+      gcp-metadata:
+        optional: true
+      kerberos:
+        optional: true
+      mongodb-client-encryption:
+        optional: true
+      snappy:
+        optional: true
+      socks:
+        optional: true
+
+  mongoose@8.9.3:
+    resolution: {integrity: sha512-G50GNPdMqhoiRAJ/24GYAzg13yxXDD3FOOFeYiFwtHmHpAJem3hxbYIxAhLJGWbYEiUZL0qFMu2LXYkgGAmo+Q==}
+    engines: {node: '>=16.20.1'}
+
+  mpath@0.9.0:
+    resolution: {integrity: sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==}
+    engines: {node: '>=4.0.0'}
+
+  mquery@5.0.0:
+    resolution: {integrity: sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==}
+    engines: {node: '>=14.0.0'}
+
   mrmime@2.0.0:
     resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==}
     engines: {node: '>=10'}
@@ -8376,6 +8489,10 @@ packages:
   neo-async@2.6.2:
     resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
 
+  new-find-package-json@2.0.0:
+    resolution: {integrity: sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==}
+    engines: {node: '>=12.22.0'}
+
   no-case@3.0.4:
     resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
 
@@ -8891,6 +9008,9 @@ packages:
     resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==}
     engines: {node: '>=0.12'}
 
+  pend@1.2.0:
+    resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
+
   periscopic@3.1.0:
     resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
 
@@ -9363,6 +9483,9 @@ packages:
   queue-microtask@1.2.3:
     resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
 
+  queue-tick@1.0.1:
+    resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
+
   queue@6.0.2:
     resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==}
 
@@ -9974,6 +10097,9 @@ packages:
     resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
     engines: {node: '>= 0.4'}
 
+  sift@17.1.3:
+    resolution: {integrity: sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==}
+
   signal-exit@3.0.7:
     resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
 
@@ -10114,6 +10240,9 @@ packages:
   space-separated-tokens@2.0.2:
     resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
 
+  sparse-bitfield@3.0.3:
+    resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==}
+
   spawn-wrap@2.0.0:
     resolution: {integrity: sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==}
     engines: {node: '>=8'}
@@ -10199,6 +10328,9 @@ packages:
     resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
     engines: {node: '>=10.0.0'}
 
+  streamx@2.21.1:
+    resolution: {integrity: sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==}
+
   string-argv@0.3.2:
     resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
     engines: {node: '>=0.6.19'}
@@ -10418,6 +10550,9 @@ packages:
     resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
     engines: {node: '>=6'}
 
+  tar-stream@3.1.7:
+    resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
+
   tar@6.2.1:
     resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
     engines: {node: '>=10'}
@@ -10455,6 +10590,9 @@ packages:
     resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
     engines: {node: '>=8'}
 
+  text-decoder@1.2.3:
+    resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==}
+
   text-extensions@1.9.0:
     resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==}
     engines: {node: '>=0.10'}
@@ -10530,6 +10668,10 @@ packages:
   tr46@0.0.3:
     resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
 
+  tr46@4.1.1:
+    resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==}
+    engines: {node: '>=14'}
+
   tree-kill@1.2.2:
     resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
     hasBin: true
@@ -11071,6 +11213,10 @@ packages:
   webidl-conversions@3.0.1:
     resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
 
+  webidl-conversions@7.0.0:
+    resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+    engines: {node: '>=12'}
+
   webpack-bundle-analyzer@4.10.2:
     resolution: {integrity: sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==}
     engines: {node: '>= 10.13.0'}
@@ -11149,6 +11295,10 @@ packages:
     resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
     engines: {node: '>=18'}
 
+  whatwg-url@13.0.0:
+    resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==}
+    engines: {node: '>=16'}
+
   whatwg-url@5.0.0:
     resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
 
@@ -11359,6 +11509,10 @@ packages:
     resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
     engines: {node: '>=12'}
 
+  yauzl@3.2.0:
+    resolution: {integrity: sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==}
+    engines: {node: '>=12'}
+
   yn@2.0.0:
     resolution: {integrity: sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==}
     engines: {node: '>=4'}
@@ -12776,12 +12930,12 @@ snapshots:
       cssnano-preset-advanced: 6.1.2(postcss@8.4.38)
       postcss: 8.4.38
       postcss-sort-media-queries: 5.2.0(postcss@8.4.38)
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   '@docusaurus/logger@3.5.2':
     dependencies:
       chalk: 4.1.2
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   '@docusaurus/mdx-loader@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)':
     dependencies:
@@ -12805,7 +12959,7 @@ snapshots:
       remark-frontmatter: 5.0.0
       remark-gfm: 4.0.0
       stringify-object: 3.3.0
-      tslib: 2.7.0
+      tslib: 2.8.1
       unified: 11.0.4
       unist-util-visit: 5.0.0
       url-loader: 4.1.1(file-loader@6.2.0(webpack@5.92.1))(webpack@5.92.1)
@@ -12857,7 +13011,7 @@ snapshots:
       react-dom: 18.3.1(react@18.3.1)
       reading-time: 1.5.0
       srcset: 4.0.0
-      tslib: 2.7.0
+      tslib: 2.8.1
       unist-util-visit: 5.0.0
       utility-types: 3.11.0
       webpack: 5.92.1
@@ -12898,7 +13052,7 @@ snapshots:
       lodash: 4.17.21
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      tslib: 2.7.0
+      tslib: 2.8.1
       utility-types: 3.11.0
       webpack: 5.92.1
     transitivePeerDependencies:
@@ -12938,7 +13092,7 @@ snapshots:
       lodash: 4.17.21
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      tslib: 2.7.0
+      tslib: 2.8.1
       utility-types: 3.11.0
       webpack: 5.92.1
     transitivePeerDependencies:
@@ -12970,7 +13124,7 @@ snapshots:
       fs-extra: 11.2.0
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      tslib: 2.7.0
+      tslib: 2.8.1
       webpack: 5.92.1
     transitivePeerDependencies:
       - '@mdx-js/react'
@@ -13000,7 +13154,7 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
       react-json-view-lite: 1.4.0(react@18.3.1)
-      tslib: 2.7.0
+      tslib: 2.8.1
     transitivePeerDependencies:
       - '@mdx-js/react'
       - '@parcel/css'
@@ -13027,7 +13181,7 @@ snapshots:
       '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.3)
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      tslib: 2.7.0
+      tslib: 2.8.1
     transitivePeerDependencies:
       - '@mdx-js/react'
       - '@parcel/css'
@@ -13055,7 +13209,7 @@ snapshots:
       '@types/gtag.js': 0.0.12
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      tslib: 2.7.0
+      tslib: 2.8.1
     transitivePeerDependencies:
       - '@mdx-js/react'
       - '@parcel/css'
@@ -13082,7 +13236,7 @@ snapshots:
       '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.3)
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      tslib: 2.7.0
+      tslib: 2.8.1
     transitivePeerDependencies:
       - '@mdx-js/react'
       - '@parcel/css'
@@ -13114,7 +13268,7 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
       sitemap: 7.1.2
-      tslib: 2.7.0
+      tslib: 2.8.1
     transitivePeerDependencies:
       - '@mdx-js/react'
       - '@parcel/css'
@@ -13296,7 +13450,7 @@ snapshots:
       lodash: 4.17.21
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      tslib: 2.7.0
+      tslib: 2.8.1
       utility-types: 3.11.0
     transitivePeerDependencies:
       - '@algolia/client-search'
@@ -13324,7 +13478,7 @@ snapshots:
   '@docusaurus/theme-translations@3.5.2':
     dependencies:
       fs-extra: 11.2.0
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   '@docusaurus/tsconfig@3.5.2': {}
 
@@ -13350,7 +13504,7 @@ snapshots:
 
   '@docusaurus/utils-common@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
     dependencies:
-      tslib: 2.7.0
+      tslib: 2.8.1
     optionalDependencies:
       '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
 
@@ -13363,7 +13517,7 @@ snapshots:
       joi: 17.13.1
       js-yaml: 4.1.0
       lodash: 4.17.21
-      tslib: 2.7.0
+      tslib: 2.8.1
     transitivePeerDependencies:
       - '@docusaurus/types'
       - '@swc/core'
@@ -13391,7 +13545,7 @@ snapshots:
       prompts: 2.4.2
       resolve-pathname: 3.0.0
       shelljs: 0.8.5
-      tslib: 2.7.0
+      tslib: 2.8.1
       url-loader: 4.1.1(file-loader@6.2.0(webpack@5.92.1))(webpack@5.92.1)
       utility-types: 3.11.0
       webpack: 5.92.1
@@ -13453,17 +13607,17 @@ snapshots:
   '@emnapi/core@1.2.0':
     dependencies:
       '@emnapi/wasi-threads': 1.0.1
-      tslib: 2.7.0
+      tslib: 2.8.1
     optional: true
 
   '@emnapi/runtime@1.2.0':
     dependencies:
-      tslib: 2.7.0
+      tslib: 2.8.1
     optional: true
 
   '@emnapi/wasi-threads@1.0.1':
     dependencies:
-      tslib: 2.7.0
+      tslib: 2.8.1
     optional: true
 
   '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)':
@@ -14222,6 +14376,10 @@ snapshots:
 
   '@microsoft/tsdoc@0.15.1': {}
 
+  '@mongodb-js/saslprep@1.1.9':
+    dependencies:
+      sparse-bitfield: 3.0.3
+
   '@napi-rs/wasm-runtime@0.2.4':
     dependencies:
       '@emnapi/core': 1.2.0
@@ -14291,6 +14449,13 @@ snapshots:
       class-transformer: 0.5.1
       class-validator: 0.14.1
 
+  '@nestjs/mongoose@10.1.0(@nestjs/common@10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.9.3(socks@2.8.3))(rxjs@7.8.1)':
+    dependencies:
+      '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      '@nestjs/core': 10.4.15(@nestjs/common@10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      mongoose: 8.9.3(socks@2.8.3)
+      rxjs: 7.8.1
+
   '@nestjs/platform-express@10.4.15(@nestjs/common@10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)':
     dependencies:
       '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
@@ -14871,7 +15036,7 @@ snapshots:
   '@nrwl/tao@19.2.0':
     dependencies:
       nx: 19.2.0
-      tslib: 2.7.0
+      tslib: 2.8.1
     transitivePeerDependencies:
       - '@swc-node/register'
       - '@swc/core'
@@ -14895,7 +15060,7 @@ snapshots:
       nx: 19.2.0
       semver: 7.6.3
       tmp: 0.2.3
-      tslib: 2.7.0
+      tslib: 2.8.1
       yargs-parser: 21.1.1
 
   '@nx/nx-darwin-arm64@19.2.0':
@@ -14954,7 +15119,7 @@ snapshots:
       supports-color: 8.1.1
       supports-hyperlinks: 2.3.0
       ts-node: 10.9.2(@types/node@22.10.5)(typescript@5.7.2)
-      tslib: 2.7.0
+      tslib: 2.8.1
       widest-line: 3.1.0
       wordwrap: 1.0.0
       wrap-ansi: 7.0.0
@@ -14991,7 +15156,7 @@ snapshots:
       supports-color: 8.1.1
       supports-hyperlinks: 2.3.0
       ts-node: 10.9.2(@types/node@22.10.5)(typescript@5.7.2)
-      tslib: 2.6.3
+      tslib: 2.8.1
       widest-line: 3.1.0
       wordwrap: 1.0.0
       wrap-ansi: 7.0.0
@@ -15160,18 +15325,18 @@ snapshots:
     dependencies:
       asn1js: 3.0.5
       pvtsutils: 1.3.5
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   '@peculiar/json-schema@1.1.12':
     dependencies:
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   '@peculiar/webcrypto@1.5.0':
     dependencies:
       '@peculiar/asn1-schema': 2.3.8
       '@peculiar/json-schema': 1.1.12
       pvtsutils: 1.3.5
-      tslib: 2.7.0
+      tslib: 2.8.1
       webcrypto-core: 1.8.0
 
   '@pkgjs/parseargs@0.11.0':
@@ -15486,7 +15651,7 @@ snapshots:
 
   '@tybys/wasm-util@0.9.0':
     dependencies:
-      tslib: 2.7.0
+      tslib: 2.8.1
     optional: true
 
   '@typechain/ethers-v6@0.5.1(ethers@6.13.4)(typechain@8.3.2(typescript@5.6.3))(typescript@5.6.3)':
@@ -15856,6 +16021,12 @@ snapshots:
 
   '@types/validator@13.12.2': {}
 
+  '@types/webidl-conversions@7.0.3': {}
+
+  '@types/whatwg-url@11.0.5':
+    dependencies:
+      '@types/webidl-conversions': 7.0.3
+
   '@types/ws@7.4.7':
     dependencies:
       '@types/node': 22.9.0
@@ -16121,7 +16292,7 @@ snapshots:
       busboy: 1.6.0
       fast-querystring: 1.1.2
       fast-url-parser: 1.1.3
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   '@xtuc/ieee754@1.2.0': {}
 
@@ -16132,7 +16303,7 @@ snapshots:
   '@yarnpkg/parsers@3.0.0-rc.46':
     dependencies:
       js-yaml: 3.14.1
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   '@zk-kit/baby-jubjub@1.0.3':
     dependencies:
@@ -16230,6 +16401,8 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  agent-base@7.1.3: {}
+
   aggregate-error@3.1.0:
     dependencies:
       clean-stack: 2.2.0
@@ -16476,7 +16649,7 @@ snapshots:
     dependencies:
       pvtsutils: 1.3.5
       pvutils: 1.1.3
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   assemblyscript@0.19.10:
     dependencies:
@@ -16499,6 +16672,10 @@ snapshots:
 
   astring@1.8.6: {}
 
+  async-mutex@0.5.0:
+    dependencies:
+      tslib: 2.8.1
+
   async@1.5.2: {}
 
   async@2.6.4:
@@ -16641,6 +16818,9 @@ snapshots:
 
   balanced-match@1.0.2: {}
 
+  bare-events@2.5.3:
+    optional: true
+
   base-x@3.0.9:
     dependencies:
       safe-buffer: 5.2.1
@@ -16878,6 +17058,8 @@ snapshots:
     dependencies:
       node-int64: 0.4.0
 
+  bson@6.10.1: {}
+
   buffer-alloc-unsafe@1.1.0: {}
 
   buffer-alloc@1.2.0:
@@ -16885,6 +17067,8 @@ snapshots:
       buffer-alloc-unsafe: 1.1.0
       buffer-fill: 1.0.0
 
+  buffer-crc32@0.2.13: {}
+
   buffer-fill@1.0.0: {}
 
   buffer-from@1.1.2: {}
@@ -16960,7 +17144,7 @@ snapshots:
   camel-case@4.1.2:
     dependencies:
       pascal-case: 3.1.2
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   camelcase-keys@6.2.2:
     dependencies:
@@ -18096,7 +18280,7 @@ snapshots:
   dot-case@3.0.4:
     dependencies:
       no-case: 3.0.4
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   dot-prop@5.3.0:
     dependencies:
@@ -19173,6 +19357,10 @@ snapshots:
     optionalDependencies:
       debug: 4.3.6(supports-color@8.1.1)
 
+  follow-redirects@1.15.9(debug@4.3.7):
+    optionalDependencies:
+      debug: 4.3.7(supports-color@8.1.1)
+
   for-each@0.3.3:
     dependencies:
       is-callable: 1.2.7
@@ -20207,6 +20395,13 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  https-proxy-agent@7.0.6:
+    dependencies:
+      agent-base: 7.1.3
+      debug: 4.3.7(supports-color@8.1.1)
+    transitivePeerDependencies:
+      - supports-color
+
   human-signals@2.1.0: {}
 
   human-signals@5.0.0: {}
@@ -21258,6 +21453,8 @@ snapshots:
 
   just-diff@6.0.2: {}
 
+  kareem@2.6.3: {}
+
   katex@0.16.10:
     dependencies:
       commander: 8.3.0
@@ -21615,7 +21812,7 @@ snapshots:
 
   lower-case@2.0.2:
     dependencies:
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   lowercase-keys@3.0.0: {}
 
@@ -21921,6 +22118,8 @@ snapshots:
     dependencies:
       fs-monkey: 1.0.6
 
+  memory-pager@1.5.0: {}
+
   memorystream@0.3.1: {}
 
   meow@12.1.1: {}
@@ -22429,6 +22628,84 @@ snapshots:
 
   modify-values@1.0.1: {}
 
+  mongodb-connection-string-url@3.0.1:
+    dependencies:
+      '@types/whatwg-url': 11.0.5
+      whatwg-url: 13.0.0
+
+  mongodb-memory-server-core@10.1.3(socks@2.8.3):
+    dependencies:
+      async-mutex: 0.5.0
+      camelcase: 6.3.0
+      debug: 4.3.7(supports-color@8.1.1)
+      find-cache-dir: 3.3.2
+      follow-redirects: 1.15.9(debug@4.3.7)
+      https-proxy-agent: 7.0.6
+      mongodb: 6.12.0(socks@2.8.3)
+      new-find-package-json: 2.0.0
+      semver: 7.6.3
+      tar-stream: 3.1.7
+      tslib: 2.8.1
+      yauzl: 3.2.0
+    transitivePeerDependencies:
+      - '@aws-sdk/credential-providers'
+      - '@mongodb-js/zstd'
+      - gcp-metadata
+      - kerberos
+      - mongodb-client-encryption
+      - snappy
+      - socks
+      - supports-color
+
+  mongodb-memory-server@10.1.3(socks@2.8.3):
+    dependencies:
+      mongodb-memory-server-core: 10.1.3(socks@2.8.3)
+      tslib: 2.8.1
+    transitivePeerDependencies:
+      - '@aws-sdk/credential-providers'
+      - '@mongodb-js/zstd'
+      - gcp-metadata
+      - kerberos
+      - mongodb-client-encryption
+      - snappy
+      - socks
+      - supports-color
+
+  mongodb@6.12.0(socks@2.8.3):
+    dependencies:
+      '@mongodb-js/saslprep': 1.1.9
+      bson: 6.10.1
+      mongodb-connection-string-url: 3.0.1
+    optionalDependencies:
+      socks: 2.8.3
+
+  mongoose@8.9.3(socks@2.8.3):
+    dependencies:
+      bson: 6.10.1
+      kareem: 2.6.3
+      mongodb: 6.12.0(socks@2.8.3)
+      mpath: 0.9.0
+      mquery: 5.0.0
+      ms: 2.1.3
+      sift: 17.1.3
+    transitivePeerDependencies:
+      - '@aws-sdk/credential-providers'
+      - '@mongodb-js/zstd'
+      - gcp-metadata
+      - kerberos
+      - mongodb-client-encryption
+      - snappy
+      - socks
+      - supports-color
+
+  mpath@0.9.0: {}
+
+  mquery@5.0.0:
+    dependencies:
+      debug: 4.3.7(supports-color@8.1.1)
+    transitivePeerDependencies:
+      - supports-color
+
   mrmime@2.0.0: {}
 
   ms@2.0.0: {}
@@ -22515,10 +22792,16 @@ snapshots:
 
   neo-async@2.6.2: {}
 
+  new-find-package-json@2.0.0:
+    dependencies:
+      debug: 4.3.7(supports-color@8.1.1)
+    transitivePeerDependencies:
+      - supports-color
+
   no-case@3.0.4:
     dependencies:
       lower-case: 2.0.2
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   node-abort-controller@3.1.1: {}
 
@@ -22710,7 +22993,7 @@ snapshots:
       tar-stream: 2.2.0
       tmp: 0.2.3
       tsconfig-paths: 4.2.0
-      tslib: 2.7.0
+      tslib: 2.8.1
       yargs: 17.7.2
       yargs-parser: 21.1.1
     optionalDependencies:
@@ -23015,7 +23298,7 @@ snapshots:
   param-case@3.0.4:
     dependencies:
       dot-case: 3.0.4
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   parent-module@1.0.1:
     dependencies:
@@ -23084,7 +23367,7 @@ snapshots:
   pascal-case@3.1.2:
     dependencies:
       no-case: 3.0.4
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   password-prompt@1.1.3:
     dependencies:
@@ -23151,6 +23434,8 @@ snapshots:
       safe-buffer: 5.2.1
       sha.js: 2.4.11
 
+  pend@1.2.0: {}
+
   periscopic@3.1.0:
     dependencies:
       '@types/estree': 1.0.5
@@ -23554,7 +23839,7 @@ snapshots:
 
   pvtsutils@1.3.5:
     dependencies:
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   pvutils@1.1.3: {}
 
@@ -23568,6 +23853,8 @@ snapshots:
 
   queue-microtask@1.2.3: {}
 
+  queue-tick@1.0.1: {}
+
   queue@6.0.2:
     dependencies:
       inherits: 2.0.4
@@ -24366,6 +24653,8 @@ snapshots:
       get-intrinsic: 1.2.4
       object-inspect: 1.13.1
 
+  sift@17.1.3: {}
+
   signal-exit@3.0.7: {}
 
   signal-exit@4.1.0: {}
@@ -24425,7 +24714,7 @@ snapshots:
   snake-case@3.0.4:
     dependencies:
       dot-case: 3.0.4
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   snarkjs@0.5.0:
     dependencies:
@@ -24624,6 +24913,10 @@ snapshots:
 
   space-separated-tokens@2.0.2: {}
 
+  sparse-bitfield@3.0.3:
+    dependencies:
+      memory-pager: 1.5.0
+
   spawn-wrap@2.0.0:
     dependencies:
       foreground-child: 2.0.0
@@ -24720,6 +25013,14 @@ snapshots:
 
   streamsearch@1.1.0: {}
 
+  streamx@2.21.1:
+    dependencies:
+      fast-fifo: 1.3.2
+      queue-tick: 1.0.1
+      text-decoder: 1.2.3
+    optionalDependencies:
+      bare-events: 2.5.3
+
   string-argv@0.3.2: {}
 
   string-format@2.0.0: {}
@@ -24951,7 +25252,7 @@ snapshots:
   synckit@0.9.1:
     dependencies:
       '@pkgr/core': 0.1.1
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   table-layout@1.0.2:
     dependencies:
@@ -24997,6 +25298,12 @@ snapshots:
       inherits: 2.0.4
       readable-stream: 3.6.2
 
+  tar-stream@3.1.7:
+    dependencies:
+      b4a: 1.6.6
+      fast-fifo: 1.3.2
+      streamx: 2.21.1
+
   tar@6.2.1:
     dependencies:
       chownr: 2.0.0
@@ -25048,6 +25355,10 @@ snapshots:
       glob: 7.2.3
       minimatch: 3.1.2
 
+  text-decoder@1.2.3:
+    dependencies:
+      b4a: 1.6.6
+
   text-extensions@1.9.0: {}
 
   text-extensions@2.4.0: {}
@@ -25119,6 +25430,10 @@ snapshots:
 
   tr46@0.0.3: {}
 
+  tr46@4.1.1:
+    dependencies:
+      punycode: 2.3.1
+
   tree-kill@1.2.2: {}
 
   treeverse@3.0.0: {}
@@ -25730,10 +26045,12 @@ snapshots:
       '@peculiar/json-schema': 1.1.12
       asn1js: 3.0.5
       pvtsutils: 1.3.5
-      tslib: 2.7.0
+      tslib: 2.8.1
 
   webidl-conversions@3.0.1: {}
 
+  webidl-conversions@7.0.0: {}
+
   webpack-bundle-analyzer@4.10.2:
     dependencies:
       '@discoveryjs/json-ext': 0.5.7
@@ -25934,6 +26251,11 @@ snapshots:
 
   whatwg-mimetype@4.0.0: {}
 
+  whatwg-url@13.0.0:
+    dependencies:
+      tr46: 4.1.1
+      webidl-conversions: 7.0.0
+
   whatwg-url@5.0.0:
     dependencies:
       tr46: 0.0.3
@@ -26167,6 +26489,11 @@ snapshots:
       y18n: 5.0.8
       yargs-parser: 21.1.1
 
+  yauzl@3.2.0:
+    dependencies:
+      buffer-crc32: 0.2.13
+      pend: 1.2.0
+
   yn@2.0.0: {}
 
   yn@3.1.1: {}