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.