Article image
Washington Silva
Washington Silva08/02/2024 10:33
Compartilhe

CRUD Golang: Migrate SQLC Golang web tutorial Passo a passo

  • #MySQL
  • #Bootstrap
  • #GoLang

Este artigo é um tutorial passo a passo que mostra como criar um aplicativo da web completo em Golang (ou Go), usando o padrão CRUD (Create, Read, Update, Delete) para interagir com um banco de dados MySQL. Começa com a configuração do ambiente de desenvolvimento no Visual Studio Code e a configuração do projeto Go. Em seguida, Mostro como criar um servidor web simples, definir rotas e renderizar páginas HTML usando templates.

O tutorial continua explicando como criar um banco de dados MySQL, gerar migrações de banco de dados usando a ferramenta "migrate", e configurar consultas SQL usando o SQLC para gerar código Go para interagir com o banco de dados de forma segura.

Em seguida, o tutorial apresenta a criação de controladores para lidar com as operações CRUD (Criar, Ler, Atualizar, Deletar) e suas respectivas páginas HTML. Mostro como criar artigos, listar artigos, exibir detalhes de um artigo específico, editar artigos existentes e excluir artigos.

No final, o tutorial conclui com uma aplicação funcional de CRUD em Go, permitindo a criação, leitura, atualização e exclusão de artigos em um banco de dados MySQL.

Este tutorial é destinado a iniciantes em Go que desejam aprender como construir um aplicativo da web básico usando a linguagem de programação Go e o banco de dados MySQL. Ele cobre uma variedade de conceitos, incluindo configuração de ambiente, manipulação de rotas HTTP, interação com banco de dados e renderização de templates HTML.

Você pode ver o tutorial em

https://www.youtube.com/watch?v=pNeMAoW8nfE

Usaremos Visual Studio Code

Extensão: Go, Mysql

Abra o vscode, para abrir o terminal

CTRL+J 

Para criar uma pasta

md blog

Para abrir o vscode na mesma janela

code blog -r 

Clique CTRL + J para abrir o terminal

para iniciar um novo modulo

go mod init criarsite 

Na raiz do projeto crie um arquivo chamado main.go

package main

 

func main() {

  println("Ola mundo")

 }

no terminal digite um dos dois comandos

 go run main.go

ou

go run . 

No arquivo main.go vamos iniciar o servidor e definir nossa primeira rota

Agora vamos criar o rota e o servidor.

No arquivo main.go

package main


import (
 "html/template"
  "net/http"
)

var tmpl = template.Must(template.ParseGlob("views/*"))

func main() {
  println("Ola mundo")


  http.HandleFunc("/", Inicio)
 
  println("http://localhost:5000")
  http.ListenAndServe(":5000", nil)
}


func Inicio(w http.ResponseWriter, r *http.Request) {
  tmpl.ExecuteTemplate(w, "inicio", nil)
}


Dentro da raiz do projeto crie uma pasta chamada: views

Dentro da pasta views crie um arquivo index.html

{{ define "inicio" }}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>Pagina inicial</h1>
</body>
</html>
{{ end }}

No terminal digite:

go run main.go

Abra no navegador: 5000 é a porta que você definil dentro do arquivo main.go

http://localhost:5000

Você deve encontrar a mensagem: Pagina inicial.

No terminal digite:

ctrl+c

Isso vai parar o servidor:

Obs: toda vez que você rodar o servidor, precisa parar, faça as alterações e inicie novamente, em um próximo tutorial vamos criar um usando a AIR para recarregar as alterações automaticamente.

Vamos começar criando um banco de dados usando a extensão mysql

CRETE DATABASE blog 

No terminal:

migrate create -ext=sql -dir=dal/migrations -seq inicio

Vai ser criado os diretórios(pastas) dal/migrations com dois arquivos extensão .sql

inicio.down.sql onde você vai colocar o código para remover as alterações

DROP TABLE IF EXISTS artigo

inicio.up.sql onde você vão colocar o código para criação das tabelas

CREATE TABLE artigo(
  ID int primary key auto_increment,
  titulo VARCHAR(50) NOT NULL, 
  descricao VARCHAR(300) NOT NULL
)

no terminal digite o comando para gerar a migração

 migrate -path=dal/migrations -database "mysql://root:root@tcp(localhost:3306)/blog" -verbose up

agora você já tem a tabela criada no banco de dados.

Vamos começar a criando um arquivo na raiz do projeto chamado sqlc.yaml

e nele coloque o coloque as informações necessárias para SQLC.

 version: "2"
sql: 
 - schema: "dal/migrations"
   queries: "dal/queries"
   engine: "mysql"
   gen: 
    go:
    package: "repositorio"
    out:  "dal/repositorio"

Agora dentro da pasta dal crie uma nova pasta chamada queries: dal/queries

e dentro da pasta queries crie um arquivo com o mesmo nome da tabela do banco de dados com a extensão .sql: artigo.sql

dentro deste arquivo coloque o código sql com o que deseja fazer no banco de dados e nomeie para que gere as funções com o nome que deseja para cada uma delas.

-- name: ListarArtigos :many
SELECT * FROM artigo;

-- name: ArtigoPorId :one
SELECT * FROM artigo WHERE id = ?;

-- name: CriarArtigo :exec
INSERT INTO artigo (id, Titulo, Descricao) VALUES(?,?,?);

-- name: AtualizarArtigo :exec
UPDATE artigo SET Titulo = ?, Descricao = ? WHERE id = ?;

-- name: DeletarArtigo :exec
 DELETE FROM artigo WHERE id = ?;

no terminal digite.

sqlc generate

Vamos criar agora o arquivo responsável pela conexão com o banco de dados.

dentro da pasta dal crie uma nova pasta chamada database: dal/database e dentro desta pasta database crie um arquivo chamado conectar.go

 package database


