Introduzindo MongoDB no CrazyStack Node.js

Dev Doido - Feb 27 '23 - - Dev Community

Nesta aula, você aprenderá sobre a introdução ao MongoDB. Veremos uma classe de helper para acessar o banco de dados MongoDB que fornece funções para conectar e desconectar do banco de dados, recuperar uma coleção específica, remover a senha de um objeto da coleção, remover a senha de vários objetos da coleção, iniciar e finalizar uma sessão no MongoDB e recuperar a sessão atual. Essa classe ajudará na gestão das conexões com o banco de dados e na realização de operações de consulta de forma simples e eficiente.

Este é um material auxiliar do bootcamp CrazyStack Node.js do DevDoido. Ele servirá como uma espécie de documentação de alguns códigos vistos durante as aulas apenas como material complementar, garanta já sua vaga no bootcamp clicando AQUI!.

import { MongoClient, Collection } from "mongodb";

export const MongoHelper = {
  client: null as unknown as MongoClient | any,
  uri: null as unknown as string,
  session: null as unknown as any,
  async connect(connectionUrl: string) {
    this.uri = connectionUrl;
    this.client = await MongoClient.connect(connectionUrl, {
      retryReads: true,
      retryWrites: true,
    });
    return this?.client?.connect?.();
  },
  async disconnect() {
    if (this?.client) {
      await this.endSession();
      await this.client.close();
      this.client = null;
    }
  },
  async getCollection(name: string): Promise<Collection> {
    if (!this.client) {
      await this.connect(this.uri);
    }
    return this?.client?.db?.()?.collection?.(name);
  },
  mapPassword(collection: any): any {
    return { ...collection, password: null };
  },
  mapCollectionPassword(collection: any[]): any[] {
    return collection?.map?.((coll) => MongoHelper.mapPassword(coll));
  },
  async startSession() {
    const session = await this?.client?.startSession?.();
    this.session = session;
    return session;
  },
  async getSession() {
    return this.session;
  },
  async endSession() {
    if (this.session) {
      await this.session.endSession();
    }
    this.session = null;
  },
};
Enter fullscreen mode Exit fullscreen mode

O código acima é uma classe de helper para acesso ao banco de dados MongoDB. Ele fornece funções para conectar e desconectar do banco de dados, recuperar uma coleção específica, remover a senha de um objeto da coleção, remover a senha de vários objetos da coleção, iniciar e finalizar uma sessão no MongoDB e recuperar a sessão atual. Ele também armazena o cliente Mongo, a URI de conexão e a sessão atual como variáveis de instância.

import {
  mapAnyToMongoObject,
  mapQueryParamsToQueryMongo,
  MongoHelper,
} from "@/application/infra/database/mongodb";
import { Repository } from "@/application/infra/contracts/repository";
import { Collection, ObjectId } from "mongodb";

export class MongoRepository extends Repository {
  public collectionName: string;
  constructor(collectionName: string) {
    super();
    this.collectionName = collectionName;
  }
  private async getCollection(): Promise<Collection> {
    return MongoHelper.getCollection(this.collectionName);
  }
  async insertOne(data: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    return collection.insertOne(mapAnyToMongoObject(data), { session });
  }
  async add(data: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    const { insertedId } = (await this.insertOne(data)) || {};
    if (!!insertedId) {
      const objInserted = await collection.findOne(
        { _id: new ObjectId(insertedId) },
        { session }
      );
      return MongoHelper.mapPassword(objInserted);
    }
    return null;
  }
  async updateOne(query: any, data: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    if (query._id) {
      query._id = new ObjectId(query._id);
    }
    return collection.updateOne(
      mapQueryParamsToQueryMongo(query),
      { $set: mapAnyToMongoObject(data) },
      { upsert: false, session }
    );
  }
  async update(query: any, data: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    const result = await this.updateOne(query, data);
    if (result?.modifiedCount === 1) {
      return collection.findOne(mapQueryParamsToQueryMongo(query), { session });
    }
    return null;
  }
  async incrementOne(query: any, data: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    if (query._id) {
      query._id = new ObjectId(query._id);
    }
    return collection.updateOne(
      mapQueryParamsToQueryMongo(query),
      { $inc: mapAnyToMongoObject(data) },
      { upsert: false, session }
    );
  }
  async increment(query: any, data: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    const result = await this.incrementOne(query, data);
    if (result?.modifiedCount === 1) {
      return collection.findOne(mapQueryParamsToQueryMongo(query), { session });
    }
    return null;
  }
  async deleteOne(query: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    if (query._id) {
      query._id = new ObjectId(query._id);
    }
    const result = (await collection.deleteOne(mapQueryParamsToQueryMongo(query), {
      session,
    })) as any;
    if (result?.deletedCount === 1) {
      return true;
    }
    return false;
  }
  async getOne(query: any, options?: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    if (query?._id) {
      query._id = new ObjectId(query._id);
    }
    return collection.findOne(mapQueryParamsToQueryMongo(query), {
      ...options,
      session,
    });
  }
  async getAll(query: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    return collection.find(mapQueryParamsToQueryMongo(query), { session }).toArray();
  }
  async getPaginate(
    page: number,
    query: any,
    sort: any,
    limit: number,
    projection: any
  ): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    if (query._id) {
      query._id = new ObjectId(query._id);
    }
    return collection
      .find(mapQueryParamsToQueryMongo(query), { session })
      .project(projection)
      .skip((page - 1) * limit)
      .limit(limit)
      .sort(sort)
      .toArray();
  }
  async getCount(query: any): Promise<any> {
    const collection = await this.getCollection();
    if (query._id) {
      query._id = new ObjectId(query._id);
    }
    return collection.countDocuments(mapQueryParamsToQueryMongo(query));
  }
  async aggregate(query: any): Promise<any> {
    const collection = await this.getCollection();
    const session = await MongoHelper.getSession();
    return collection.aggregate(query, { session }).toArray();
  }
}
Enter fullscreen mode Exit fullscreen mode

