Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
JavaScriptchevron-right

Introdução ao Deno 2.0 e MongoDB

Jesse Hall13 min read • Published Jan 21, 2022 • Updated Oct 22, 2024
AtlasTypeScriptJavaScript
APLICATIVO COMPLETO
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Deno, o tempo de execução "Modern" para JavaScript e Typescript criado em Rust, lançaram recentemente a versão 2.0. Esta atualização principal traz melhorias significativas e novos recursos que tornam o Deno uma opção ainda mais chamativa para os desenvolvedores.
Se você estiver familiarizado com o Node.js, achará o Deno bastante semelhante, mas com algumas melhorias importantes. Do mesmo criador de Node.js, Ryan Dahl, Deno foi projetado para ser um sucessor mais seguro e moderno do Node.js
Curiosidade: Deno é um anagrama. Reorganizar as letras no nó para escrever Deno.
O Deno agora oferece suporte a gerenciadores de pacote como npm e JSR, mantendo a capacidade de importar diretamente de URLs sem um gerenciador de pacote . Ele usa módulos ES, tem suporte de primeira classe await, tem testes integrados e implementa APIs padrão da web sempre que possível, como fetch e localStorageintegrados. Essa flexibilidade no gerenciamento de dependências permite que os desenvolvedores escolham a abordagem que melhor se adequa às necessidades do projeto .
Além disso, também é muito seguro. É completamente bloqueado por padrão e exige que você habilite cada método de acesso especificamente. Isso faz com que o Deno emparelhe bem com o MongoDB , pois ele também é super seguro por padrão.
Aprenda assistindo
Aqui está uma versão em vídeo deste artigo, se você preferir assistir.

O que há de novo em Deno 2.0?

O Deno 2.0 apresenta vários recursos e melhorias interessantes:
  1. Compatibilidade npm aprimorada: o Deno agora suporta uma variedade mais ampla de pacotes npm, incluindo o driver oficial do MongoDB .
  2. Melhor desempenho: Melhorias significativas na velocidade em várias operações.
  3. Executador de testes integrado: não há necessidade de estruturas de testes externas.
  4. servidor HTTP nativo: construa aplicativos web simples sem frameworks de terceiros.
  5. Modelo de segurança aprimorado: permissões mais granulares e recursos de segurança aprimorados.
Neste tutorial, exploraremos alguns desses novos recursos ao criar um aplicação CRUD simples usando o MongoDB.

Pré-requisitos

  • Conhecimento básico do Typescript
  • Noções básicas sobre conceitos MongoDB
  • Familiaridade com APIs RESTful

Configurando o Deno 2.0

Para começar a usar o Deno 2.0, você precisará instalar ou atualizar o Deno no seu sistema.
  • Para macOS e Linux:
    curl -fsSL https://deno.land/install.sh | sh
  • Para Windows (usando o PowerShell):
    irm https://deno.land/install.ps1 | iex
Para obter mais opções, verifique as instruções oficiais de instalaçãodo Deno.

Extensão Deno VS Code

Se você estiver usando o VS Code, é altamente recomendável instalar a extensão oficial do Denoand. Esta extensão permite a verificação de tipo, integração com IntelliSense, Deno CLI e muito mais.

Criando um servidor HTTP básico com roteamento