import (
  "database/sql"
  "log"


  _ "github.com/go-sql-driver/mysql"
)



 func ConectarDB() *sql.DB {
  ConectionString := "root:root@/crud?charset=utf8&parseTime=True&loc=Local"

  db, erro := sql.Open("mysql", ConectionString)
  if erro != nil {
      log.Fatal(erro)
  }

  erro = db.Ping()
  if erro != nil {
      log.Fatal(erro)
  }
  return db

Você precisa do pacote com o driver do mysql.

No terminal digite:

go get github.com/go-sql-driver/mysql

Agora vamos mudar a função inicio para a controller.

Na raiz do projeto crie uma pasta chamada: controller:

dentro da pasta controller crie um arquivo chamado viewsController.go

mova a função Inicio do arquivo main.go para o arquivo viewsController.go

package controller


import (
  "html/template"
  "net/http"
)

var tmpl = template.Must(template.ParseGlob("views/*"))

func Inicio(w http.ResponseWriter, r *http.Request) {
  tmpl.ExecuteTemplate(w, "inicio", nil)
}

No arquivo main.go defina a rota para acessar a função Inicio dentro da controller

package main


import (
  "criarsite/controller"
  "net/http"
)


func main() {
  println("Ola mundo")


  http.HandleFunc("/", controller.Inicio)
 
  println("http://localhost:5000")
  http.ListenAndServe(":5000", nil)
}


no terminal digite:

go run main.go

Abra a rota:

http://localhost:5000

Agora vamos começar o CRUD

Dentro da pasta controller crie uma nova controller chamado artigoController.go

defina o pacote controller e crie uma instancia do repositório.

package controller

var repo = repositorio.New(database.ConectarDB())

Obs: A dinâmica vai ser.

Criar a função, criar a views e definir a rota:

Começaremos pela função de CriarArtigo.

func CriarArtigo(w http.ResponseWriter, r *http.Request) {
  if r.Method == http.MethodPost {
      if erro := r.ParseForm(); erro != nil {
          http.Error(w, "Erro a analizar os dados do formulario", http.StatusBadRequest)
          return
      }


      titulo := r.FormValue("titulo")
      descricao := r.FormValue("descricao")


      artigo := repositorio.CriarArtigoParams{
          Titulo:    titulo,
          Descricao: descricao,
      }


      if erro := repo.CriarArtigo(r.Context(), artigo); erro != nil {
          http.Error(w, "Erro ao criar um novo artigo", http.StatusInternalServerError)
          return
      }


      http.Redirect(w, r, "/listar", http.StatusSeeOther)
      return


  }


  tmpl.ExecuteTemplate(w, "criarartigo", nil)
}

Na pasta views crie um novo arquivo chamado: criar.html

{{ define "criarartigo" }}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
  <title>Criar Artigo</title>
</head>
<body>
  <h1>Cadastrar</h1>
  <div class="container mt-5">
<form action="/criar" method="post">


 
<div class="form-group">
  <label for="titulo">Titulo</label>
  <input type="text" class="form-control" name="titulo" id="titulo">
</div>


<div class="form-group">
  <label for="descricao">Descriçãp</label>
  <input type="text" class="form-control" name="descricao" id="descricao">
</div>


 <button type="submit" class="btn btn-primary"> Cadastrar</button>


</form>
</div>
</body>
</html>
{{ end }}

No arquivo main.go defina uma rota:

http.HandleFunc("/criar", controllers.CriarArtigo)

No terminal digite

go run main.go

Acesse a rota

http://localhost:5000/criar

Faça a criação de teste

Abra o mysql e verifique se o arquivo foi salvo com sucesso

SELECT * FROM `blog`.`artigo` LIMIT 1000;

Agora vamos criar a função ListarArtigos:

func ListarArtigos(w http.ResponseWriter, r *http.Request) {
  artigos, erro := repo.ListarArtigos(r.Context())
  if erro != nil {
      http.Error(w, "Erro ao listar artigos"+erro.Error(), http.StatusInternalServerError)
      return
  }
  erro = tmpl.ExecuteTemplate(w, "listarArtigos", artigos)
  if erro != nil {
      http.Error(w, "Erro ao renderizar a pagina"+erro.Error(), http.StatusInternalServerError)
      return
  }
}

Na pasta views crie um novo arquivo chamado: listar.html

{{ define "listarArtigos" }}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
  <title>Listar Artigo</title>
</head>
<body>
  <h1>Artigos</h1>
  <a href="/criar" role="button" class="btn btn-primary"> Cadastrar</a>
  <div class="container mt-5">
  <table  class="table table-striped">
      <thead>
          <tr>
              <td>Titulo</td>
              <td>Descrição</td>
              <td>Ações</td>
          </tr>
      </thead>
      <tbody>
{{ range .}}


<tr>
  <td>{{ .Titulo }}</td>
  <td>{{ .Descricao }}</td>
  <td>
      <a href="/detalhes?id={{ .ID }}" role="button" class="btn btn-info"> Ler</a>
      <a href="/editar?id={{ .ID }}" role="button" class="btn btn-warning"> Editar</a>
      <a href="/deletar?id={{ .ID }}" role="button" class="btn btn-danger"> Deletar</a>
  </td>
</tr>


{{ end }}


      </tbody>
  </table>
</div>
</body>
</html>
{{ end }}

No arquivo main.go defina a rota:

http.HandleFunc("/listar", controller.ListarArtigos)

No terminal digite:

go run main.go 

Abra a rota:

http://localhost:5000/criar

Verifique se mostra uma tabela com os artigos que você criou.

Vamos criar a função que mostra cada item individual: ArtigoPorId

func ArtigoPorId(w http.ResponseWriter, r *http.Request) {
  id := r.FormValue("id")
  ID, erro := strconv.Atoi(id)
  if erro != nil {
      http.Error(w, "ID do artigo invalido"+erro.Error(), http.StatusBadRequest)
      return
  }
  artigo, erro := repo.ArtigoPorId(r.Context(), int32(ID))
  if erro != nil {
      http.Error(w, "Erro ao buscar artigo"+erro.Error(), http.StatusInternalServerError)
      return
  }
  erro = tmpl.ExecuteTemplate(w, "detalhes", artigo)
  if erro != nil {
      http.Error(w, "Erro ao renderizar o artigo"+erro.Error(), http.StatusInternalServerError)
      return
  }
}

Na pasta views crie um novo arquivo chamado: detalhes.html

{{ define "detalhes" }}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
  <title>Detalhes do Artigo</title>
</head>
<body>
  <h1>Detalhes</h1>
  <div class="container mt-5">
  <p>{{ .ID }}</p>
  <p>{{ .Titulo }}</p>
  <p>{{ .Descricao }}</p>


  <div>
      <a href="/editar?id={{ .ID }}" role="button" class="btn btn-warning"> Editar</a>
      <a href="/" role="button" class="btn btn-dark"> Voltar</a>
  </div>
</div>
</body>
</html>
{{ end }}

No arquivo main.go defina a rota:

http.HandleFunc("/detalhes", controller.ArtigoPorId)

No terminal digite:

go run main.go 

Abra a rota:

http://localhost:5000/detalhes?id=1

Ou acesse a rota

http://localhost:5000/listar

e clique em algum botão chamado ler, isso vai abrir uma nova pagina com os detalhes do item escolhido.

Vamos criar a função EditarArtigo:

func EditarArtigo(w http.ResponseWriter, r *http.Request) {
  if r.Method == http.MethodPost {
      if erro := r.ParseForm(); erro != nil {
          http.Error(w, "Erro ao analiar os dados do formulario", http.StatusBadRequest)
          return
      }


      id := r.FormValue("id")
      ID, erro := strconv.Atoi(id)
      if erro != nil {
          http.Error(w, "Erro: ID do artigo invalido", http.StatusBadRequest)
          return
      }


      titulo := r.FormValue("titulo")
      descricao := r.FormValue("descricao")


      artigoAtualizado := repositorio.AtualizarArtigoParams{
          Titulo:    titulo,
          Descricao: descricao,
          ID:        int32(ID),
      }


      if erro := repo.AtualizarArtigo(r.Context(), artigoAtualizado); erro != nil {
          http.Error(w, "Erro ao atualizar o artigo", http.StatusInternalServerError)
          return
      }


      http.Redirect(w, r, "/listar", http.StatusSeeOther)
      return


  } else {
      id := r.FormValue("id")
      ID, erro := strconv.Atoi(id)
      if erro != nil {
          http.Error(w, "Erro: ID do artigo invalido", http.StatusBadRequest)
          return
      }


      artigo, erro := repo.ArtigoPorId(r.Context(), int32(ID))
      if erro != nil {
          http.Error(w, "Erro ao obter os dados do artigo", http.StatusInternalServerError)
          return
      }


      if erro := tmpl.ExecuteTemplate(w, "editar", artigo); erro != nil {
          http.Error(w, "Erro ao renderizar o formulario", http.StatusInternalServerError)
          return
      }


  }
}

Na pasta views crie um novo arquivo chamado editar.html

{{ define "editar" }}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
  <title>Editar Artigo</title>
</head>
<body>
  <h1>Editar Artigo</h1>
  <div class="container mt-5">
<form action="/editar" method="post">
  <input type="hidden" value="{{ .ID }}"   name="id" id="id">
 
<div class="form-group">
  <label for="titulo">Titulo</label>
  <input type="text" value="{{ .Titulo }}"   class="form-control" name="titulo" id="titulo">
</div>


<div class="form-group">
  <label for="descricao">Descriçãp</label>
  <input type="text" value="{{ .Descricao }}"   class="form-control" name="descricao" id="descricao">
</div>


 <button type="submit" class="btn btn-primary"> Editar</button>


</form>
</div>
</body>
</html>
{{ end }}

No arquivo main.go defina a rota

http.HandleFunc("/editar", controller.EditarArtigo)

No terminal digite:

go run main.go 

Abra a rota:

http://localhost:5000/editar?id=1

Ou acesse a rota

http://localhost:5000/listar

Clique no botão editar, isso vai abrir uma nova pagina com um formulário com os dados do artigo a ser editado.

Agora vamos criar a função que remove um artigo do banco de dados: Deletar

func Deletar(w http.ResponseWriter, r *http.Request) {
  if r.Method == http.MethodGet {
      id := r.FormValue("id")
      ID, erro := strconv.Atoi(id)
      if erro != nil {
          http.Error(w, "ID do artigo invalido", http.StatusBadRequest)
          return
      }
      if erro := repo.DeletarArtigo(r.Context(), int32(ID)); erro != nil {
          http.Error(w, "Erro ao deletar o artigo", http.StatusInternalServerError)
          return
      }


      http.Redirect(w, r, "/listar", http.StatusSeeOther)
      return


  } else {
      tmpl.ExecuteTemplate(w, "/listar", nil)
  }
} 

No arquivo main.go defina a rota

http.HandleFunc("/deletar", controller.Deletar)

No terminal digite:

go run main.go 

Abra a rota:

http://localhost:5000/listar

Clique no botão deletar, isso vai apagar o item do banco de dados.

Prontinho agora você acabou de criar um CRUD em Golang, este tutorial é simples, para ficar mais legal veja aqui como Criar site com uplaod de imagem .

Gostou? deixe seu comentário.

Compartilhe
Comentários (2)
Keoma Baudin
Keoma Baudin - 12/02/2024 00:47

Muito bom!

Claudinei Santos
Claudinei Santos - 08/02/2024 12:13

Parabéns Washington!


Bem detalhado e já dá para tentar aprender algo novo de forma guiada. Ficou massa seu artigo e obrigado por compartilhar.