Este é um código de uma classe chamada "MongoRepository" que herda de uma classe chamada "Repository". Esta classe fornece uma estrutura básica para acessar e manipular dados armazenados em um banco de dados MongoDB. Ele contém métodos para inserir, atualizar, excluir e recuperar dados, bem como métodos para paginação, contagem e agregação de dados. Ele também usa outras classes para ajudar a mapear os dados entre o aplicativo e o banco de dados, e para gerenciar a conexão com o banco de dados.

Em breve

Em breve no curso vocês entenderão o poder de utilizar essa classe abstrata Repository. Por exemplo, caso queiramos usar um ORM como o prisma poderíamos fazer um PrismaRepository:

import { PrismaClient } from '@prisma/client'
import { Repository } from '@/application/infra/contracts/repository'

export class PrismaRepository extends Repository {
  private prisma: PrismaClient

  constructor() {
    super()
    this.prisma = new PrismaClient()
  }

  async insertOne(data: any): Promise<any> {
    return this.prisma.create(this.collectionName, data)
  }

  async add(data: any): Promise<any> {
    return this.prisma.create(this.collectionName, data)
  }

  async updateOne(query: any, data: any): Promise<any> {
    return this.prisma.update(this.collectionName, { data, where: query })
  }

  async update(query: any, data: any): Promise<any> {
    const result = await this.updateOne(query, data)
    if (result) {
      return this.getOne(query)
    }
    return null
  }

  async deleteOne(query: any): Promise<any> {
    return this.prisma.delete(this.collectionName, { where: query })
  }

  async getOne(query: any, options?: any): Promise<any> {
    return this.prisma.findOne(this.collectionName, { where: query, ...options })
  }

  async getAll(query: any): Promise<any> {
    return this.prisma.findMany(this.collectionName, { where: query })
  }

  async getPaginate(
    page: number,
    query: any,
    sort: any,
    limit: number,
    projection: any
  ): Promise<any> {
    const skip = (page - 1) * limit
    return this.prisma.findMany(this.collectionName, {
      where: query,
      skip,
      take: limit,
      orderBy: sort,
      select: projection,
    })
  }

  async getCount(query: any): Promise<any> {
    return this.prisma.count(this.collectionName, { where: query })
  }

  async aggregate(query: any): Promise<any> {
    // Not supported by Prisma
    return null
  }
}
Enter fullscreen mode Exit fullscreen mode

Ou um SupaBaseRepository

import { Repository } from "@/application/infra/contracts/repository";
import { Supabase } from "@supabase/supabase-js";

export class SupabaseRepository extends Repository {
  private supabase: Supabase;

  constructor(supabase: Supabase) {
    super();
    this.supabase = supabase;
  }

  async insertOne(tableName: string, data: any): Promise<any> {
    return this.supabase.from(tableName).insert(data).one();
  }

  async add(tableName: string, data: any): Promise<any> {
    const result = await this.insertOne(tableName, data);
    if (result?.inserted_id) {
      return this.supabase
        .from(tableName)
        .select("*")
        .where({ id: result.inserted_id })
        .one();
    }
    return null;
  }

  async updateOne(tableName: string, query: any, data: any): Promise<any> {
    return this.supabase
      .from(tableName)
      .update(data)
      .where(query)
      .returning("*")
      .one();
  }

  async update(tableName: string, query: any, data: any): Promise<any> {
    const result = await this.updateOne(tableName, query, data);
    if (result) {
      return result;
    }
    return null;
  }

  async deleteOne(tableName: string, query: any): Promise<boolean> {
    const result = await this.supabase
      .from(tableName)
      .delete()
      .where(query)
      .returning("*")
      .one();
    if (result) {
      return true;
    }
    return false;
  }

  async getOne(tableName: string, query: any): Promise<any> {
    return this.supabase
      .from(tableName)
      .select("*")
      .where(query)
      .one();
  }
  ///resto
}
Enter fullscreen mode Exit fullscreen mode

LINK DO REPOSITÓRIO

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .