<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gabriel Pinheiro</title>
    <description>The latest articles on DEV Community by Gabriel Pinheiro (@gabrielfmpinheiro).</description>
    <link>https://dev.to/gabrielfmpinheiro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F786143%2Fa5f7f30a-ef40-48f7-b793-2cbcaee2cdfc.png</url>
      <title>DEV Community: Gabriel Pinheiro</title>
      <link>https://dev.to/gabrielfmpinheiro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gabrielfmpinheiro"/>
    <language>en</language>
    <item>
      <title>Criando PDF com Node JS e React</title>
      <dc:creator>Gabriel Pinheiro</dc:creator>
      <pubDate>Fri, 02 Sep 2022 01:37:06 +0000</pubDate>
      <link>https://dev.to/gabrielfmpinheiro/criar-pdf-com-node-js-e-react-19cm</link>
      <guid>https://dev.to/gabrielfmpinheiro/criar-pdf-com-node-js-e-react-19cm</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Talvez você já tenha escutado o termo "mala direta" envolvendo o Microsoft Word. Para quem não sabe, basicamente consiste em criar um documento padrão e deixar alguns campos que irão ocorrer algum tipo de alteração de forma automatizada. A ideia desse post é bem parecida. Com o auxílio do Node JS, mais precisamente com o framework &lt;strong&gt;express&lt;/strong&gt; e as bibliotecas &lt;strong&gt;html-pdf-node&lt;/strong&gt;, &lt;strong&gt;ejs&lt;/strong&gt; e &lt;strong&gt;React&lt;/strong&gt;, iremos construir, de forma prática, um gerador de PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passo 1
&lt;/h2&gt;

&lt;p&gt;O projeto seguirá a seguinte estrutura de arquivos. Primeiramente, crie somente a pasta "app". As demais serão criadas ao longo desse tutorial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├─ backend/
│  ├─ node_modules/
│  ├─ public/
│  ├─ package.json
│  ├─ package-lock.json
│  ├─ src/
│  │  ├─ example.ejs
│  │  ├─ index.js
│  │  ├─ services/
│  │  │  ├─ createPDF.js
│  ├─ .gitignore/
├─ frontend/
│  ├─ node_modules/
│  ├─ public/
│  ├─ package.json
│  ├─ package-lock.json
│  ├─ src/
│  │  ├─ App.jsx
│  │  ├─ index.css
│  │  ├─ main.jsx
│  ├─ .gitignore
│  ├─ index.html
│  ├─ vite.config.js

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Iremos começar pelo Back-end, instalando alguns pacotes necessários para o projeto. Diante disso, crie a pasta "backend". Iniciaremos pelo arquivo "package.json", que conterá diversas informações importantes da nossa aplicação. Abra o terminal na pasta citada e execute o comando abaixo:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm init -y&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Em seguida, iremos instalar o restante dos pacotes que utilizaremos nesse projeto:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm i nodemon cors express ejs html-pdf-node&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;De forma breve, irei explicar cada uma dessas instalações. Caso você já conheça, pule essa parte.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nodemon: ajudará a iniciar o nosso servidor Node JS no processo de desenvolvimento. O seu grande diferencial é que ele reinicia automaticamente o nosso servidor a medida que alterações ou erros sejam realizados.&lt;/li&gt;
&lt;li&gt;cors: utilizado para adicionar cabeçalhos HTTP que permite que aplicações de origens diferentes comuniquem entre si.&lt;/li&gt;
&lt;li&gt;express: possibilitará criar um servidor onde será construído as nossas APIs.&lt;/li&gt;
&lt;li&gt;ejs: ajudará a construír páginas HTML com javascript de forma direta.&lt;/li&gt;
&lt;li&gt;html-pdf-node: ajudará a converter o arquivo html em pdf.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora, para o Front-end, utilizaremos o React JS com o auxílio do ViteJS. Para isso, é necessário rodar o seguinte comando dentro da pasta "app". Após rodá-lo, algumas opções de instalações irão aparecer, selecione "React" e depois "React" novamente.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm create vite@latest frontend --template react&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Rode os dois comandos abaixo para instalar as dependências do Front-end.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd frontend&lt;/code&gt;&lt;br&gt;
&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Passo 2
&lt;/h2&gt;

