Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?

Como deixar o Swagger com tema dark mode usando Swaggo e Golang

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
Enter fullscreen mode

Exit fullscreen 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.
  • 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)
  }
Enter fullscreen mode

Exit fullscreen mode

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
Enter fullscreen mode

Exit fullscreen mode

É 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)
  }
Enter fullscreen mode

Exit fullscreen mode

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
Enter fullscreen mode

Exit fullscreen mode

É 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:

Project structure

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)
  }
Enter fullscreen mode

Exit fullscreen mode

Se desejar formatar suas anotações do swag basta rodar o comando:

  swag fmt
Enter fullscreen mode

Exit fullscreen mode

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
Enter fullscreen mode

Exit fullscreen mode

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
Enter fullscreen mode

Exit fullscreen mode

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"
}
Enter fullscreen mode

Exit fullscreen mode

Acessando nossa outra rota http://localhost:8080/docs/index.html, teremos nossa documentação com swagger:

Swaggo light
Meme my eyes



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`
Enter fullscreen mode

Exit fullscreen mode

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+"`")
Enter fullscreen mode

Exit fullscreen mode

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)
  }
Enter fullscreen mode

Exit fullscreen mode

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:
Swaggo dar mode
Meme liked



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+"`")
Enter fullscreen mode

Exit fullscreen mode

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`
  )
Enter fullscreen mode

Exit fullscreen mode

Apenas como exemplo, o favicon e a emblem foram utilizados a mesma imagem em base64, mas você poderia separar, veja como ficou:
Swaggo dar mode



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

Add a Comment

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?