Com o Deno 2.0, podemos criar um servidor HTTP simples com recursos de roteamento usando apenas recursos integrados. Vamos começar criando um arquivoserver.ts:
1const PORT = 3000;
2
3async function handler(req: Request): Promise<Response> {
4 const url = new URL(req.url);
5 const path = url.pathname;
6
7 if (req.method === "GET" && path === "/") {
8 return new Response("Hello, World!");
9 } else if (req.method === "POST" && path === "/api/todos") {
10 // Handle POST /api/todos
11 } else if (req.method === "GET" && path === "/api/todos") {
12 // Handle GET /api/todos
13 } else if (req.method === "GET" && path === "/api/todos/incomplete/count") {
14 // Handle GET /api/todos/incomplete/count
15 } else if (req.method === "GET" && path.startsWith("/api/todos/")) {
16 // Handle GET /api/todos/:id
17 } else if (req.method === "PUT" && path.startsWith("/api/todos/")) {
18 // Handle PUT /api/todos/:id
19 } else if (req.method === "DELETE" && path.startsWith("/api/todos/")) {
20 // Handle DELETE /api/todos/:id
21 } else {
22 return new Response("Not Found", { status: 404 });
23 }
24}
25
26console.log(`HTTP webserver running. Access it at: http://localhost:${PORT}/`);
27Deno.serve({ port: PORT }, handler);
Isso configura um servidor HTTP básico usando a funçãoDeno.serveintegrada do Deno. A funçãohandler processa as solicitações recebidas, roteando-as com base no método HTTP e no caminho da URL . Ele inclui espaços reservados para lidar com várias operações CRUD em um recurso "todos", bem como uma rota especial para contar todos incompletos. Se nenhuma rota correspondente for encontrada, ela retornará uma resposta 404 "Não encontrado". O servidor escuta na porta 3000 e uma mensagem do console é registrada para indicar que o servidor está em execução.
Agora, podemos iniciar nosso servidor Deno executando o seguinte comando:
1deno run --allow-env --allow-read --allow-net --allow-sys --env --watch server.ts
Isso iniciará nosso servidor e observará as alterações em nosso código. Se você fizer alterações, o servidor será reiniciado automaticamente. Lembre-se de que o Deno é muito seguro por padrão, portanto, precisamos usar os sinalizadores--allow-*apropriados para permitir que nosso servidor funcione.
Observação: com o Deno 2.0, agora você pode usar sinalizadores abreviados para permitir as permissões necessárias.
1deno -ERNS --env --watch server.ts
O sinalizador-E permite variáveis de ambiente, R permite a leitura de arquivos, N permite o acesso à rede, S permite o acesso ao sistema e --env permite o uso de variáveis de ambiente.

Testando nosso servidor

Agora, podemos testar nosso servidor navegando até http://localhost:3000 em nosso navegador. Devemos ver "Hello, World! exibido no navegador.

Configurando a conexão do MongoDB

Agora que temos um servidor básico, vamos configurar nossa conexão MongoDB . Usaremos o pacote oficial npm do MongoDB , que agora é totalmente compatível com o Deno 2.0.
Primeiro, vamos criar um novo arquivo chamado db.ts:
1import { MongoClient } from "npm:mongodb@5.6.0";
2
3const MONGODB_URI = Deno.env.get("MONGODB_URI") || "";
4const DB_NAME = Deno.env.get("DB_NAME") || "todo_db";
5
6if (!MONGODB_URI) {
7 console.error("MONGODB_URI is not set");
8 Deno.exit(1);
9}
10
11const client = new MongoClient(MONGODB_URI);
12
13try {
14 await client.connect();
15 await client.db("admin").command({ ping: 1 });
16 console.log("Connected to MongoDB");
17} catch (error) {
18 console.error("Error connecting to MongoDB:", error);
19 Deno.exit(1);
20}
21
22const db = client.db(DB_NAME);
23const todos = db.collection("todos");
24
25export { db, todos };
Se você estiver familiarizado com o Node.js, notará que o Deno faz as coisas de forma um pouco diferente. Em vez de usar um arquivopackage.json e baixar todos os pacotes para o diretório do projeto , o Deno usa caminhos de arquivo ou URLs para fazer referência às importações do módulo. Os módulos são baixados e armazenados em cache localmente, mas isso é feito globalmente e não por projeto. Isso elimina muito do bloat inerente ao Node.js e sua pastanode_modules .
Com o Deno 2.0, você pode optar por usar um gerenciador de pacote como npm ou JSR. Você também pode usar um projeto Node.js existente com o Deno e ele utilizará o arquivopackage.json.
Neste arquivo, importamos o MongoClient do pacote oficial npm do MongoDB e criamos uma nova instância dele. Em seguida, nos conectamos à nossa instância MongoDB usando a variável de ambienteMONGODB_URI . Se a variável não estiver definida, registramos um erro e encerramos o processo.
Uma vez conectado, ping no banco de dados de dados para garantir que nossa conexão esteja funcionando. Se o ping falhar, registramos um erro e encerramos o processo.
Definimos nosso banco de dados de dados e collection e os exportamos para que possamos usá-los em nosso aplicação.
Antes de podermos usar esse arquivo, precisaremos definir nossas variáveis de ambienteMONGODB_URI e DB_NAME . Podemos fazer isso criando um arquivo.env e adicionando o seguinte:
1MONGODB_URI="...your connection string..."
2DB_NAME="todo_db"
A maneira mais fácil de obter sua string de conexão é usar a GUI do MongoDB Atlas . Se você ainda não tiver uma conta, inscreva-se em uma camada grátis para sempre. Confira a documentação doConnect to Your Cluster para obter mais informações sobre como obter sua string de conexão.

