Bun e HTMX são duas das coisas mais interessantes que acontecem em software atualmente. Bun é uma plataforma JavaScript do lado do servidor incrivelmente rápida e completa, e HTMX é uma extensão HTML usada para criar interfaces simples e poderosas. Neste artigo, usaremos essas duas excelentes ferramentas juntas para desenvolver um aplicativo full-stack que usa MongoDB para armazenamento de dados e Elysia como servidor HTTP.
A pilha de tecnologia
Nosso foco neste artigo é como os quatro componentes principais de nossa pilha de tecnologia interagem. Os componentes são Bun, HTMX, Elysia e MongoDB. Essa pilha oferece uma configuração rápida, fácil de configurar e ágil para alterar.
- Bun é um tempo de execução JavaScript, empacotador, gerenciador de pacotes e executor de testes.
- Elysia é um servidor HTTP de alto desempenho semelhante ao Express, mas desenvolvido para Bun.
- HTMX oferece uma nova abordagem para adicionar interatividade refinada ao HTML.
- MongoDB é o principal armazenamento de dados orientado a documentos NoSQL.
Observe que este artigo tem duas partes. Na segunda metade, incorporaremos o Pug, o mecanismo de modelagem HTMX, que usaremos para desenvolver algumas interações sofisticadas de front-end.
Instalação e configuração
Você precisará instalar o Bun.js, o que é fácil de fazer. Também executaremos o MongoDB como um serviço junto com o Bun em nossa máquina de desenvolvimento. Você pode ler sobre como instalar e configurar o MongoDB aqui. Depois de instalar esses pacotes, tanto o bun -v
e mongod -version
os comandos devem funcionar na linha de comando.
A seguir, vamos começar um novo projeto:
$ bun create elysia iw-beh
Isso diz bun
para criar um novo projeto usando o modelo Elysia. A modelo no Bun é uma maneira conveniente de iniciar projetos usando o create
comando. Bun pode funcionar como Node, sem nenhuma configuração, mas é bom ter a configuração. (Saiba mais sobre os modelos de Bun aqui.)
Agora, vá para o novo diretório: $ cd iw-beh
.
E execute o projeto como está: $ bun run src/index.js
.
Este último comando diz bun
para executar o src/index.js
arquivo. O src/index.js
file é o código para iniciar um servidor Elysia simples:
import { Elysia } from "elysia";
const app = new Elysia()
.get("https://www.infoworld.com/", () => "Hello Elysia")
.listen(3000);
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);
Neste arquivo, importamos Elysia e o usamos para instanciar um novo servidor que escuta na porta 3000 e possui um único GET
ponto final na raiz. Este endpoint retorna uma string de texto: “Hello Elysia.” O modo como tudo isso funciona é semelhante em espírito ao Express.
Se você visitar localhost:3000
você receberá uma saudação simples:
Agora que temos o Elysia rodando, vamos adicionar o static
plugar. Elysia possui vários plugins para lidar com cenários comuns. Neste caso, queremos servir algum HTML do disco. O static
plugin é exatamente o que precisamos:
$ bun add @elysiajs/static
Agora o servidor Elysia executando o static
plugin deve servir tudo no iw-beh/public
diretório. Se colocarmos um arquivo HTML simples lá e visitarmos localhost:3000/ public
veremos seu conteúdo.
A magia do HTML
A seguir, vamos adicionar uma página HTML ao index.html
. Aqui está um simples da página inicial do HTMX:
<script src="https://unpkg.com/[email protected]"></script>
<button hx-post="/clicked"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML">
Click Me!
</button>
Esta página exibe um botão. Quando clicado, o botão faz uma chamada ao servidor para o /clicked
endpoint, e o botão é substituído pelo que estiver na resposta. Ainda não há nada lá, então atualmente não faz nada.
Mas observe que tudo isso ainda é HTML. Estamos fazendo uma chamada de API e realizando uma alteração refinada no DOM sem qualquer JavaScript. (O trabalho está sendo feito pelo JavaScript no htmx.org
biblioteca que acabamos de importar, mas a questão é que não precisamos nos preocupar com isso.)
HTMX fornece uma sintaxe HTML que faz essas coisas usando apenas três atributos de elemento:
hx-post
envia uma postagem quando é acionada para uma solicitação AJAX.hx-target
dizhx-post
quais eventos executam uma solicitação AJAX.hx-swap
diz o que fazer quando ocorre um evento AJAX. Neste caso, substitua o elemento presente pela resposta.
Isso é muito simples!
Elysia e MongoDB
Agora vamos criar um endpoint no Elysia que escreverá algo no MongoDB. Primeiro, adicionaremos o driver MongoDB para Bun:
bun add mongodb
A seguir, modifique src.index.ts
assim:
import { Elysia } from "elysia";
import { staticPlugin } from '@elysiajs/static';
const { MongoClient } = require('mongodb');
const app = new Elysia()
.get("https://www.infoworld.com/", () => "Hello Elysia")
.get("/db", async () => {
const url = "mongodb://127.0.0.1:27017/quote?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.8.0";
const client = new MongoClient(url, { useUnifiedTopology: true });
try {
await client.connect();
const database = client.db('quote');
const collection = database.collection('quotes');
const stringData = "Thought is the grandchild of ignorance.";
const result = await collection.insertOne({"quote":stringData});
console.log(`String inserted with ID: ${result.insertedId}`);
} catch (error) {
console.error(error);
} finally {
await client.close();
}
return "OK";
})
.use(staticPlugin())
.listen(3000);
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);
Neste código, adicionamos um /db
endpoint que se comunica com o MongoDB. No momento, ele apenas grava uma cotação no banco de dados de cotações, dentro da coleção de cotações. Você pode testar isso diretamente acessando localhost:3000/db
. Então, você pode verificar se os dados estão no MongoDB:
$ mongosh
test> use quote
switched to db quote
quote> db.quotes.find()
(
{
_id: ObjectId("65ba936fd59e9c265cc8c092"),
quote: 'Thought is the grandchild of ignorance.',
author: 'Swami Venkatesananda'
}
)
Crie uma tabela HTMX
Agora que estamos nos conectando ao banco de dados pelo front end, vamos criar uma tabela para gerar as cotações existentes. Como um teste rápido, adicionaremos um endpoint ao servidor:
.get("/quotes", async () => {
const data = (
{ name: 'Alice' },
{ name: 'Bob' },
);
// Build the HTML list structure
let html="<ul>";
for (const item of data) {
html += `<li>${item.name}</li>`;
}
html += '</ul>';
return html
})
E então use-o em nosso index.html
:
<ul id="data-list"></ul>
<button hx-get="/quotes" hx-target="#data-list">Load Data</button>
Agora, quando você carrega /public/index.html
e clique no botão, a lista enviada do servidor é exibida. Observe que a emissão de HTML do endpoint é diferente do padrão JSON comum. Isso é intencional. Estamos em conformidade com os princípios RESTful aqui. HTMX possui plug-ins para trabalhar com endpoints JSON, mas isso é mais idiomático.
Em nosso endpoint, estamos apenas criando manualmente o HTML. Em uma aplicação real, provavelmente usaríamos algum tipo de estrutura de modelagem JavaScript-HTML para tornar as coisas mais gerenciáveis.
Agora podemos recuperar os dados do banco de dados:
.get("/quotes", async () => {
const url = "mongodb://127.0.0.1:27017/quote?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.8.0";
const client = new MongoClient(url, { useUnifiedTopology: true });
try {
await client.connect();
const database = client.db('quote');
const collection = database.collection('quotes');
const quotes = await collection.find().toArray();
// Build the HTML table structure
let html="<table border="1">";
html += '<tr><th>Quote</th><th>Author</th></tr>';
for (const quote of quotes) {
html += `<tr><td>${quote.quote}</td><td>${quote.author}</td></tr>`;
}
html += '</table>';
return html;
} catch (error) {
console.error(error);
return "Error fetching quotes";
} finally {
await client.close();
}
})
Neste endpoint, recuperamos todas as cotações existentes no banco de dados e as retornamos como uma tabela HTML simples. (Observe que em uma aplicação real, extrairíamos o trabalho de conexão do banco de dados para um local central.)
Você verá algo assim:
Esta captura de tela mostra a única linha que inserimos quando atingimos o /db
ponto final mais cedo.
Agora, vamos adicionar a capacidade de criar uma nova cotação. Aqui está o código de back-end (src/index.ts
):
app.post("/add-quote", async (req) => {
const url = "mongodb://127.0.0.1:27017/quote?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.8.0";
try {
const client = new MongoClient(url, { useUnifiedTopology: true });
await client.connect();
const database = client.db('quote');
const collection = database.collection('quotes');
const quote = req.body.quote;
const author = req.body.author;
await collection.insertOne({ quote, author });
return "Quote added successfully";
} catch (error) {
console.error(error);
return "Error adding quote";
} finally {
await client.close();
}
})
E aqui está o front-end (public/index.html
):
<form hx-post="/add-quote">
<input type="text" name="quote" placeholder="Enter quote">
<input type="text" name="author" placeholder="Enter author">
<button type="submit">Add Quote</button>
Quando você insere um autor e uma citação e clica Adicionar cotação ele será adicionado ao banco de dados. Se você clicar Carregar dados, você verá sua atualização na lista. Deve ser algo assim:
Se você observar o servidor e o cliente do aplicativo até agora, verá que estamos fazendo o mínimo de trabalho. A maior coisa que o HTMX simplificou aqui foi o envio do formulário. O hx-post
atributo substitui todo o trabalho de retirar os dados do formulário, empacotá-los em JSON e enviá-los com fetch()
ou algo semelhante.
Conclusão
À medida que as coisas ficam mais complexas, você começa a depender de JavaScript no cliente, mesmo com HTMX. Por exemplo, edição de linha embutida. Algumas coisas para as quais você espera usar JavaScript, como inserir as novas linhas diretamente na tabela, podem ser feitas com a troca de HTMX. O HTMX permite que você faça muito com sua sintaxe simples e depois retorne ao JavaScript quando necessário.
A maior mudança mental está na geração de HTMX a partir do servidor. Você pode escolher entre vários mecanismos de modelagem HTML ou JavaScript de última geração para tornar isso muito mais fácil. Quando você estiver acostumado a trabalhar com HTMX, será muito fácil. Essencialmente, você eliminou toda a camada de conversão JSON da pilha.
Acabamos de fazer a combinação mais rápida de bun, Elysia, HTMX e MongoDB, mas você deve pelo menos ter uma ideia dessa pilha. Os componentes funcionam bem juntos sem qualquer atrito desnecessário. Bun, Elysia e MongoDB fazem seu trabalho silenciosamente, enquanto HTMX exige um pouco mais de reflexão se você estiver mais acostumado com APIs JSON. Encontre o código deste artigo em meu repositório GitHub. Trabalharemos mais com este exemplo no próximo artigo, onde usaremos o Pug para adicionar algumas interações sofisticadas à mistura.