Alguém aí gosta de escrever documentação? Eu não! No entanto, é indiscutível a necessidade de se ter uma boa documentação em nossas APIs a fim de facilitar a vida de outros devs e empresas que desejam ou precisem se integrar com nosso backend, certo? E se tivesse uma forma de, além de documentar as nossas APIs, de fornecer uma UI amigável para testá-las facilmente no browser?
Foi pensando nisso que a empresa SmartBear, em 2011, criou o Swagger, um conjunto de ferramenta para desenvolvimento, documentação e testes de APIs que, mais tarde, se tornaria a especificação OpenAPI, unindo nomes de gigantes como Microsoft, IBM e outras. Hoje, Swagger segue sendo sinônimo de padronização na documentação de APIs, mas vale ressaltar que existem diversas ferramentas no mercado que aderiam à OpenAPI, sendo importante diferenciar tooling (Swagger) de especificação (OpenAPI).
Neste tutorial vamos ver como usar o Swagger em uma aplicação backend Node.js + Express para através da especificação OpenAPI gerar uma documentação online e testável de uma API fictícia de clientes, mas que você pode facilmente adaptar a outros cenários, incluindo autenticação, um caso de uso comum em APIs.
Se preferir, você pode assistir ao vídeo abaixo ao invés de ler o tutorial.
Vamos lá!
#1 – O projeto
Como base para documentarmos usando o Swagger, vou usar um outro projeto aqui do site onde ensinei sobre implementação de autenticação JWT em aplicação Node.js com Express, disponível no meu GitHub. Ele é bem simples e o arquivo principal, index.js, eu reproduzo abaixo com uma leve modificação, onde extraí as rotas para outro arquivo para fins de organização.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//index.js import "dotenv/config"; import express from "express"; const app = express(); app.use(express.json()); import router from "./routes.js"; app.use(router); app.listen(3000, () => { console.log("Servidor escutando na porta 3000..."); }); |
Agora o arquivo de rotas (routes.js) ficou assim:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
import jwt from "jsonwebtoken"; import express from "express"; const router = express.Router(); router.get("/", (req, res, next) => { res.json({ message: "Tudo ok por aqui!" }); }) router.get("/clientes", verifyJWT, (req, res) => { console.log("Retornou todos clientes!"); res.json([{ id: 1, nome: "luiz" }]); }) function verifyJWT(req, res, next) { let token = req.headers["authorization"]; if (!token) return res.status(401).json({ message: "No token provided." }); token = req.headers["authorization"].replace("Bearer ", ""); if (blacklist[token]) return res.status(403).json({ message: "Invalid token." }); try { const decoded = jwt.verify(token, process.env.JWT_SECRET); if (!decoded) return res.status(403).json({ message: "Invalid token." }); res.locals.token = decoded; return next(); } catch (err) { return res.status(403).json({ message: err.message }); } } router.post("/login", (req, res, next) => { //esse teste abaixo deve ser feito no seu banco de dados if (req.body.user === "luiz" && req.body.password === "123") { //auth ok const userId = 1; //esse id viria do banco de dados const token = jwt.sign({ userId }, process.env.JWT_SECRET, { expiresIn: parseInt(process.env.JWT_EXPIRES) }); return res.json({ token: token }); } res.status(401).json({ message: "Invalid credentials!" }); }) const blacklist = {}; router.post("/logout", verifyJWT, (req, res) => { const token = req.headers["authorization"].replace("Bearer ", ""); blacklist[token] = true; setTimeout(() => delete blacklist[token], parseInt(process.env.JWT_EXPIRES) * 1000); res.json({ token: null }); }) export default router; |
O primeiro passo para preparmos este projeto para uso do Swagger é a instalação das dependências. Existe apenas uma dependência realmente necessária, mas vou instalar duas pois a segunda facilita bastante o uso do Swagger na minha opinião.
|
1 2 3 |
npm install swagger-ui-express swagger-jsdoc |
Os pacotes que instalamos são:
- Swagger UI Express: o Swagger em si, que fornecerá uma UI web para nossa documentação e testes;
- Swagger JSDoc: a biblioteca que vai gerar a documentação pra gente a partir de comentários do tipo JSDoc, seguindo a especificação OpenAPI;
Sem o Swagger JSDoc nós teríamos que criar arquivos JSON ou YAML separados do código para definir toda a especificação. Mas com ele, usamos apenas blocos de comentários JSDoc acima de cada endpoint e a documentação será extraída a partir desses blocos.
Agora vamos importar esses dois pacotes no topo do index.js:
|
1 2 3 4 |
import swaggerUI from "swagger-ui-express"; import swaggerJsdoc from "swagger-jsdoc"; |
E na sequência vamos configurar o Swagger JSDoc como abaixo, explico na sequência:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
const openapiSpecification = swaggerJsdoc({ definition: { openapi: '3.0.0', info: { title: 'Clients API', version: '1.0.0', }, components: { securitySchemes: { bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT', }, }, } }, apis: ['./routes.js'], }); |
Aqui estou dizendo duas coisas principais: o padrão que vamos usar e onde estão minhas APIs no projeto.
O padrão que vou usar aqui é o OpenAPI 3, onde na prop info eu digo o nome da minha API e a versão dela, informações úteis que vão aparecer depois na interface do Swagger. Já em components eu declaro o esquema de segurança usado nessa API (Bearer Auth com JWT). Essa informação será usada depois para os testes de rotas autenticadas funcionarem pela UI.
Por fim, agora temos de injetar o Swagger como um middleware no Express, passando nossas configurações como abaixo.
|
1 2 3 |
app.use("/docs", swaggerUI.serve, swaggerUI.setup(openapiSpecification)); |
Com isso eu criei uma rota docs onde teremos a Swagger UI disponível para ser acessada no browser, por enquanto praticamente vazia, já que ainda não escrevemos os JSDocs nos endpoints.


#2 – Primeira Documentação
Vamos começar a documentar nossos endpoints a partir das rotas públicas, por serem mais simples. A mais básica de todas é a nossa rota de Health Check, um simples GET que avisa se o servidor está ok, veja:
|
1 2 3 4 5 |
router.get("/", (req, res, next) => { res.json({ message: "Tudo ok por aqui!" }); }) |
Para documentar esta rota, abra um bloco JSDoc acima dela com padrão YAML, informando o schema @openapi, confirmando o endpoint como “/”, o verbo como “get” e fornecendo algumas props que detalho a seguir. Apenas atente-se à identação, ela é importante no formato YAML pois denota hierarquia de informações e escopo, como se fossem as chaves do JS:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * @openapi * /: * get: * summary: Health Check * description: Returns the health status of the server * responses: * 200: * description: Success * content: * application/json: * schema: * type: object * properties: * message: * type: string */ |
- summary: aqui você coloca o título do endpoint;
- description: aqui vai o descritivo desse endpoint (para que ele serve);
- responses: aqui temos as respostas possíveis para uma chamada HTTP nele, sendo comum ter ao menos a resposta 200 mas também pode informar demais códigos passíveis de serem retornados, como veremos mais tarde;
Para cada resposta, temos o description da resposta em si e seu conteúdo (content). Geralmente em APIs REST o content vai ser application/json como o meu e o schema varia conforme o retorno, se você tiver algum. Particularmente eu uso o GitHub Copilot para me ajudar a escrever essas documentações de schema, pois ele já saca rapidamente o que precisa colocar como schema com base no código do próprio endpoint, super recomendo, é grátis.
Se você rodar seu projeto e novo, agora vai ver que já temos um endpoint documentado e que pode ser testado.

Pare um minuto para ver todos os detalhes dessa UI e entender a relação dela com o JSDoc que escrevemos. Ao final, o botão “Try it out” te permite testar essa rota e ver se ela está funcionando.

Como essa rota não tem parâmetros o teste é bem direto e o retorno igualmente simples, mas vale perder um minuto aqui também pra dar uma olhada e ver se deu tudo certo como esperado. Veja o comando CURL usado para o teste e o response body.
Com isso, finalizamos um primeiro uso do Swager. Agora podemos avançar mais rapidamente nos demais endpoints.
#3 – Autenticação
Vamos fazer outro endpoint público, mas que vai nos permitir depois testar os privados. O endpoint de login é bem simples e tem uma autenticação fake fácil de ser testada nesse exemplo.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
router.post("/login", (req, res, next) => { //esse teste abaixo deve ser feito no seu banco de dados if (req.body.user === "luiz" && req.body.password === "123") { //auth ok const userId = 1; //esse id viria do banco de dados const token = jwt.sign({ userId }, process.env.JWT_SECRET, { expiresIn: parseInt(process.env.JWT_EXPIRES) }); return res.json({ token: token }); } res.status(401).json({ message: "Invalid credentials!" }); }) |
Para documentá-lo, atenção à troca de endpoint (/login) e de verbo (post) que exige request body com os dados de autenticação. Também repare que agora temos duas respostas possíveis (200 e 401) a depender do sucesso na autenticação. Os demais campos não tem muita novidade aqui, voê vai notar rapidamente que realmente é um trabalho bem repetitivo, por isso reitero a dica de usar GitHub Copilot pra te ajudar a acelerar esse processo.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/** * @openapi * /login: * post: * summary: User login * description: Authenticates a user and returns a JWT token * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * user: * type: string * password: * type: string * responses: * 200: * description: Success * content: * application/json: * schema: * type: object * properties: * token: * type: string * 401: * description: Invalid credentials */ |
Salve, abra no browser e teste essa rota informando primeiro credenciais inválidas para ver o 401, depois as credenciais certas (luiz e 123) para receber o token.

Repare como no response body você vai receber o JWT. Copie o conteúdo dele pois precisamos autorizar as próximas requisições privadas. Esse JWT deve ser informado no campo Authorize que foi criado no campo superior direito do Swagger por causa de nossas configurações.

Uma vez autorizado, as requisições que precisam enviar token usarão essa informação que você preencheu, como verá a seguir.

#4 – Rotas Privadas
Agora que aprendemos sobre autenticação via Swagger, podemos fazer a documentação das rotas privadas. Primeiro a de listagem de clientes, cuja rota reproduzo abaixo:
|
1 2 3 4 5 6 |
router.get("/clientes", verifyJWT, (req, res) => { console.log("Retornou todos clientes!"); res.json([{ id: 1, nome: "luiz" }]); }) |
E a sua documentação na sequência:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/** * @openapi * /clientes: * get: * summary: Get all clients * description: Returns a list of clients * security: * - bearerAuth: [] * responses: * 200: * description: Success * content: * application/json: * schema: * type: array * items: * type: object * properties: * id: * type: integer * nome: * type: string * 401: * description: Unauthorized */ |
A única novidade aqui, além de um response com schema mais elaborado por ser um array de itens, é a prop security logo no topo, indicando que esta requisição deve ser autenticada. Isso vai incluir nos testes do Swagger o JWT que você preencheu no campo Authorize da interface.
Para finalizar, temos o endpoint de logout abaixo:
|
1 2 3 4 5 6 7 8 |
router.post("/logout", verifyJWT, (req, res) => { const token = req.headers["authorization"].replace("Bearer ", ""); blacklist[token] = true; setTimeout(() => delete blacklist[token], parseInt(process.env.JWT_EXPIRES) * 1000); res.json({ token: null }); }) |
Com a documentação a seguir:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/** * @openapi * /logout: * post: * summary: User logout * description: Logs out a user and invalidates the JWT token * security: * - bearerAuth: [] * responses: * 200: * description: Success * content: * application/json: * schema: * type: object * properties: * token: * type: string * nullable: true * 401: * description: Unauthorized */ |
Zero novidades nessa, já que o esquema de security é o mesmo anterior. Com todas as documentações posicionadas, nosso Swagger UI está completo, como pode ver na imagem abaixo. Repare os ícones de cadeado à direita, indicando quais rotas são privadas.

Com isso terminamos esse tutorial de Swagger.
Até a próxima!

Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.