Configuração de cada rota

Neste ponto, precisamos configurar cada função para cada rota. Eles serão responsáveis pela criação, leitura, atualização e exclusão de documentos (CRUD) em nosso banco de banco de dados MongoDB .
Vamos criar uma nova pasta chamada controllers e um arquivo dentro dela chamado todoController.ts.
No arquivotodoController.ts, primeiro importaremos nossa collectiontodose o ObjectId do pacotemongodbnpm :
1import { todos } from "../db.ts";
2import { ObjectId } from "npm:mongodb@5.6.0";

Criar rota

Agora, podemos começar a criar nossa primeira função de rota. Chamaremos essa função addTodo. Esta função adicionará um novo item de tarefa à nossa coleção de banco de dados de dados.
1// ... imports
2
3async function addTodo(req: Request): Promise<Response> {
4 try {
5 const body = await req.json();
6 const result = await todos.insertOne(body);
7 return new Response(JSON.stringify({ id: result.insertedId }), {
8 status: 201,
9 headers: { "Content-Type": "application/json" },
10 });
11 } catch (error) {
12 return new Response(JSON.stringify({ error: error.message }), {
13 status: 400,
14 headers: { "Content-Type": "application/json" },
15 });
16 }
17}
18
19export { addTodo };
A funçãoaddTodo pega um objetoRequest, extrai e analisa seu corpo JSON e tenta inserir esses dados na coleçãotodos. Em caso de sucesso, ele retorna um Response com um código de status 201 e o novo ID do documento. Se ocorrer um erro, ele retornará um código de status 400 com a mensagem de erro. A função usa try-catch para tratamento de erros e retorna respostas JSON, aderindo às práticas da API RESTful.
Vamos adicionar a funçãoaddTodo à nossa função handler no arquivoserver.ts.
1import { addTodo } from "./controllers/todoController.ts";
2// ... existing code
3
4async function handler(req: Request): Promise<Response> {
5 // ... existing code
6 else if (req.method === "POST" && path === "/api/todos") {
7 return await addTodo(req);
8 }
9 // ... existing code
10}

Testando a rota de criação

Agora, podemos testar nossa funçãoaddTodo. Usaremos o comandocurl para enviar uma solicitação POST para nossa rota de criação. Como alternativa, você pode usar uma ferramenta como Postman, Insonia ou Cloud Client no VS Code para enviar a solicitação.
1curl -X POST http://localhost:3000/api/todos -H "Content-Type: application/json" -d '{"title": "Todo 1", "complete": false}'
Você deverá ver uma resposta com um código de status 201 e o ID da nova tarefa.

Rota de leitura de todos os documentos

Vamos para as rotas de leitura. Começaremos com uma rota que recebe todos os nossos clientes, chamada getTodos. Isso Go para o arquivotodoController.ts.
1// ... existing code
2
3async function getTodos(): Promise<Response> {
4 try {
5 const allTodos = await todos.find().toArray();
6 return new Response(JSON.stringify(allTodos), {
7 headers: { "Content-Type": "application/json" },
8 });
9 } catch (error) {
10 return new Response(JSON.stringify({ error: error.message }), {
11 status: 500,
12 headers: { "Content-Type": "application/json" },
13 });
14 }
15}
16
17export { addTodo, getTodos };
Esta função recupera todos os itens de tarefas da collection todos usando o método finddo MongoDB . Se for bem-sucedido, ele retornará uma resposta JSON contendo todos os Todos com um código de status 200 . Se ocorrer um erro durante o processo, ele o detectará e retornará uma resposta JSON com a mensagem de erro e um código de status 500 . Finalmente, a função é exportada.
Vamos adicionar isso à nossa funçãohandler no arquivoserver.ts.
1import { addTodo, getTodos } from "./controllers/todoController.ts";
2// ... existing code
3
4async function handler(req: Request): Promise<Response> {
5 // ... existing code
6 else if (req.method === "GET" && path === "/api/todos") {
7 return await getTodos();
8 }
9 // ... existing code
10}

