Criando uma API Node.js Express para converter Markdown em HTML

Publicados: 2022-03-10
Resumo rápido ↬ Aprenda a usar o Node.js e a estrutura Express para criar um endpoint de API — no contexto da criação de um aplicativo que converte a sintaxe Markdown em HTML.

Markdown é uma linguagem de marcação de texto leve que permite que o texto marcado seja convertido em vários formatos. O objetivo original de criar o Markdown era permitir que as pessoas “escrevessem usando um formato de texto simples fácil de ler e escrever” e, opcionalmente, convertê-lo em XHTML (ou HTML) estruturalmente válido. Atualmente, com o WordPress suportando Markdown, o formato tornou-se ainda mais amplamente utilizado.

O objetivo de escrever o artigo é mostrar como usar o Node.js e a estrutura Express para criar um endpoint de API. O contexto no qual aprenderemos isso é construindo um aplicativo que converte a sintaxe Markdown em HTML. Também adicionaremos um mecanismo de autenticação à API para evitar o uso indevido de nosso aplicativo.

Um aplicativo Markdown Node.js

Nosso pequeno aplicativo, que chamaremos de 'Markdown Converter', nos permitirá postar texto no estilo Markdown e recuperar uma versão HTML. O aplicativo será criado usando o framework Node.js Express e suporta autenticação para solicitações de conversão.

Construiremos o aplicativo em pequenos estágios — inicialmente criando um scaffold usando o Express e, em seguida, adicionando vários recursos, como autenticação, à medida que avançamos. Então, vamos começar com o estágio inicial de construção do aplicativo criando um scaffold.

Mais depois do salto! Continue lendo abaixo ↓

Etapa 1: Instalando o Express

Supondo que você já tenha instalado o Node.js em seu sistema, crie um diretório para armazenar seu aplicativo (vamos chamá-lo de “ markdown-api ”) e mude para esse diretório:

 $ mkdir markdown-api $ cd markdown-api

Use o comando npm init para criar um arquivo package.json para seu aplicativo. Este comando solicita várias coisas, como o nome e a versão do seu aplicativo.

Por enquanto, basta pressionar Enter para aceitar os padrões para a maioria deles. Eu usei o arquivo de ponto de entrada padrão como index.js , mas você pode tentar app.js ou algum outro dependendo de suas preferências.

Agora instale o Express no diretório markdown-api e salve-o na lista de dependências:

 $ npm install express --save

Crie um arquivo index.js no diretório atual ( markdown-api ) e adicione o seguinte código para testar se a estrutura Express está instalada corretamente:

 Const express = require('express'); var app = express(); app.get('/', function(req, res){ res.send('Hello World!'); }); app.listen(3000);

Agora navegue até a URL https://localhost:3000 para verificar se o arquivo de teste está funcionando corretamente. Se tudo estiver em ordem, veremos um Hello World!' saudação no navegador e podemos continuar a construir uma API base para converter Markdown em HTML.

Estágio 2: criando uma API base

O objetivo principal de nossa API será converter texto em uma sintaxe Markdown para HTML. A API terá dois endpoints:

  • /login
  • /convert

O endpoint de login permitirá que o aplicativo autentique solicitações válidas enquanto o endpoint convert converterá (obviamente) Markdown em HTML.

Abaixo está o código da API base para chamar os dois endpoints. A chamada de login apenas retorna uma string “Autenticada”, enquanto a chamada de convert retorna qualquer conteúdo Markdown que você enviou ao aplicativo. O método home apenas retorna um 'Hello World!' corda.

 const express = require("express"); const bodyParser = require('body-parser'); var app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.get('/', function(req, res){ res.send('Hello World!'); }); app.post('/login', function(req, res) { res.send("Authenticated"); }, ); app.post("/convert", function(req, res, next) { console.log(req.body); if(typeof req.body.content == 'undefined' || req.body.content == null) { res.json(["error", "No data found"]); } else { res.json(["markdown", req.body.content]); } }); app.listen(3000, function() { console.log("Server running on port 3000"); });

Usamos o middleware body-parser para facilitar a análise de solicitações de entrada para os aplicativos. O middleware disponibilizará todas as solicitações recebidas na propriedade req.body . Você pode ficar sem o middleware adicional, mas adicioná-lo torna muito mais fácil analisar vários parâmetros de solicitação de entrada.

Você pode instalar body-parser simplesmente usando npm:

 $ npm install body-parser