&lt;p&gt;Realizado o passo 1, iremos dar início ao desenvolvimento da lógica na pasta services com a criação do arquivo createPDF.js, seguindo a estrutura abordada no início.&lt;br&gt;
O código abaixo irá resumir, através de comentários, os motivos e o que está sendo realizado em cada parte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const html_to_pdf = require("html-pdf-node");
const ejs = require("ejs");

async function createPDF(file, ejsVariables) {
  let html;
  // A constante url irá ajudar caso a gente queira utilizar imagens que estão disponíveis na pasta public do backend no nosso arquivo ejs
  const url = process.env.URL || "http://localhost:3001";

  ejs.renderFile(
    // Nome do arquivo ejs que será transformado
    file,
    // Objeto contendo as váriaveis para substituir no arquivo ".ejs"
    { ...ejsVariables, url },
    (err, content) =&amp;gt; {
      if (err) {
        throw new Error(
          "Can't render the file, probably some variables are missing"
        );
      }
      // Vamos armazenar o html após a conversão na variável html
      html = { content };
    }
  );

  // Algumas opções para o pdf que irá ser gerado. Caso precise de algo mais específico, favor consultar a documentação: https://github.com/mrafiqk/html-pdf-node
  const options = { format: "A4" };

  // Aqui será gerado o buffer do nosso pdf
  const pdfBuffer = await html_to_pdf.generatePdf(html, options);

  return pdfBuffer;
}