Testando a rota de leitura total

Agora, podemos testar nossa funçãogetTodos. Usaremos o comandocurl para enviar uma solicitação GET para nossa rota de leitura.
1curl http://localhost:3000/api/todos
Você deve ver uma resposta com um código de status 200 e uma array JSON de Todos. Observe o id na resposta. Vamos precisar disso para nossa próxima rota.

Leia uma rota de documento único

Em seguida, configuraremos nossa função para ler um único documento. Vamos chamar isso getTodo e, novamente, colocaremos isso no arquivotodoController.ts.
1// ... existing code
2
3async function getTodo(id: string): Promise<Response> {
4 try {
5 const todo = await todos.findOne({ _id: new ObjectId(id) });
6 if (!todo) {
7 return new Response(JSON.stringify({ error: "Todo not found" }), {
8 status: 404,
9 headers: { "Content-Type": "application/json" },
10 });
11 }
12 return new Response(JSON.stringify(todo), {
13 headers: { "Content-Type": "application/json" },
14 });
15 } catch (error) {
16 return new Response(JSON.stringify({ error: error.message }), {
17 status: 500,
18 headers: { "Content-Type": "application/json" },
19 });
20 }
21}
22
23export { addTodo, getTodos, getTodo };
Esta função recupera um único item de tarefa da coleção todos usando o método findOnedo MongoDB com base no idfornecido. Se o item de tarefas for encontrado, ele retornará uma resposta JSON com os dados de tarefas e um código de status 200 . Se o item de tarefa não for encontrado, ele retornará um código de status 404 com uma mensagem de erro. Se ocorrer um erro durante o processo, ele o detectará e retornará uma resposta JSON com a mensagem de erro e um código de status 500 . Finalmente, a função é exportada.
Vamos adicionar isso à nossa funçãohandler no arquivoserver.ts.
1import { addTodo, getTodos, getTodo } from "./controllers/todoController.ts";
2// ... existing code
3
4async function handler(req: Request): Promise<Response> {
5 // ... existing code
6 else if (req.method === "GET" && path.startsWith("/api/todos/")) {
7 const id = path.split("/")[3];
8 return await getTodo(id);
9 }
10 // ... existing code
11}
Nesta rota, precisamos extrair o id do caminho da URL e passá-lo para nossa funçãogetTodo. Podemos fazer isso dividindo o caminho no / e pegando o quarto elemento (índice 3).

Testando a rota única de leitura

Agora, podemos testar nossa funçãogetTodo. Usaremos o comandocurl para enviar uma solicitação GET para nossa rota única de leitura. Lembre-se daquele _id que tiramos do teste anterior? Precisamos usar isso aqui.
1curl http://localhost:3000/api/todos/<...id here...>
2# example:
3# curl http://localhost:3000/api/todos/67005eef3bf67a631efc95f6
Você deverá ver uma resposta com um código de status 200 e um objeto JSON representando a tarefa.

Atualizar rota