Agora que temos nossas funções de stub fictícias, usaremos o Postman para testar o mesmo. Vamos começar com uma breve visão geral do Postman.

Visão geral do carteiro

O Postman é uma ferramenta de desenvolvimento de API que facilita a criação, modificação e teste de terminais de API a partir de um navegador ou baixando um aplicativo de desktop (a versão do navegador agora está obsoleta). Ele tem a capacidade de fazer vários tipos de solicitações HTTP, ou seja, GET, POST, PUT, PATCH. Está disponível para Windows, macOS e Linux.

Aqui está uma amostra da interface do Postman:

Interface do carteiro
(Visualização grande)

Para consultar um endpoint de API, você precisará executar as seguintes etapas:

  1. Insira o URL que você deseja consultar na barra de URL na seção superior;
  2. Selecione o método HTTP à esquerda da barra de URL para enviar a solicitação;
  3. Clique no botão 'Enviar'.

O Postman enviará a solicitação para o aplicativo, recuperará todas as respostas e a exibirá na janela inferior. Este é o mecanismo básico de como usar a ferramenta Postman. Em nossa aplicação, também teremos que adicionar outros parâmetros à solicitação, que serão descritos nas seções a seguir.

Usando o carteiro

Agora que vimos uma visão geral do Postman, vamos continuar usando-o para nosso aplicativo.

Inicie seu aplicativo markdown-api na linha de comando:

 $ node index.js

Para testar o código da API base, fazemos chamadas de API para o aplicativo do Postman. Observe que usamos o método POST para passar o texto a ser convertido para o aplicativo.

Atualmente, o aplicativo aceita o conteúdo Markdown para converter por meio do parâmetro content POST. Isso nós passamos como um formato codificado de URL. O aplicativo, atualmente, retorna a string literalmente em um formato JSON — com o primeiro campo sempre retornando a markdown de string e o segundo campo retornando o texto convertido. Posteriormente, quando adicionarmos o código de processamento do Markdown, ele retornará o texto convertido.

Estágio 3: Adicionando o Conversor de Markdown

Com o scaffold do aplicativo agora construído, podemos examinar a biblioteca JavaScript Showdown que usaremos para converter Markdown em HTML. Showdown é um conversor bidirecional de Markdown para HTML escrito em JavaScript que permite converter Markdown para HTML e vice-versa.

Testando com o Postman
(Visualização grande)

Instale o pacote usando npm:

 $ npm install showdown

Depois de adicionar o código de confronto necessário ao scaffold, obtemos o seguinte resultado:

 const express = require("express"); const bodyParser = require('body-parser'); const showdown = require('showdown'); var app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); converter = new showdown.Converter(); app.get('/', function(req, res){ res.send('Hello World!'); }); app.post('/login', function(req, res) { res.send("Authenticated"); }, ); app.post("/convert", function(req, res, next) { if(typeof req.body.content == 'undefined' || req.body.content == null) { res.json(["error", "No data found"]); } else { text = req.body.content; html = converter.makeHtml(text); res.json(["markdown", html]); } }); app.listen(3000, function() { console.log("Server running on port 3000"); });

O código do conversor principal está no terminal /convert conforme extraído e mostrado abaixo. Isso converterá qualquer texto Markdown que você postar em uma versão HTML e o retornará como um documento JSON.

 ... } else { text = req.body.content; html = converter.makeHtml(text); res.json(["markdown", html]); }

O método que faz a conversão é converter.makeHtml(text) . Podemos definir várias opções para a conversão de Markdown usando o método setOption com o seguinte formato:

 converter.setOption('optionKey', 'value');

Assim, por exemplo, podemos definir uma opção para inserir e vincular automaticamente uma URL especificada sem qualquer marcação.

 converter.setOption('simplifiedAutoLink', 'true');

