Introdução ao Bun e ao MongoDB
Avalie esse Início rápido
O JavaScript percorreu um longo caminho desde sua criação nos 1990s. Agora é muito mais do que uma linguagem de programação de front-end usada para criar efeitos sofisticados em uma página da web. Ele desenvolveu e agora é uma linguagem de back-end de nível empresarial com o Node.js.
Nos últimos anos, existiram algumas alternativas para usar JavaScript backend. Além do Node.js, tempos de execução como o Deno visam corrigir alguns dos problemas que os engenheiros têm com o JavaScript.
Este artigo se concentrará no Bun, um tempo de execução de nova geração para executar JavaScript ou TypeScript em um servidor.
Bun é um novo tempo de execução projetado para velocidade. Ele é otimizado para servidores da Web de grande escala e inclui ferramentas modernas, como um interpretador TypeScript integrado, um executor de testes e um gerenciador de pacotes.
É totalmente compatível com o Node.js, e os pacotes que você usa com seus outros projetos também devem funcionar com o Bun.
Neste início rápido, mostraremos como usar o Bun para escrever uma API CRUD simples para ler e escrever em uma collection do MongoDB. Este guia simples usa os pacotes integrados do Bun para criar um servidor web e o driver MongoDB Node.js para se conectar ao banco de dados.
- MongoDB: use sua própria instância do MongoDB ou crie um Atlas cluster gratuito para sempre
- (Opcional) Conjunto de dados de amostra: Se quiser começar com alguns dados, você pode importar o conjunto de dados de amostra para o seu Atlas cluster gratuito
Se você quiser começar com um modelo MongoDB e Bun, você pode executar os seguintes comandos.
1 git clone https://github.com/mongodb-developer/bun-with-mongodb 2 cd bun-with-mongodb 3 bun i 4 echo "MONGODB_URI=<your_atlas_connection_string>" > .env 5 bun run index.ts
Como em um projeto típico do Node.js, você precisará começar inicializando seu aplicativo. Isso criará os andaimes necessários para seu aplicativo.
Manteremos nosso projeto muito simples para este tutorial e usaremos o servidor da web
Bun.serve
integrado. A única dependência necessária é o pacotemongodb
do npm.Em uma nova pasta, execute os seguintes comandos para inicializar seu projeto e instalar as dependências necessárias.
1 bun init 2 bun add mongodb
Se você observar os arquivos em sua pasta, agora deverá ver um arquivo
index.ts
, junto com as dependências instaladas na pastanode_modules
. Se você é um desenvolvedor do Node.js, isso deve parecer muito familiar.Nosso aplicativo será uma API simples com cinco rotas básicas.
- POST /movies: Pegará um filme no corpo e o inserirá na collection
- GET /movies: recupera os últimos 10 filmes que foram adicionados ao banco de dados
- GET /movies/:id: Retorna um único filme por id
- PUT /movies/:id: Atualiza o filme especificado no caminho com o corpo
- DELETE /movies/:id: Exclui o filme especificado no caminho
Todas as rotas serão tratadas no arquivo
index.ts
principal . A lógica para se conectar ao banco de dados está localizada no arquivoutils/db.ts
, e todas as ações executadas no banco de dados podem ser encontradas no arquivocontollers/movies.ts
.Vamos começar com um servidor Web básico. O Bun tem vários pacotes incorporados, incluindo
Bun.serve
. Esse pacote contém todos os componentes necessários para criar um servidor Web básico.Substitua o conteúdo do arquivo
index.ts
pelo seguinte código:1 const server = Bun.serve({ 2 async fetch(req) { 3 const url = new URL(req.url); 4 const method = req.method; 5 if (url.pathname === "/") return new Response("Welcome to the movie database"); 6 return new Response("404!"); 7 }, 8 }); 9 console.log(`Listening on http://localhost:${server.port} ...`);
Agora, inicie o aplicativo com o seguinte comando.
1 bun --watch run index.ts
Usar
--watch
recarregará automaticamente o servidor sempre que um arquivo for alterado. Isso é muito conveniente enquanto você está no modo de desenvolvimento.Por padrão, o Bun usará a porta 3000 com
Bun.serve
. Você pode alterar isso definindo a variável de ambientePORT
do seu shell. O Bun usará automaticamente a porta definida nessa variável de ambiente.Para testá-lo, use o Postman ou um serviço semelhante, seu navegador da Web ou uma ferramenta CLI, como o curl. Para este artigo, usarei curl.
Execute o seguinte comando em uma nova janela de terminal para testar seu aplicativo.
1 curl localhost:3000
Você deve ver uma mensagem dizendo Bem-vindo ao banco de dados de filmes.
Parabéns! Você criou com sucesso um servidor da web com o Bun — hora de se conectar a um MongoDB database.
A conexão com o MongoDB database será tratada por um arquivo chamado
utils/db.ts
. Aqui, você usará o MongoDB (já instalado com o bun add
) para criar um MongoClient
. Em seguida, você se conectará à coleçãomovies
e exportará essa coleção para seus comandos usarem.Em um ambiente de produção, você também adicionaria alguma lógica para garantir que a conexão com o banco de dados esteja funcionando corretamente, mas, para os fins deste artigo, vamos nos ater ao básico.
Comece com um novo arquivo chamado
utils/db.ts
.1 import { MongoClient } from "mongodb"; 2 let MONGODB_URI = process.env.MONGODB_URI; 3 if (!MONGODB_URI) { 4 throw new Error("Please define the MONGODB_URI environment variable inside .env"); 5 } 6 7 const client: MongoClient = await MongoClient.connect(MONGODB_URI); 8 const moviesCollection = client.db("sample_mflix").collection("movies"); 9 10 export { 11 moviesCollection 12 }
Você notará que a string de conexão para MongoDB (
MONGODB_URI
) é lida diretamente das variáveis de ambiente. Com o Bun, não há necessidade de usar um pacote para injetar essas variáveis em seu aplicativo. Ele será lido automaticamente do seu arquivo.env
.Então, vamos em frente e crie um arquivo
.env
na pasta raiz do seu projeto.1 MONGODB_URI=<your_atlas_connection_string>
ótimo trabalho! Agora você pode se conectar ao banco de dados. O código não é invocado em lugar nenhum, portanto não há muito para testar. Vejamos como podemos usar essa collection para realizar operações CRUD.
Bun usa o TypeScript pronto para usar. Você também pode optar por usar JavaScript simples, mas como estamos aproveitando o TS para esse aplicativo, precisaremos criar um modelo de filme para informar ao nosso aplicativo como é um filme. Para esse início rápido, nossos filmes terão apenas um título, alguns atores e o ano em que foram lançados.
Criar um arquivo
models/movies.ts
:1 import type { ObjectId } from "mongodb"; 2 3 // Create a custom type for our movies 4 export interface Movie { 5 _id?: ObjectId, 6 title: string, 7 actors: string[], 8 year?: number, 9 }
Observe como também estamos usando o tipo ObjectId nativo do MongoDB aqui para o campo
_id
.Como o MongoDB é um banco de dados, seus documentos podem conter muitas propriedades diferentes, incluindo arrays de strings, como fizemos neste exemplo. Estamos mantendo as coisas simples aqui, mas podem ser objetos ainda mais complexos, como uma array de objetos atores. Você pode descobrir mais sobre o document model na MongoDB University.
Agora que você tem seu modelo, você pode criar seu controlador de filmes.
Você tem um cliente conectado ao seu banco de dados e um modelo que gerencia o formato dos seus dados. Agora é hora de fazer essas operações CRUD no seu banco de dados.
Essas operações são todas tratadas no controlador de filmes. Esse controlador se comunica com o banco de dados e retorna o resultado da operação ao nosso servidor.
Crie um novo arquivo chamado
controllers/movies.ts
1 // Import the Movies collection 2 import { moviesCollection } from '../utils/db.ts'; 3 // Import the necessary types 4 import type { ObjectId } from "mongodb"; 5 import type { Movie } from "../models/movies.ts"; 6 7 class MovieController { 8 /** 9 * CRUD operations for the movies collection 10 */ 11 12 // Add a movie 13 public async addMovie(movie: Movie) { 14 return await moviesCollection.insertOne(movie); 15 } 16 17 // Fetch the latest ten movies 18 public async getMovies() { 19 return await moviesCollection.find({}).sort({_id: -1}).limit(10).toArray(); 20 } 21 22 // Fetch one movie 23 public async getMovieById(_id: ObjectId) { 24 return await moviesCollection.findOne({_id}); 25 } 26 27 // Update the movies 28 public async updateMovie(_id: ObjectId, movie: Movie) { 29 return await moviesCollection.updateOne({ _id }, { $set: movie }); 30 } 31 32 // Delete a single movie 33 public async deleteMovie(movieId: ObjectId) { 34 return await moviesCollection.deleteOne({ _id: movieId }); 35 } 36 } 37 38 export default MovieController;
Este controlador lista as operações mais básicas que podem ser executadas no banco de dados. Se você quiser saber mais sobre essas operações, recomendamos o Tutorial do MongoDB e Node.js - Operações CRUD na Central do Desenvolvedor. Você também pode consultar nossos Docs para os métodosfind, findOne, insertOne, updateOnee deleteOne de nosso driver em nossos Docs.
Se você precisar de operações mais avançadas, como agrupamento ou facet, também deverá consultar a estrutura de agregação do MongoDB.
Agora que você tem todo o código para executar as operações CRUD na sua collection, é hora de retornar ao servidor e começar com o roteamento da solicitação.
Agora é hora de unir tudo em nosso arquivo
index.ts
, que é o código do próprio servidor. Nesse arquivo, importaremos os componentes necessários, executaremos algum roteamento e chamaremos os métodos apropriados do nosso controlador.Primeiro, importe os arquivos necessários na parte superior do arquivo
index.ts
.1 // Import the Movies functions 2 import { ObjectId } from "mongodb"; 3 import MovieController from "./controllers/movies.ts"; 4 import type { Movie } from "./models/movies.ts";
Isso importará o controlador de filme que criamos anteriormente e os tipos necessários que usaremos.
Em seguida, reescreva sua lógica
Bun.serve
para dar suporte a várias rotas.1 const server = Bun.serve({ 2 async fetch(req) { 3 const url = new URL(req.url); 4 const method = req.method; 5 if (url.pathname === "/") return new Response("Welcome to the movie database"); 6 7 // Routes for the API 8 let moviesRoutes = new RegExp(/^\/movies\/?(.*)/) 9 const movies = new MovieController(); 10 11 // POST /movies 12 if (url.pathname.match(moviesRoutes) && method === "POST") { 13 return new Response("Not implemented yet!"); 14 } 15 16 // GET /movies and GET /movies/:id 17 if (url.pathname.match(moviesRoutes) && method === "GET") { 18 return new Response("Not implemented yet!"); 19 } 20 21 // PUT /movies/:id 22 if (url.pathname.match(moviesRoutes) && method === "PUT") { 23 return new Response("Not implemented yet!"); 24 } 25 26 // DELETE /movies/:id 27 28 if (url.pathname.match(moviesRoutes) && method === "DELETE") { 29 return new Response("Not implemented yet!"); 30 } 31 32 return new Response("404!"); 33 }, 34 });
Nesse caso, escrevemos uma expressão regular que corresponde a qualquer rota que comece com
/movies
. Também examinamos o método enviado na solicitação. Se a rota não corresponder a uma das rotas de controle ou começar com /movies
, retornaremos a mensagem 404!
.Você pode testar essas rotas usando curl.
1 curl localhost:3000/movies 2 curl localhost:3000/movies -X POST 3 curl localhost:3000/movies -X PUT 4 curl localhost:3000/movies -X DELETE 5 curl localhost:3000/movies -X PATCH # 404!
Agora é a hora de conectar seu servidor ao seu banco de dados.
Podemos finalmente colocar tudo junto.
Para adicionar um filme ao banco de dados, você deve ler o corpo da solicitação usando
req.json()
e enviar esse filme para o seu controlador.1 // POST /movies 2 if (url.pathname.match(moviesRoutes) && method === "POST") { 3 const movie: Movie = await req.json(); 4 return Response.json(await movies.addMovie(movie)); 5 }
Aqui, estamos supondo que a solicitação sempre corresponda a um objeto de filme, mas, na realidade, você deseja validar isso. É provável que você também queira tratar quaisquer erros ao inserir os dados.
Depois que o código estiver pronto, você poderá adicionar um novo filme à coleção.
1 curl localhost:3000/movies -X POST --data '{"title": "New Movie"}'
Você verá uma confirmação de que a operação funcionou e receberá o novo
insertedId
.Nossa rota
GET /movies
é um pouco mais complexa, pois lidará com/movies
para recuperar uma lista de filmes 10 e /movies/:id
para recuperar um único filme. Para descobrir qual usar, procuraremos a existência de um parâmetro após o componente/movies
.Se estiver lá, pegaremos essa string e a converteremos para um ObjectId antes de enviá-la para o controlador de filmes. Se o parâmetro não estiver lá, usamos o método
getMovies
do controlador de filmes para retornar o 10 mais recente.1 // GET /movies and GET /movies/:id 2 if (url.pathname.match(moviesRoutes) && method === "GET") { 3 const routeParams = url.pathname.split("/"); 4 if (routeParams[2]) { 5 const movieId: ObjectId = new ObjectId(routeParams[2]); 6 return Response.json(await movies.getMovieById(movieId)); 7 } else { 8 return Response.json(await movies.getMovies()); 9 } 10 }
Agora você pode testar essas novas rotas. Observe que você precisará alterar a rota do segundo comando curl para corresponder a um ObjectId da sua coleção de filmes existente.
1 curl localhost:3000/movies 2 curl localhost:3000/movies/668e855f76f86976e4045ae9
Usamos o mesmo truque para atualizar um filme para localizar o parâmetro id e convertê-lo em um ObjectId. Em seguida, passamos o corpo da solicitação para o método de atualização do nosso controlador de filmes.
1 // PUT /movies/:id 2 if (url.pathname.match(moviesRoutes) && method === "PUT") { 3 const movieId: ObjectId = new ObjectId(url.pathname.split("/")[2]); 4 const movie: Movie = await req.json(); 5 return Response.json(await movies.updateMovie(movieId, movie)); 6 }
Por fim, para excluir um filme, extraímos o ID do filme do nome do caminho e o passamos para a função de exclusão do nosso controlador de filme.
1 // DELETE /movies/:id 2 if (url.pathname.match(moviesRoutes) && method === "DELETE") { 3 const movieId: ObjectId = new ObjectId(url.pathname.split("/")[2]); 4 return Response.json(await movies.deleteMovie(movieId)); 5 }
É isso ai! Você tem um servidor totalmente funcional em execução no Bun que pode se conectar à sua MongoDB collection e executar operações CRUD básicas. Você pode encontrar todo o código em nosso repositório doGithub .
Para implantar esse código na produção, você deve adicionar mais robustez ao seu código. Você também precisaria tratar os erros e retornar os códigos de erro apropriados quando um documento não for encontrado. Mas este é um bom ponto de partida para seu primeiro aplicativo.
Se tiver alguma dúvida, registre-se em nossos fóruns da comunidade e use o formulário abaixo para entrar em contato conosco!
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.