Agora que temos documentos, vamos configurar nossa rota de atualização para nos permitir fazer alterações nos documentos existentes. Chamaremos essa função updateTodo.
1// ... existing code
2
3async function updateTodo(id: string, req: Request): Promise<Response> {
4 try {
5 const body = await req.json();
6 const result = await todos.updateOne(
7 { _id: new ObjectId(id) },
8 { $set: body },
9 );
10 if (result.matchedCount === 0) {
11 return new Response(JSON.stringify({ error: "Todo not found" }), {
12 status: 404,
13 headers: { "Content-Type": "application/json" },
14 });
15 }
16 return new Response(JSON.stringify({ updated: result.modifiedCount }), {
17 headers: { "Content-Type": "application/json" },
18 });
19 } catch (error) {
20 return new Response(JSON.stringify({ error: error.message }), {
21 status: 400,
22 headers: { "Content-Type": "application/json" },
23 });
24 }
25}
26
27export { addTodo, getTodos, getTodo, updateTodo };
Esta função atualiza um item de tarefa na coleção todos com base no idfornecido. Ele extrai os dados atualizados do corpo da solicitação, usa o updateOne método MongoDBmethod para modificar o documento existente e retorna uma resposta JSON indicando o número de documentos modificados. Se o item de tarefa não for encontrado, ele retornará um código de status 404 com uma mensagem de erro. Se ocorrer um erro durante o processo, ele o detectará e retornará uma resposta JSON com a mensagem de erro e um código de status 400 . Finalmente, a função é exportada.
Vamos adicionar isso à nossa funçãohandler no arquivoserver.ts.
1import { addTodo, getTodos, getTodo, updateTodo } from "./controllers/todoController.ts";
2// ... existing code
3
4async function handler(req: Request): Promise<Response> {
5 // ... existing code
6 else if (req.method === "PUT" && path.startsWith("/api/todos/")) {
7 const id = path.split("/")[3];
8 return await updateTodo(id, req);
9 }
10 // ... existing code
11}
Desta vez, passaremos o id e a solicitação para nossa funçãoupdateTodo.

Testando a rota de atualização

Agora, podemos testar nossa funçãoupdateTodo. Usaremos o comandocurl para enviar uma solicitação PUT para nossa rota de atualização. Você pode usar o _id dos testes anteriores.
1curl -X PUT http://localhost:3000/api/todos/<...id here...> -H "Content-Type: application/json" -d '{"title": "Updated Todo", "complete": true}'
Você deverá ver uma resposta com um código de status 200 e um objeto JSON indicando o número de documentos modificados.

Excluir rota

Em seguida, configuraremos nossa rota de exclusão. Chamaremos isso deleteTodo.
1// ... existing code
2
3async function deleteTodo(id: string): Promise<Response> {
4 try {
5 const result = await todos.deleteOne({ _id: new ObjectId(id) });
6 if (result.deletedCount === 0) {
7 return new Response(JSON.stringify({ error: "Todo not found" }), {
8 status: 404,
9 headers: { "Content-Type": "application/json" },
10 });
11 }
12 return new Response(JSON.stringify({ deleted: result.deletedCount }), {
13 status: 200,
14 headers: { "Content-Type": "application/json" },
15 });
16 } catch (error) {
17 return new Response(JSON.stringify({ error: error.message }), {
18 status: 400,
19 headers: { "Content-Type": "application/json" },
20 });
21 }
22}
23
24export { addTodo, getTodos, getTodo, updateTodo, deleteTodo };
Esta função exclui um item de tarefa da coleção todos com base no idfornecido. Ele usa o métododeleteOne MongoDB para remover o documento e retorna um código de status 200 em caso de sucesso, junto com uma resposta JSON indicando o número de documentos excluídos. Se o item de tarefa não for encontrado, ele retornará um código de status 404 com uma mensagem de erro. Se ocorrer um erro durante o processo, ele o detectará e retornará uma resposta JSON com a mensagem de erro e um código de status 400 . Finalmente, a função é exportada.
Vamos adicionar isso à nossa funçãohandler no arquivoserver.ts.
1import { addTodo, getTodos, getTodo, updateTodo, deleteTodo } from "./controllers/todoController.ts";
2// ... existing code
3
4async function handler(req: Request): Promise<Response> {
5 // ... existing code
6 else if (req.method === "DELETE" && path.startsWith("/api/todos/")) {
7 const id = path.split("/")[3];
8 return await deleteTodo(id);
9 }
10 // ... existing code
11}
Desta vez, passaremos o id para nossa funçãodeleteTodo.

Testando a rota de exclusão