Como no exemplo do Postman, se passarmos uma string simples (como Google home https://www.google.com/ ) para o aplicativo, ele retornará a seguinte string se o simplifiedAutoLink estiver ativado:

 <p>Google home <a href="https://www.google.com/">https://www.google.com/</a></p>

Sem a opção, teremos que adicionar informações de marcação para obter os mesmos resultados:

 Google home <https://www.google.com/>

Existem muitas opções para modificar como o Markdown é processado. Uma lista completa pode ser encontrada no site do Showdown.

Portanto, agora temos um conversor de Markdown para HTML em funcionamento com um único ponto de extremidade. Vamos avançar e adicionar autenticação para ter application.

Etapa 4: adicionar autenticação de API usando o Passport

Expor a API do seu aplicativo para o mundo externo sem autenticação adequada incentivará os usuários a consultar o endpoint da API sem restrições. Isso convidará elementos sem escrúpulos a fazer uso indevido de sua API e também sobrecarregará seu servidor com solicitações não moderadas. Para mitigar isso, temos que adicionar um mecanismo de autenticação adequado.

Usaremos o pacote Passport para adicionar autenticação ao nosso aplicativo. Assim como o middleware body-parser que encontramos anteriormente, o Passport é um middleware de autenticação para Node.js. A razão pela qual usaremos o Passport é que ele tem uma variedade de mecanismos de autenticação para trabalhar (nome de usuário e senha, Facebook, Twitter e assim por diante), o que dá ao usuário a flexibilidade de escolher um mecanismo específico. Um middleware do Passport pode ser facilmente inserido em qualquer aplicativo Express sem alterar muito código.

Instale o pacote usando npm.

 $ npm install passport

Também usaremos a estratégia local , que será explicada posteriormente, para autenticação. Então instale-o também.

 $ npm install passport-local

Você também precisará adicionar o módulo de codificação e decodificação JWT (JSON Web Token) para Node.js que é usado pelo Passport:

 $ npm install jwt-simple

Estratégias no passaporte

O Passport utiliza o conceito de estratégias para autenticar solicitações. As estratégias são vários métodos que permitem autenticar solicitações e podem variar desde o caso simples como verificar credenciais de nome de usuário e senha, autenticação usando OAuth (Facebook ou Twitter) ou usando OpenID. Antes de autenticar as solicitações, a estratégia utilizada por um aplicativo deve ser configurada.

Em nossa aplicação, usaremos um esquema simples de autenticação de nome de usuário e senha, pois é simples de entender e codificar. Atualmente, o Passport suporta mais de 300 estratégias que podem ser encontradas aqui.

Embora o design do Passport possa parecer complicado, a implementação em código é muito simples. Aqui está um exemplo que mostra como nosso endpoint /convert é decorado para autenticação. Como você verá, adicionar autenticação a um método é bastante simples.

 app.post("/convert", passport.authenticate('local',{ session: false, failWithError: true }), function(req, res, next) { // If this function gets called, authentication was successful. // Also check if no content is sent if(typeof req.body.content == 'undefined' || req.body.content == null) { res.json(["error", "No data found"]); } else { text = req.body.content; html = converter.makeHtml(text); res.json(["markdown", html]); }}, // Return a 'Unauthorized' message back if authentication failed. function(err, req, res, next) { return res.status(401).send({ success: false, message: err }) });

Agora, junto com a string Markdown a ser convertida, também temos que enviar um nome de usuário e senha. Isso será verificado com nosso nome de usuário e senha do aplicativo e verificado. Como estamos usando uma estratégia local para autenticação, as credenciais ficam armazenadas no próprio código.

Embora isso possa parecer um pesadelo de segurança, para aplicativos de demonstração, isso é bom o suficiente. Isso também facilita a compreensão do processo de autenticação em nosso exemplo. Aliás, um método de segurança comum usado é armazenar credenciais em variáveis ​​de ambiente. Ainda assim, muitas pessoas podem não concordar com esse método, mas acho isso relativamente seguro.

O exemplo completo com autenticação é mostrado abaixo.

 const express = require("express"); const showdown = require('showdown'); const bodyParser = require('body-parser'); const passport = require('passport'); const jwt = require('jwt-simple'); const LocalStrategy = require('passport-local').Strategy; var app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); converter = new showdown.Converter(); const ADMIN = 'admin'; const ADMIN_PASSWORD = 'smagazine'; const SECRET = 'secret#4456'; passport.use(new LocalStrategy(function(username, password, done) { if (username === ADMIN && password === ADMIN_PASSWORD) { done(null, jwt.encode({ username }, SECRET)); return; } done(null, false); })); app.get('/', function(req, res){ res.send('Hello World!'); }); app.post('/login', passport.authenticate('local',{ session: false }), function(req, res) { // If this function gets called, authentication was successful. // Send a 'Authenticated' string back. res.send("Authenticated"); }); app.post("/convert", passport.authenticate('local',{ session: false, failWithError: true }), function(req, res, next) { // If this function gets called, authentication was successful. // Also check if no content is sent if(typeof req.body.content == 'undefined' || req.body.content == null) { res.json(["error", "No data found"]); } else { text = req.body.content; html = converter.makeHtml(text); res.json(["markdown", html]); }}, // Return a 'Unauthorized' message back if authentication failed. function(err, req, res, next) { return res.status(401).send({ success: false, message: err }) }); app.listen(3000, function() { console.log("Server running on port 3000"); });

Uma sessão do Postman que mostra a conversão com autenticação adicionada é mostrada abaixo.

Teste final do aplicativo com o Postman
Teste final do aplicativo com o Postman (visualização grande)

Aqui podemos ver que temos uma string convertida em HTML adequada de uma sintaxe Markdown. Embora tenhamos solicitado apenas a conversão de uma única linha de Markdown, a API pode converter uma quantidade maior de texto.

Isso conclui nossa breve incursão na criação de um endpoint de API usando Node.js e Express. A construção de API é um tópico complexo e há nuances mais sutis das quais você deve estar ciente ao construir uma, que infelizmente não temos tempo para aqui, mas talvez abordaremos em artigos futuros.

Acessando nossa API de outro aplicativo

Agora que criamos uma API, podemos criar um pequeno script Node.js que mostrará como a API pode ser acessada. Para nosso exemplo, precisaremos instalar o pacote request npm que fornece uma maneira simples de fazer solicitações HTTP. (Você provavelmente já terá isso instalado.)

 $ npm install request --save

O código de exemplo para enviar uma solicitação à nossa API e obter a resposta é fornecido abaixo. Como você pode ver, o pacote de request simplifica consideravelmente o assunto. O markdown a ser convertido está na variável textToConvert .

Antes de executar o script a seguir, verifique se o aplicativo de API que criamos anteriormente já está em execução. Execute o script a seguir em outra janela de comando.

Nota : Estamos usando o sinal (back-tick) para abranger várias linhas JavaScript para a variável textToConvert . Esta não é uma citação simples.

 var Request = require("request"); // Start of markdown var textToConvert = `Heading ======= ## Sub-heading Paragraphs are separated by a blank line. Two spaces at the end of a line produces a line break. Text attributes _italic_, **bold**, 'monospace'. A [link](https://example.com). Horizontal rule:`; // End of markdown Request.post({ "headers": { "content-type": "application/json" }, "url": "https://localhost:3000/convert", "body": JSON.stringify({ "content": textToConvert, "username": "admin", "password": "smagazine" }) }, function(error, response, body){ // If we got any connection error, bail out. if(error) { return console.log(error); } // Else display the converted text console.dir(JSON.parse(body)); });

Quando fazemos uma solicitação POST para nossa API, fornecemos o texto Markdown a ser convertido junto com as credenciais. Se fornecermos as credenciais erradas, seremos recebidos com uma mensagem de erro.

 { success: false, message: { name: 'AuthenticationError', message: 'Unauthorized', status: 401 } }

Para uma solicitação corretamente autorizada, o Markdown de amostra acima será convertido para o seguinte:

 [ 'markdown', `<h1>Heading</h1> <h2>Sub-heading</h2> <p>Paragraphs are separated by a blank line.</p> <p>Two spaces at the end of a line<br /> produces a line break.</p> <p>Text attributes <em>italic</em>, <strong>bold</strong>, 'monospace'. A <a href="https://example.com">link</a>. Horizontal rule:</p>` ]

Embora tenhamos codificado o Markdown aqui, o texto pode vir de várias outras fontes - arquivo, formulários da web e assim por diante. O processo de solicitação permanece o mesmo.

Observe que, como estamos enviando a solicitação como um tipo de conteúdo application/json ; precisamos codificar o corpo usando json, daí a chamada de função JSON.stringify . Como você pode ver, é preciso um exemplo muito pequeno para testar ou aplicar a API.

Conclusão

Neste artigo, embarcamos em um tutorial com o objetivo de aprender como usar Node,js e a estrutura Express para criar um endpoint de API. Em vez de construir algum aplicativo fictício sem propósito, decidimos criar uma API que converte a sintaxe Markdown em HTML, que ancora ou aprende em um contexto útil. Ao longo do caminho, adicionamos autenticação ao nosso endpoint de API e também vimos maneiras de testar nosso endpoint de aplicativo usando o Postman.