module.exports = {
  createPDF,
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após isso, precisaremos criar uma instância do nosso servidor e uma API para enviar esse documento para o usuário. Para isso, bastar copiar o conteúdo abaixo e colar no arquivo "index.js", dentro da pasta "src":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require("express");
const cors = require("cors");
const { createPDF } = require("./services/createPDF");
const path = require("path");

const PORT = process.env.PORT || 3001;

const app = express();

app.use(cors());
app.use(express.json());
// Pasta onde ficarão os arquivos estáticos, como fotos
app.use(express.static("public"));

app.post("/generate-pdf", async (req, res) =&amp;gt; {
  const FILE_PATH = path.join(__dirname, "/example.ejs");
  const pdfBuffer = await createPDF(FILE_PATH, { ...req.body });

  // Iremos setar o header com o tipo de arquivo enviado
  res.setHeader("Content-Type", "application/pdf");
  // Por fim, enviar o arquivo
  res.end(pdfBuffer);
});

app.listen(PORT, () =&amp;gt; console.log(`Server is running on port ${PORT}`));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Passo 3
&lt;/h2&gt;

&lt;p&gt;A partir desse momento, podemos desenvolver o nosso arquivo "example.ejs" que conterá a estrutura HTML e as variáveis que serão preenchidas automaticamente. Vale ressaltar que essa etapa demandará que você faça testes à medida que for desenvolvendo a página, pois nem sempre o PDF será originado igual ao HTML. &lt;br&gt;
Outro ponto importante é que as variáveis utilizadas dentro do arquivo devem sempre ser enviadas para o Back-end. Caso tenha algum campo facultativo, enviar essa propriedade com valor "null" ou algo similar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- example.ejs --&amp;gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="pt"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8" /&amp;gt;
    &amp;lt;meta http-equiv="X-UA-Compatible" content="IE=edge" /&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&amp;gt;
    &amp;lt;title&amp;gt;PDF Generator&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
      * {
        margin: 0;
        padding: 0;
      }
      h1,
      h3,
      p {
        margin: 10px 0 10px 10px;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;img style="width: 100%" src="&amp;lt;%= url %&amp;gt;/images.png" /&amp;gt;
    &amp;lt;h1&amp;gt;Dono do documento: &amp;lt;%= name %&amp;gt;&amp;lt;/h1&amp;gt;
    &amp;lt;h3&amp;gt;Objetivo do documento:&amp;lt;/h3&amp;gt;
    &amp;lt;p style="font-size: 24px"&amp;gt;Esse documento tem como objetivo gerar um PDF&amp;lt;/p&amp;gt;
    &amp;lt;% if (references) { %&amp;gt;
    &amp;lt;h3&amp;gt;Referências:&amp;lt;/h3&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;%= references.join(", ") + '.' %&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;% } %&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora o nosso Back-end está quase finalizado. Ainda precisamos configurar o nodemon para iniciar o nosso servidor. Para isso, adicione a linha abaixo na parte de scripts do package.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "scripts": {
    "dev": "nodemon src/index.js"
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caso queira testar, você já pode iniciar o servidor com o comando abaixo dentro da pasta "backend":&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Uma mensagem como essa deve aparecer: "Server is running on port 3001". Caso algum erro aconteça, favor retornar aos passos anteriores e também conferir a estrutura dos arquivos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passo 4
&lt;/h2&gt;

&lt;p&gt;Nesse momento iremos montar uma estrutura simples na pasta "frontend" para realizarmos a requisição. Basta copiar e colar os códigos abaixo nos arquivos corretos (pode substituir os arquivos que já estavam presentes).&lt;/p&gt;

&lt;p&gt;No arquivo "App.jsx":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default function App() {
  // Informações utilizadas nas variáveis dos arquivos .ejs
  const data = {
    name: "SEU NOME",
    references: ["https://ejs.co/", "https://github.com/mrafiqk/html-pdf-node"],
  };

  const generatePdf = (e) =&amp;gt; {
    e.preventDefault();

    fetch("http://localhost:3001/generate-pdf", {
      headers: {
        "Content-type": "application/json; charset=UTF-8",
      },
      method: "POST",
      body: JSON.stringify(data),
    })
      .then((res) =&amp;gt; res.blob())
      .then((blob) =&amp;gt; {
        // Criando uma url de acesso as informações
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = "example.pdf";
        // Necessário para funcionar em todos os browsers
        document.body.appendChild(a);
        a.click();
        a.remove();
      });
  };

  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;form onSubmit={generatePdf}&amp;gt;
        &amp;lt;button type="submit"&amp;gt;Download Here!&amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No arquivo "main.tsx"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")).render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No arquivo index.css&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
:root {
  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 24px;
  font-weight: 400;
}

body {
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  min-width: 100vw;
  min-height: 100vh;
  background-color: #8187f5;
}

button {
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  background-color: #1a1a1a;
  cursor: pointer;
  transition: border-color 0.25s;
}
button:hover {
  border-color: #646cff;
}
button:focus,
button:focus-visible {
  outline: 4px auto -webkit-focus-ring-color;
}

@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }
  a:hover {
    color: #747bff;
  }
  button {
    background-color: #f9f9f9;
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto! A partir desse momento, você pode iniciar o seu Front-end com o comando abaixo e também o seu Back-end, caso não tenha rodado nos passos anteriores. Acesse o browser com a URL gerada no seu terminal após executar o comando. Você irá observar um botão de download e, ao apertá-lo, um arquivo PDF será baixado. &lt;br&gt;
Uma observação é que a imagem não irá aparecer, pois você não a possui em sua máquina. Irei deixar o código da aplicação no fim desse post para que você possa pegá-lo na pasta backend/public e salvar no mesmo local no seu código, caso queira testar.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Por fim, espero ter contribuído para que você consiga gerar seus arquivos PDFs utilizando dados presentes nas suas aplicações. Vale ressaltar que existem várias soluções para esse problema e cabe a você avaliar qual irá atender melhor as necessidades do projeto que esteja fazendo. Caso perceba algum ponto de melhoria, tenha alguma dúvida ou queira contribuir com mais informações a respeito, basta deixar nos comentários!&lt;/p&gt;

&lt;p&gt;Código Git Hub: &lt;a href="https://github.com/GabrielFMPinheiro/PDFGenerator"&gt;Link&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>node</category>
      <category>express</category>
      <category>pdf</category>
    </item>
  </channel>
</rss>