Agora, podemos testar nossa funçãodeleteTodo. Usaremos o comandocurl para enviar uma solicitação DELETE para nossa rota de exclusão.
1curl -X DELETE http://localhost:3000/api/todos/<...id here...>
Você deverá ver uma resposta com um código de status 204 .

Bônus: Rota de agregação

Vamos criar mais uma rota de treinamento. Este demonstrará um pipeline de agregação básico. Chamaremos isso getIncompleteTodos.
1// ... existing code
2
3async function getIncompleteTodos(): Promise<Response> {
4 try {
5 const pipeline = [
6 { $match: { complete: false } },
7 { $count: "incomplete" },
8 ];
9 const result = await todos.aggregate(pipeline).toArray();
10 const incompleteCount = result[0]?.incomplete || 0;
11 return new Response(JSON.stringify({ incompleteCount }), {
12 headers: { "Content-Type": "application/json" },
13 });
14 } catch (error) {
15 return new Response(JSON.stringify({ error: error.message }), {
16 status: 500,
17 headers: { "Content-Type": "application/json" },
18 });
19 }
20}
21
22export { addTodo, deleteTodo, getIncompleteTodos, getTodo, getTodos, updateTodo };
Essa função executa um pipeline de agregação na collection Todos para contar o número de Todos incompletos. Ele usa o estágio$match para filtrar todos em que complete é false, e o estágio$count para contar o número de documentos que correspondem a esses critérios. O resultado é retornado como uma resposta JSON com um código de status 200 . Se ocorrer um erro durante o processo, ele o capturará e retornará uma resposta JSON com a mensagem de erro e um código de status 500 .
Vamos adicionar isso à nossa funçãohandler no arquivoserver.ts.
1import { addTodo, deleteTodo, getTodo, getTodos, updateTodo, getIncompleteTodos } from "./controllers/todoController.ts";
2// ... existing code
3
4async function handler(req: Request): Promise<Response> {
5 // ... existing code
6 else if (req.method === "GET" && path === "/api/todos/incomplete/count") {
7 return await getIncompleteTodos();
8 }
9 // ... existing code
10}
Observe que esta rota é posicionada acima da rota getTodona funçãohandler. Isso ocorre porque a rotagetTodo usa um caminho que corresponde ao início da rotagetIncompleteTodos. Se colocarmos getIncompleteTodos abaixo getTodo na função handler, o servidor não conseguiria distinguir entre as duas rotas e sempre corresponderia à rotagetTodo .
Como alternativa, poderíamos usar uma expressão regular para corresponder ao caminho e colocar getIncompleteTodos abaixo getTodo na função handler. Isso nos permitiria usar um caminho mais específico para nossa rota de agregação .

Testando a rota de agregação

Agora, podemos testar nossa funçãogetIncompleteTodos. Usaremos o comandocurl para enviar uma solicitação GET para nossa rota de agregação .
1curl http://localhost:3000/api/todos/incomplete/count
Você deve ver uma resposta com um código de status 200 e um objeto JSON contendo a contagem de Todos incompletos.

Conclusão

Neste tutorial, criamos um servidor Deno que usa o driver MongoDB para criar, ler, atualizar e excluir documentos (CRUD) em nosso banco de banco de dados MongoDB . Adicionamos uma rota de desconto para demonstrar o uso de um pipeline de agregação com o driver MongoDB . E agora?
O código completo pode ser encontrado no repositórioComeçando com Deno e MongoDB . Você deve ser capaz de usar isso como ponto de partida para seu próximo projeto e modificá-lo para atender às suas necessidades.
Eu gostaria muito de ouvir seus comentários ou perguntas. Vamos conversar na MongoDB Community.

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Início rápido

Tutorial de fluxos de alterações e triggers com o Node.js


Aug 24, 2023 | 17 min read
Tutorial

Como desenvolver um aplicativo web com funções sem servidor da Netlify e MongoDB


Aug 30, 2024 | 6 min read
Tutorial

Autenticação NextAuth.js com MongoDB


Aug 30, 2024 | 10 min read
Tutorial

Criar um pipeline de dados para o fluxo de alterações do MongoDB usando a assinatura Pub/Sub do BigQuery


Apr 02, 2024 | 5 min read
Sumário