Criando uma API Node.js Express para converter Markdown em HTML
Publicados: 2022-03-10Markdown é 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.
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:
Para consultar um endpoint de API, você precisará executar as seguintes etapas:
- Insira o URL que você deseja consultar na barra de URL na seção superior;
- Selecione o método HTTP à esquerda da barra de URL para enviar a solicitação;
- 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.
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.
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.