Recentemente criei um port mostrando como deixar o Swagger com tema darkish mode utilizando NestJS, agora vou mostrar como deixar o Swagger em darkish mode utilizando o Swaggo com Go.
O que é o Swaggo?
O Swaggo é uma ferramenta que nos ajuda a documentar nossa API desenvolvida em GO, gerando a documentação no padrão da OpenAPI.
Não vamos focar em como utilizar o Swaggo, mas vou deixar aqui um excelente publish no style-tricks.com que ensina como fazer isso.
Criando o projeto de exemplo
Vamos criar um exemplo mais alinhado com uma utilização no mundo actual, para isso vamos utilizar o Go Chi, que é o roteador muito simples, mas que facilita muito na criação de rotas em Go.
Vamos iniciar o projeto rodando o comando:
go mod init swagger-darkish-mode
Isso vai criar nosso arquivo go.mod
, que vai servir para gerenciar nossos pacotes.
Organizando o projeto
Vamos organizar seguindo um padrão muito utilizado pela comunidade do Go, você pode ver nesse repositório
-
cmd: Aqui é onde vamos deixar nosso arquivos que iniciam a nossa aplicação.
-
webserver: Aqui é onde vamos deixar o
essential.go
que inicia nosso webserver.
-
webserver: Aqui é onde vamos deixar o
-
inside: Nessa pasta onde deve ficar todo o código da nossa aplicação.
- handler: Aqui vai ficar os arquivos responsáveis por receber nossas solicitações http, você pode conhecer também como controllers.
- routes: Aqui vamos organizar nossas rotas, incluido a rota da nossa documentação.
essential.go
:
package deal essential
import (
"fmt"
"web/http"
"swagger-dark-mode/inside/handler/routes"
"github.com/go-chi/chi/v5"
)
func essential() {
r := chi.NewRouter()
routes.InitRoutes(r)
fmt.Println("Server operating on port 8080")
http.ListenAndServe(":8080", r)
}
No arquivo essential.go
, iniciamos nosso router com go chi, iniciamos nossas rotas com routes.InitRoutes()
, e damos begin em nosso server que vai rodar na porta 8080
Você pode baixar os pacotes do go chi e swaggo com o comando:
go get github.com/swaggo/http-swagger github.com/go-chi/chi/v5
É necessário instalar o swag na sua máquina, veja como neste link
routes.go
:
package deal routes
import (
"swagger-dark-mode/inside/handler"
_ "swagger-dark-mode/docs"
"github.com/go-chi/chi/v5"
httpSwagger "github.com/swaggo/http-swagger"
)
var (
docsURL = "http://localhost:8080/docs/doc.json"
)
// @title Swagger Darkish Mode
// @model 1.0
func InitRoutes(r chi.Router) {
r.Get("/docs/*", httpSwagger.Handler(httpSwagger.URL(docsURL)))
r.Get("/consumer", handler.GetUser)
}
No arquivo routes.go
é onde criamos nossas rotas, criamos uma rota do tipo GET /consumer
que chama nosso handler GetUser
e outra rota
GET para nossa docs /docs/*
o /*
após o path docs
, indica que qualquer combinação de caracteres pode aparecer na posição correspondente.
o import "_ swagger-dark-mode/docs"
vem da pasta gerada após iniciar o swag, usando o comando:
swag init -g inside/handler/routes/routes.go
É necessário rodar esse comando sempre que houver alterações na sua documentação, o swag vai criar um pasta chamada docs, você não precisa alterar nada nessa pasta, veja como fica agora a estrutura do projeto:
Dentro do swagger.json
fica o arquivo no padrão da OpenAPI.
o caminho inside/handler/routes/routes.go
deve ser onde está as anotações gerais do swag, no nosso caso setamos apenas o @title
e o @model
, veja todas as anotações possíveis aqui.
consumer.go
:
package deal handler
import (
"encoding/json"
"log/slog"
"web/http"
)
sort Person struct {
ID int `json:"id"`
Title string `json:"title"`
E-mail string `json:"electronic mail"`
}
// Get pretend consumer
// @Abstract Get Pretend consumer
// @Description Get Pretend consumer for instance
// @Tags consumer
// @Settle for json
// @Produce json
// @Success 200 {object} Person
// @Failure 500
// @Router /consumer [get]
func GetUser(w http.ResponseWriter, r *http.Request) {
consumer := Person{
ID: 1,
Title: "John Doe",
E-mail: "jonh.doe@electronic mail.com",
}
userMarshal, err := json.Marshal(consumer)
if err != nil {
slog.Error("Error marshalling consumer", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(userMarshal)
}
Se desejar formatar suas anotações do swag basta rodar o comando:
swag fmt
No arquivo consumer.go
é onde podemos validar nossa requisição, onde podemos pegar o physique por exemplo e transformar em um struct go. Criamos um consumer pretend, fizemos o enconding da a struct Person
para Json usando Marshal, caso não aconteça um erro duranto o Marshal, retornamos o json usando o w.author
.
Rodando o projeto
Tudo pronto para rodar o projeto, caso não tenha instalado os pacotes, rode o comando:
go mod tidy
Esse comando vai no arquivo go.mod
e faz o obtain das dependências.
Vamos iniciar o projeto, rodando nosso essential.go
go run cmd/webserver/essential.go
Se tudo estiver correto, vamos ter no terminal a mensagem Server operating on port 8080.
Acessando nossa rota by way of http://localhost:8080/consumer
teremos esse resultado:
{
"id": 1,
"title": "John Doe",
"electronic mail": "jonh.doe@electronic mail.com"
}
Acessando nossa outra rota http://localhost:8080/docs/index.html
, teremos nossa documentação com swagger:
Criando nosso CSS e JS
Bom, finalmente vamos ao intuito do publish, deixar o tema do swagger em darkish mode, infelizmente não existe nada nativo ou que seja simples quanto deixar o swagger em darkish mode utilizando o NestJS. (Pelo menos até a knowledge de pulbicação desse publish).
Vamos precisar injetar nosso css customizado no swaggo, para isso você pode utilizar o css desse gist, por´m também não conseguimos injetar o css diretamente, mas conseguimos injetar um JavaScript, com isso também se torna possível manipular a DOM e consequentemente injetar css.
Vamos criar uma pasta dentro da pasta docs
, chamado customized
, onde vamos colocar nossas customizações.
Você pode criar fora da pasta docs
, já que é uma pasta gerada dinamicamente pelo swaggo, pode ser substituida e acabar perdendo sua customização, mas para este exemplo vamos deixar dentro da pasta docs
mesmo.
Dentro do customized vamos criar 2 arquivos, custom_css.go
e custom_layout.go
.
custom_css.go
:
package deal customized
var customCSS = `css do gist`
No arquivo custom_css.go
, apenas retornamos o css que deixe no gist em string.
custom_layout.go
:
package deal customized
import "fmt"
var CustomLayoutJS = fmt.Sprintf(`
// darkish mode
const model = doc.createElement('model');
model.innerHTML = %s;
doc.head.appendChild(model);
`, "`"+customCSS+"`")
No arquivo custom_layout.go
criamos noss JavaScript para ser injetado em nosso swagger, criamos uma tag model e adicionamos ao DOM, convertamos em string utilizando o Sprintf
do pacote fmt, veja um publish meu sobre o pacote fmt aqui.
""+customCSS+""
, isso envolve o arquivo JS em aspas duplas.
Aplicando o Darkish mode
Vamos finalmente aplicar nosso darkish mode, para isso vamos alterar no arquivo routes.go
:
func InitRoutes(r chi.Router) {
r.Get("/docs/*", httpSwagger.Handler(httpSwagger.URL(docsURL),
httpSwagger.AfterScript(customized.CustomJS),
httpSwagger.DocExpansion("none"),
httpSwagger.UIConfig(map[string]string{
"defaultModelsExpandDepth": `"-1"`,
}),
))
r.Get("/consumer", handler.GetUser)
}
httpSwagger.AfterScript(customized.CustomJS)
: Isso injeta nosso JS no swagger depois da página ser carregada.httpSwagger.DocExpansion("none")
: Isso faz com que cada o endpoint abre expandido ou não (gosto pessoal), mas ajuda quando sua documentação possui muitos endpoints, no exemplo o -1
faz com que por padrão não fique expandido as rotas."defaultModelsExpandDepth": "-1"
: Isso faz com que os fashions sejam ocultos, caso exact deixar visível basta remover.
Existe mais opções possíveis nas docs do swagger.
Agora rodando novamente nosso projeto, já teremos o swagger em darkish mode:
Personalizando ainda mais
Agora com o poder da manipulação da DOM e com paciência, podemos modificar o structure da forma que desejarmos, vamos por exemplo altera a emblem e favicon e o title.
Vamos modificar nosso custom_layout.go
var CustomJS = fmt.Sprintf(`
// set customized title
doc.title="Swagger Darkish Mode With Go";
// set customized favicon
const hyperlink = doc.createElement('hyperlink');
hyperlink.rel="icon";
hyperlink.sort="picture/x-icon";
hyperlink.href="knowledge:picture/png;base64,%s";
doc.head.appendChild(hyperlink);
// set customized emblem
const picture = doc.querySelector('.hyperlink img');
const base64URL = 'knowledge:picture/png;base64,%s';
picture.src = base64URL;
// darkish mode
const model = doc.createElement('model');
model.innerHTML = %s;
doc.head.appendChild(model);
`, CustomLogo, CustomLogo, "`"+customCSS+"`")
Adicionei um title, favicon e emblem, usando um base64 para as imagens. Salvamos esse base64 em um arquivo chamado photographs.go
dentro da pasta customized
.
package deal customized
var (
CustomLogo = `base64 aqui`
)
Apenas como exemplo, o favicon e a emblem foram utilizados a mesma imagem em base64, mas você poderia separar, veja como ficou:
Considerações finais
Como podemos ver, não é tão complicado personalizar, apesar da personalização ser um pouco trabalhosa e parecer não ser a mais adequeada, ainda conseguimos deixar o tema do swagger com uma aparência mais agradável para quem for consumir nossa api.
Hyperlink do repositório
repositório do projeto
link do projeto no meu weblog