Aprenda como construir uma ferramenta CLI (Command Line Interface) de gerenciador de tarefas com Node.js e MongoDB. Este tutorial cobrirá os fundamentos da criação de uma ferramenta CLI, armazenamento de dados no MongoDB e implementação de operações CRUD básicas.
Olá a todos 👋 Neste tutorial, você aprenderá como fazer uma ferramenta CLI (Command Line Interface) simples do Gerenciador de Tarefas. Isso significa que você pode usar comandos para criar, visualizar, atualizar ou excluir seus todos.
Estaremos construindo esta ferramenta CLI usando NodeJS . Também usaremos o MongoDB como banco de dados para armazenar todas as nossas tarefas. Finalmente, usaremos alguns pacotes úteis do npm :
Antes de começarmos, quero que você saiba que pode encontrar o código completo deste projeto no GitHub. Se você não tiver certeza sobre algo no código, poderá sempre consultar a versão final.
Configuração do projeto
Bem-vindo à primeira seção deste manual! Aqui estaremos montando nosso projeto.
Isso envolve alguns passos simples: criar um novo diretório, configurar o package.jsonarquivo e instalar os pacotes npm necessários, como giz, investigador, comandante e outros sobre os quais falaremos em breve. Também organizaremos o projeto criando pastas.
Antes de começarmos, vamos garantir que você tenha o NodeJS instalado em seu sistema. Você pode obter a versão LTS mais recente neste site: https://nodejs.org/en .
Para verificar se o Node está instalado corretamente, digite este comando: node --version. Se você vir um número de versão, está tudo pronto! Caso contrário, você precisará solucionar os erros.
Assim que o NodeJS estiver instalado e funcionando, crie uma nova pasta chamada “todo”. Você pode usar seu editor de código favorito (prefiro Visual Studio Code) ou seguir estas etapas em seu terminal:
A primeira e mais importante etapa é configurar o package.jsonarquivo. Mas não se preocupe em fazer isso manualmente. Você pode economizar tempo usando este comando:
npm init --yes
Concluída esta etapa, vamos passar para a próxima etapa e obter todas as coisas necessárias para o nosso projeto.
Para construir este projeto, precisaremos de alguns pacotes. Basta executar este comando simples para obter todos eles:
npm i commander inquirer chalk ora mongoose nanoid dotenv
Antes de continuar, vamos fazer uma pequena alteração no package.jsonarquivo. Remova esta linha: "main": "index.js"e adicione estas duas linhas:
"exports": "./index.js",
"type": "module",
Com essas mudanças, estamos convertendo nosso projeto de Módulos CommonJS para Módulos ES. Isso significa que usaremos importem vez de require()trazer módulos e exportem vez de module.exportscompartilhar coisas entre arquivos.
Agora, vamos organizar nosso projeto configurando uma estrutura de pastas inteligente. Isso significa que criaremos pastas para armazenar nossos arquivos JavaScript de maneira organizada. Esta etapa é muito importante. Torna as coisas fáceis de gerenciar e se desenvolve sem problemas.
Estamos criando 3 pastas e 2 arquivos na pasta principal:
Primeira pasta: commands . Dentro desta pasta, você criará 4 arquivos. Os nomes dos arquivos e a descrição do código que eles conterão são mencionados abaixo:
Segunda pasta: db . Dentro desta pasta, adicione um arquivo chamado connectDB.js. Este arquivo conterá o código para conectar-se ao banco de dados MongoDB e desconectar quando necessário.
Terceira pasta: schema . Dentro dele, crie um arquivo chamado TodoSchema.js. Este arquivo armazena o esquema e o modelo do Mongoose. Basicamente, um projeto para nossas tarefas, é assim que nossas tarefas ficarão.
Primeiro arquivo: .env . Crie este arquivo dentro do diretório raiz/pasta principal do projeto. É aqui que você colocará sua string de conexão do MongoDB.
Segundo arquivo: Crie o index.jsarquivo no próprio diretório raiz que servirá como ponto de entrada do nosso projeto. É como se fosse a frente do projeto – onde tudo começa.
Quando terminarmos, as pastas do seu projeto deverão ficar mais ou menos assim:
Estrutura de pastas do projeto Como conectar-se ao banco de dados
Agora que você configurou o projeto com sucesso, é hora de mergulhar na parte emocionante.
Para acompanhar todos os nossos todos, precisamos de um local para armazená-los. É aí que entra o MongoDB Atlas. É como um serviço especial que gerencia bancos de dados para nós. A melhor parte? Você pode começar a usá-lo gratuitamente (sem necessidade de cartão de crédito).
Para se conectar a ele, tudo que você precisa é algo chamado string de conexão. Se o MongoDB Atlas é novo para você, não se preocupe. Confira este artigo fácil de seguir: Tutorial do MongoDB Atlas - Como começar . Fornece informações suficientes para começar a usar o Atlas. Quando terminar, você saberá como obter o que precisa, incluindo a cadeia de conexão.
Depois de ter essa cadeia de conexão, crie uma nova coisa chamada "variável de ambiente". É como um código secreto que seu projeto usa. Abra o .envarquivo e faça uma linha como esta: MONGO_URI=. Após o =, coloque sua string de conexão.
Lembre-se: Substitua <password>pela sua senha real e <username>pelo nome de usuário do administrador do banco de dados na string de conexão. Além disso, adicione todosentre /?na string. Quando terminar, seu .envarquivo deverá ficar parecido com isto:
MONGO_URI=mongodb+srv://<username>:<password>@cluster0.k5tmsld.mongodb.net/todos?retryWrites=true&w=majority
Agora, vamos mergulhar no código que conecta nossa ferramenta ao banco de dados MongoDB. Abra o ./db/connectDB.jsarquivo e vamos escrever um código para fazer essa conexão acontecer.
Primeiramente, precisamos trazer o dotenvpacote que pegamos anteriormente quando estávamos configurando o projeto e invocar o config()método em dotenv. Isso nos ajuda a carregar variáveis de ambiente do .envarquivo. Veja como você faz isso:
import dotenv from 'dotenv'
dotenv.config()
A seguir, queremos importar mais alguns pacotes que usaremos aqui. Estes são mongoose, orae chalk:
import mongoose from 'mongoose'
import ora from 'ora'
import chalk from 'chalk'
Nota: mongoose é uma biblioteca Object Data Modeling (ODM) para MongoDB. Ele fornece uma abstração de nível superior, tornando mais fácil fazer coisas como adicionar, ler, atualizar e excluir coisas do banco de dados MongoDB.
Agora, vamos entrar em ação real. Definiremos duas funções aqui: connectDB()e disconnectDB().
A connectDB()função conterá o código para ajudar a conectar nosso aplicativo NodeJS ao banco de dados M0ngoDB usando mongoose. É como um telefonema conectando-os. Se não estabelecermos uma conexão primeiro, nosso aplicativo não será capaz de interagir com o banco de dados e realizar as diversas operações CRUD.
A disconnectDB()função faz o oposto. É como desligar o telefone depois que nosso aplicativo termina de falar com o banco de dados. Se não desconectarmos, é como manter a ligação mesmo depois de terminarmos.
Deixar de desconectar do banco de dados após terminarmos de interagir com ele pode causar vazamentos de recursos. Isso pode fazer com que seu aplicativo fique lento ou potencialmente trave com o tempo.
Deixe-me mostrar o código para ambas as funções:
export async function connectDB(){
try {
const spinner = ora('Connecting to the database...').start()
await mongoose.connect(process.env.MONGO_URI)
spinner.stop()
console.log(chalk.greenBright('Successfully connected to database!!!'))
} catch (error) {
console.log(chalk.redBright('Error: '), error);
process.exit(1)
}
}
export async function disconnectDB(){
try {
await mongoose.disconnect()
console.log(chalk.greenBright('Disconnected from the database.'))
} catch(err) {
console.log(chalk.redBright('Error: '), error);
process.exit(1)
}
}
É muito código para digerir de uma só vez, então deixe-me explicar isso para você:
Na connectDB()função, a linha mongoose.connect(process.env.MONGO_URI)nos ajuda a realmente conectar ao banco de dados usando a string de conexão.
Lembra do .envarquivo? Estamos usando suas informações aqui. Para carregar a MONGO_URIvariável, usamos o dotenvpacote e chamamos a config()função e então podemos acessá-la usando process.env.MONGO_URI.
Como mongoose.connect()retorna uma promessa, usamos a awaitpalavra-chave anterior para garantir que prosseguiremos somente quando a promessa retornada for resolvida.
É possível encontrar alguns erros ao executar este código, por isso agrupamos todo o código em um try...catch()bloco para garantir que quaisquer erros que apareçam sejam tratados corretamente no catch()bloco.
O orapacote nos ajuda a mostrar um botão giratório enquanto nos conectamos ao banco de dados. Uma vez conectado com sucesso, paramos o botão giratório e mostramos uma mensagem feliz em verde usando chalk.
Se você notar, estamos fazendo a mesma coisa na disconnectDB()função. Mas, em vez de conectar, desconectamos do banco de dados usando mongoose.disconnect(). Nós o envolvemos em um bloco try-catch semelhante e novamente mostramos mensagens coloridas usando chalk.
Usamos exportbefore essas funções para permitir que outras partes do projeto as utilizem. Não se esqueça de adicionar estas duas linhas temporárias no final do arquivo por enquanto:
connectDB()
disconnectDB()
Agora, você pode executar o connectDB.jsarquivo usando o comando: node ./db/connectDB.jse esperar ver isto no console:
Saída vista no terminal quando connectDB.jso arquivo é executado. Ele mostra como nosso código se conecta com sucesso ao banco de dados e se desconecta dele, mostrando mensagens de console apropriadas quando invocamos os métodos connectDB()e .disconnectDB()
Conectar-se ao banco de dados é um grande passo, mas você está fazendo um grande progresso! Antes de prosseguir, remova as 2 linhas que você adicionou no final, pois elas foram adicionadas apenas para verificar se nossas funções de conexão e desconexão estão funcionando conforme o esperado.
Como criar um modelo Mangusto
Um modelo Mongoose é como uma ferramenta que nos ajuda a conversar com o banco de dados. Com ele, podemos facilmente fazer coisas como adicionar, ler, atualizar e excluir tarefas. É como um assistente útil que sabe como se comunicar com o banco de dados.
Para fazer este modelo, precisamos de algo chamado Schema. Basicamente define como deve ser cada tarefa. Pense nisso como um plano ou um conjunto de instruções que orienta como cada tarefa é criada, quais informações ela deve conter e como essas informações são organizadas. É como definir regras sobre como nossas tarefas são armazenadas no banco de dados.
Vamos construir esse esquema no ./schema/TodoSchema.jsarquivo. Abra-o e vamos começar. Primeiro, precisamos de duas ferramentas especiais: mongoosee nanoid. Usaremos nanoidpara criar IDs curtos e exclusivos para cada tarefa.
Digite estas linhas para importar as ferramentas:
import mongoose from 'mongoose'
import {nanoid} from 'nanoid'
Agora, usamos o mongoose.Schema()método para criar nosso esquema. Aqui está o código para isso:
const TodoSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
detail: {
type: String,
required: true,
trim: true
},
status: {
type: String,
required: true,
enum: ['completed', 'pending'],
default: 'pending',
trim: true
},
code: {
type: String,
required: true,
default: 'code',
trim: true
}
}, {timestamps: true})
Qualquer tarefa criada usando este esquema terá as seguintes propriedades:
Definimos nosso esquema com sucesso – mas você pode estar se perguntando se a codepropriedade deveria ser exclusiva para cada tarefa. Atualmente ele armazena o mesmo valor, ou seja, " código ", para cada tarefa. Não se preocupe, nós consertaremos isso. Adicione este código no final:
TodoSchema.pre('save', function(next){
this.code = nanoid(10)
next()
})
Aqui, TodoSchema.pre('save', function(){....})nos ajuda a definir um gancho/função de pré-salvamento que é executado sempre antes que uma tarefa seja salva no banco de dados.
Dentro da função, usamos nanoid(10)para criar um ID exclusivo de 10 caracteres para a tarefa e colocar esse ID gerado no codecampo da tarefa (podemos realmente acessar qualquer propriedade/campo da tarefa usando a thispalavra-chave).
A última linha de código: next()basicamente informa ao computador que terminamos e ele pode finalmente salvar o documento agora. Com isso, geramos um ID exclusivo para cada tarefa criada usando o nanoidpacote.
Por último, faremos um Todosmodelo usando este TodoSchemablueprint e exportaremos. É assim:
const Todos = mongoose.model('Todos', TodoSchema)
export default Todos
E aí está! Construímos nosso esquema e modelo. Agora vamos prosseguir para a próxima seção deste tutorial.
Como trabalhar em operações CRUD
Parabéns por ter acompanhado com sucesso até aqui. Até agora, fizemos três coisas:
A seguir, trabalharemos nas diversas operações CRUD, como criação, leitura, atualização e exclusão de tarefas de nosso banco de dados.
Agora, vamos começar criando tarefas em nosso projeto. No início, planejei um processo simples em que você adiciona uma tarefa por vez ao banco de dados. Isso significa que quando você instrui a ferramenta a criar uma tarefa, ela solicita uma vez os detalhes da tarefa – como o nome e a descrição – e então salva a tarefa.
Mas então percebi: e se alguém quiser adicionar muitas tarefas rapidamente? Fazer isso um por um não é legal. Existem dois problemas:
Esses problemas não são nada divertidos! Para corrigir isso, precisamos de uma maneira de adicionar várias tarefas de uma só vez. Veja como faremos isso:
Depois de inserir o nome e a descrição da tarefa, perguntaremos se você deseja adicionar mais tarefas. Se você inserir sim, continuamos o processo desde o início (pedindo que você insira novamente o nome e a descrição da próxima tarefa). Mas se você inserir não, o processo de solicitação de perguntas será interrompido e todas as tarefas inseridas serão salvas juntas no banco de dados. Dessa forma, você pode criar muitas tarefas sem o incômodo de fazer uma por uma. É tudo uma questão de tornar as coisas mais fáceis para você.
Estaremos escrevendo algum código no ./commands/addTask.jsarquivo. É aqui que a mágica acontece. Vamos decompô-lo passo a passo:
Primeiro, importamos os pacotes e funções necessários que criamos anteriormente. Você pode adicionar estas linhas de código para fazer isso:
import inquirer from "inquirer";
import { connectDB, disconnectDB } from '../db/connectDB.js'
import Todos from "../schema/TodoSchema.js";
import ora from "ora";
import chalk from "chalk";
Agora, criamos uma função assíncrona chamada input()para coletar o nome e os detalhes da tarefa do usuário. Veja como é:
async function input(){
const answers = await inquirer.prompt([
{ name: 'name', message: 'Enter name of the task:', type: 'input' },
{ name: 'detail', message: 'Enter the details of the task:', type: 'input' },
])
return answers
}
Em termos simples, input()solicita inquirerao usuário o nome e os detalhes da tarefa. As respostas são então retornadas como um objeto.
Mas espere, você pode se perguntar o que inquirer.prompt()está acontecendo. É um método do inquirerpacote que faz perguntas e aguarda respostas. Você fornece uma série de objetos de pergunta, cada um contendo detalhes como a mensagem a ser exibida ao usuário e o tipo de pergunta. A função retorna uma Promise, então esperamos awaitpelas respostas do usuário que são retornadas como um objeto.
Aqui { name: 'name', message: 'Enter name of the task:', type: 'input' }está a primeira pergunta que será feita ao usuário. A messagepropriedade contém a pergunta que será exibida ao usuário. No nosso caso, é: Enter the name of the task. O usuário será solicitado a inserir algum texto (uma String), já que esta questão é de type: 'input'. Isso name: 'name'significa que a resposta do usuário a esta pergunta será atribuída a uma propriedade chamada – nameno objeto da resposta.
O próximo objeto é a segunda pergunta que será feita ao usuário. Neste caso, uma mensagem será exibida no terminal: Enter the details of the taske a resposta do usuário será atribuída a uma propriedade chamada detailno objeto da resposta.
Para ver como o código acima funciona, você pode adicionar estas 2 linhas de código no final do arquivo:
const output = await input()
console.log(output)
Agora salve o arquivo e execute o código usando o comando: node ./commands/addTask.js. Isto é o que você verá ao executar o código:
Saída vista no terminal quando apenas invocamos o input()método e executamos o código. Mostra como inquirer.jsretorna as respostas do usuário após o processo de solicitação de perguntas.
Agora podemos prosseguir com o resto do código e você pode remover as duas últimas linhas que acabou de adicionar.
Agora, vamos criar uma função chamada askQuestions()para reunir múltiplas tarefas. Isto é o que parece:
const askQuestions = async() => {
const todoArray = []
let loop = false
do{
const userRes = await input()
todoArray.push(userRes)
const confirmQ = await inquirer.prompt([{ name: 'confirm', message: 'Do you want to add more tasks?', type: 'confirm' }])
if(confirmQ.confirm){
loop = true
} else {
loop = false
}
} while(loop)
return todoArray
}
Em askQuestions(), configuramos um loop que continua solicitando tarefas até que o usuário decida parar. Reunimos cada tarefa do usuário chamando a input()função, e a resposta do usuário retornada é enviada para o arquivo todoArray.
Em seguida, perguntamos se o usuário deseja adicionar mais tarefas por meio de uma pergunta de confirmação. Se eles disserem sim, definimos loopcomo truee o loop continua – caso contrário, looptorna-se falsee o loop termina. Finalmente, retornamos o array de tarefas, ou seja todoArray.
Você pode testar isso adicionando estas linhas de código no final do arquivo:
const output = await askQuestions()
console.log(output)
Ao executar o arquivo usando node ./commands/addTask.js, você verá um resultado semelhante ao que vê aqui:
Saída vista no terminal quando invocamos o askQuestions()método e executamos o código. Mostra o array de tarefas retornadas pelo método quando o usuário não deseja continuar adicionando mais tarefas.
Estamos quase lá! Antes de continuar, não se esqueça de remover as duas últimas linhas que você adicionou agora. Depois de fazer isso, vamos em frente.
Até este ponto, conseguimos coletar com sucesso todas as tarefas que o usuário deseja criar.
Agora, vamos definir a última peça do quebra-cabeça: a addTask()função. Esta função reúne tudo e completa o processo de criação de tarefas. Aqui está o código completo:
export default async function addTask() {
try {
// calling askQuestions() to get array of todo's
const userResponse = await askQuestions()
// connecting to the database
await connectDB()
// Displaying a spinner with the following text message using ora
let spinner = ora('Creating the todos...').start()
// looping over every todo in the userResponse array
// and saving each todo in the database
for(let i=0; i<userResponse.length; i++){
const response = userResponse[i]
await Todos.create(response)
}
// Stopping the spinner and displaying the success message
spinner.stop()
console.log(
chalk.greenBright('Created the todos!')
)
// disconnecting the database
await disconnectDB()
} catch (error) {
// Error Handling
console.log('Something went wrong, Error: ', error)
process.exit(1)
}
}
A addTask()função começa chamando a askQuestions()função para reunir o conjunto de tarefas e atribuí-lo à userResponsevariável. Em seguida, ele se conecta ao banco de dados usando connectDB(), exibe um controle giratório usando orapara mostrar o processo de criação da tarefa, percorre cada tarefa na matriz e salva-a no banco de dados usando Todos.create(response).
Depois que todas as tarefas forem salvas, o controle giratório para, uma mensagem de sucesso é mostrada e então ele se desconecta do banco de dados usando disconnectDB().
Todo o código é agrupado em um try...catchbloco para lidar com possíveis erros normalmente.
Com este código, você concluiu o processo de criação de tarefas. Bom trabalho! Este foi provavelmente o trecho de código mais complexo de todo o projeto. Operações futuras, como tarefas de leitura, exclusão e atualização, serão bastante simples e fáceis em comparação. Dito isso, vamos prosseguir para a operação de leitura.
Agora exploraremos como ler tarefas do banco de dados MongoDB. O processo é direto e orientarei você por todo o código do ./commands/readTask.jsarquivo:
Primeiro, vamos importar os pacotes e funções necessários no início do arquivo:
// Importing packages and functions
import { connectDB, disconnectDB } from '../db/connectDB.js'
import Todos from '../schema/TodoSchema.js'
import chalk from 'chalk'
import ora from 'ora'
Agora, vamos definir uma função assíncrona chamada readTask()que encapsula a lógica para tarefas de leitura. Toda a função é envolvida em um bloco try...catch para lidar com possíveis erros:
export default async function readTask(){
try {
// connecting to the database
await connectDB()
// starting the spinner
const spinner = ora('Fetching all todos...').start()
// fetching all the todos from the database
const todos = await Todos.find({})
// stopping the spinner
spinner.stop()
// check if todos exist or not
if(todos.length === 0){
console.log(chalk.blueBright('You do not have any tasks yet!'))
} else {
todos.forEach(todo => {
console.log(
chalk.cyanBright('Todo Code: ') + todo.code + '\n' +
chalk.blueBright('Name: ') + todo.name + '\n' +
chalk.yellowBright('Description: ') + todo.detail + '\n'
)
})
}
// disconnect from the database
await disconnectDB()
} catch (error) {
// Error Handling
console.log('Something went wrong, Error: ', error)
process.exit(1)
}
}
readTask()
Agora, vamos detalhar o código passo a passo:
Na última linha do código, chamamos a readTask()função. Isso é apenas para fins de teste e você pode remover esta linha conforme as instruções.
Para executar o código, use o comando: node ./commands/readTask.js. Ao executar isso, você verá algo semelhante à saída mostrada aqui:
Nota: Eu já havia criado algumas tarefas aleatórias antes, então quando executo o readTask.jsarquivo, vejo isso em meu terminal:
Saída vista no terminal quando readTask.jso arquivo é executado. Mostra como o código lê com sucesso todas as tarefas do banco de dados e as imprime no terminal.
Antes de prosseguirmos, não se esqueça de remover a última linha de código do readTask.jsarquivo, pois não precisaremos dela no futuro.
Com este código, você implementou com sucesso a funcionalidade de leitura para sua ferramenta CLI do gerenciador de tarefas. Bom trabalho! Nas próximas seções, exploraremos como excluir e atualizar tarefas.
Esta seção do tutorial aborda o processo simples de exclusão de todos do banco de dados. A lógica é simples: os usuários inserem o código Todo da tarefa que desejam excluir e nós removemos essa tarefa do banco de dados.
Vamos nos aprofundar no código para fazer isso acontecer no ./commands/deleteTask.jsarquivo.
A primeira etapa é importar os pacotes e funções necessários no início do arquivo, incluindo investigador, Todosmodelo, connectDB(), desconectaDB(), ora e giz.
// Importing packages and functions
import inquirer from "inquirer";
import Todos from '../schema/TodoSchema.js'
import {connectDB, disconnectDB} from '../db/connectDB.js'
import ora from "ora";
import chalk from "chalk";
A seguir, definiremos uma função assíncrona chamada getTaskCode(). A função desta função é solicitar ao usuário que insira o código da tarefa que deseja excluir usando inquirer. A função então corta o código inserido pelo usuário usando o trim()método e retorna o código cortado. O processo de corte é necessário para remover os espaços em branco iniciais ou finais que o código pode conter.
Aqui está o código da getTaskCode()função:
export async function getTaskCode(){
try {
// Prompting the user to enter the todo code
const answers = await inquirer.prompt([
{name: 'code', 'message': 'Enter the code of the todo: ', type: 'input'},
])
// Trimming user's response so that the todo code does not contain any starting or trailing white spaces
answers.code = answers.code.trim()
return answers
} catch (error) {
console.log('Something went wrong...\n', error)
}
}
Agora definiremos a função principal chamada deleteTask(). O código completo está abaixo:
export default async function deleteTask(){
try {
// Obtaining the todo code provided by user
const userCode = await getTaskCode()
// Connecting to the database
await connectDB()
// Starting the spinner
const spinner = ora('Finding and Deleting the todo...').start()
// Deleting the task
const response = await Todos.deleteOne({code: userCode.code})
// Stopping the spinner
spinner.stop()
// Checking the delete operation
if(response.deletedCount === 0){
console.log(chalk.redBright('Could not find any todo matching the provided name. Deletion failed.'))
} else {
console.log(chalk.greenBright('Deleted Task Successfully'))
}
// Disconnecting from the database
await disconnectDB()
} catch (error) {
// Error Handling
console.log('Something went wrong, Error: ', error)
process.exit(1)
}
}
Vamos detalhar esse código passo a passo:
Se eu chamar a função: deleteTask()e depois executar o código usando node /commands/deleteTask.jso comando, verei isso em meu console:
Saída vista no terminal quando deleteTask.jso arquivo é executado. Mostra como o código exclui com êxito uma única tarefa do banco de dados.
Como você pode ver no GIF acima, o código solicitará que você insira um código Todo para a tarefa que deseja excluir. Após a exclusão, você receberá uma mensagem de confirmação no console. Quando lemos todas as nossas tarefas após o processo de exclusão, não conseguimos ver a tarefa excluída. Isso implica que nosso código consegue fazer o que deveria fazer com sucesso!
Nesta seção, veremos o código para atualizar uma tarefa específica. Atualizar uma tarefa é um pouco mais complicado em comparação com as operações anteriores. O processo se desenrola da seguinte forma:
Vamos começar a codificar! A primeira coisa que você precisa fazer é importar todos os pacotes e funções que precisaremos para realizar este trabalho.
// Importing packages and functions
import {connectDB, disconnectDB} from '../db/connectDB.js'
import { getTaskCode } from './deleteTask.js'
import inquirer from 'inquirer'
import Todos from '../schema/TodoSchema.js'
import ora from 'ora'
import chalk from 'chalk'
Antes de começarmos a trabalhar em nossa updateTask()função, criaremos uma pequena função no mesmo arquivo chamada askUpdateQ(). A função desta função é solicitar ao usuário que insira os valores atualizados da tarefa, como nome, descrição e status da tarefa. No final, esta função retornará o objeto de resposta.
Aqui está o código para isso:
async function askUpdateQ(todo){
try {
// Prompting the user to update the todo data
const update = await inquirer.prompt([
{name: 'name', message: 'Update the name?', type: 'input', default: todo.name},
{name: 'detail', message: 'Update the Description?', type: 'input', default: todo.detail},
{name: 'status', message: 'Update the status', type: 'list', choices: ['pending', 'completed'], default: todo.status}
])
return update
} catch (error) {
console.log('Something went wrong... \n', error)
}
}
Duas coisas devem ser observadas aqui:
Dito isso, agora vamos dar uma olhada no código da updateTask()função:
export default async function updateTask(){
try {
// Obtaining the task code entered by user by calling getTaskCode() method
const userCode = await getTaskCode()
// Connecting to the database
await connectDB()
// Starting the spinner
const spinner = ora('Finding the todo...').start()
// Finding the todo which the user wants to update
const todo = await Todos.findOne({code: userCode.code})
// Stopping the spinner
spinner.stop()
// Checking if the todo exists or not
if(!todo){
console.log(chalk.redBright('Could not find a Todo with the code you provided.'))
} else{
console.log(chalk.blueBright('Type the updated properties. Press Enter if you don\'t want to update the data.'))
// Get the user's response of the updated data by calling askUpdateQ() method
const update = await askUpdateQ(todo)
// If user marked status as completed, we delete the todo else we update the data
if(update.status === 'completed'){
// Changing spinner text and starting it again
spinner.text = 'Deleting the todo...'
spinner.start()
// Deleting the todo
await Todos.deleteOne({_id : todo._id})
// Stopping the spinner and display the success message
spinner.stop()
console.log(chalk.greenBright('Deleted the todo.'))
} else {
// Update the todo
spinner.text = 'Updating the todo'
spinner.start()
await Todos.updateOne({_id: todo._id}, update, {runValidators: true})
spinner.stop()
console.log(chalk.greenBright('Updated the todo.'))
}
}
// Disconnecting from the database
await disconnectDB()
} catch (error) {
// Error Handling
console.log('Something went wrong, Error: ', error)
process.exit(1)
}
}
Aqui está um detalhamento do código acima:
Se eu chamar a updateTask()função e executar o código usando o comando: node ./commands/updateTask.js, verei algo assim em meu console:
Saída vista no terminal quando updateTask.jso arquivo é executado. Ele mostra como o código busca com sucesso a tarefa original e a substitui com sucesso pelos valores atualizados fornecidos pelo usuário.
Com isso, você implementou com sucesso todas as operações CRUD. Agora, vamos usar a commanderbiblioteca para reunir tudo e criar uma ferramenta CLI totalmente funcional.
Como escrever o ponto de entrada CLI usando Commander
Nos estágios finais do nosso projeto, aproveitaremos o poder da commanderbiblioteca para criar uma interface CLI amigável. Com commander, podemos definir diferentes comandos – como ler, adicionar, atualizar e excluir – de maneira organizada e intuitiva.
Nosso código residirá no index.jsarquivo, que serve como ponto de entrada de nossa aplicação. Abaixo está o código completo:
#!/usr/bin/env node
// Importing the required functions for each command
import addTask from './commands/addTask.js'
import deleteTask from './commands/deleteTask.js'
import readTask from './commands/readTask.js'
import updateTask from './commands/updateTask.js'
// Importing the Command class from Commander.js library
import { Command } from 'commander'
// Creating an instance of the Command class
const program = new Command()
// Setting the name and description of the CLI tool
program
.name('todo')
.description('Your terminal task manager!')
.version('1.0.0')
// Defining a command called 'add'
program
.command('add')
.description('Create a new todo.')
.action(addTask)
// Defining a command called 'read'
program
.command('read')
.description('Reads all the todos.')
.action(readTask)
// Defining a command called 'update'
program
.command('update')
.description('Updates a todo.')
.action(updateTask)
// Defining a command called 'delete'
program
.command('delete')
.description('Deletes a todo.')
.action(deleteTask)
// Parsing the command-line arguments and executing the corresponding actions
program.parse()
Como testar a ferramenta CLI
Agora estamos quase na linha de chegada da criação de nossa ferramenta CLI do Gerenciador de Tarefas! Antes de podermos instalar e utilizar a ferramenta, só precisamos fazer um pequeno ajuste no package.jsonarquivo. Basta adicionar a seguinte entrada ao arquivo JSON:
"bin": {
"todo": "index.js"
}
Se você já criou uma ferramenta CLI e deseja que os usuários a acessem a partir da linha de comando de maneira semelhante a comandos como nodeou npm, a propriedade "bin" entra em ação.
A propriedade "bin" no package.jsonarquivo permite especificar comandos que se tornarão acessíveis globalmente assim que o pacote for instalado. Em termos mais simples, permite criar atalhos para executar scripts ou funções específicas a partir da linha de comando.
O código fornecido instrui o Node.js a executar o script definido sempre index.jsque alguém entrar todono terminal. Em essência, isso transforma seu script em uma ferramenta de linha de comando acessível globalmente.
A etapa final antes de começar a usar a ferramenta é instalá-la globalmente em seu sistema! Execute o seguinte comando para fazer isso:
npm i -g .
Conclusão
Parabéns se você acompanhou até aqui! Agora você está totalmente equipado para começar a usar a ferramenta CLI do Gerenciador de Tarefas.
Aqui estão os comandos que você pode usar para operar a ferramenta:
Você também pode usar estas 2 opções usando a ferramenta:
E isso encerra este manual. Espero que você tenha achado agradável e informativo.
Aqui está o link para o Repositório: Task Manager CLI Tool Repo .
Vejo você na próxima vez! 👋 ❤️ ✨
Fonte: https://www.freecodecamp.org
#node #nodejs #mongodb