在上一篇博客文章中,我们为Todos应用程序创建了基本的CRUD端点。现在,我们将使用像MySQL
这样的真实数据库为其表存储和接收数据。
先决条件
应该遍历上一个博客文章,以便基本理解Write APIs in Go using go-chi。
MySQL
在您的系统和基本工作知识中安装。
代码
因此,让我们首先设置我们将用于存储烟多的桌子。首先,我们将创建一个数据库Practice
,然后在该数据库中我们创建Todos
表。
CREATE DATABASE IF NOT EXISTS `Practice`;
CREATE TABLE IF NOT EXISTS `Practice`.`Todos` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`task` VARCHAR(255) NOT NULL,
`completed` INT NOT NULL DEFAULT 0,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
请注意,我们在此Todos
表中添加了新字段,id
,created_at
,updated_at
。当我们在此表上执行操作时,我们将查看如何使用这些。
现在创建一个环境文件,将您的mysql <user>
,<password>
和<database>
添加到其中。
.env
PORT=:5000
DATABASE_URL=<user>:<password>@tcp(localhost)/<database>?parseTime=true
DB_DRIVER=mysql
现在,让我们安装几个依赖项
go get github.com/joho/godotenv
go get github.com/go-sql-driver/mysql
现在让我们开始编写用于连接到数据库的代码
main.go
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"github.com/go-chi/chi/v5"
_ "github.com/go-sql-driver/mysql"
"github.com/joho/godotenv"
)
var (
DATABASE_URL, DB_DRIVER, PORT string
)
func init() {
err := godotenv.Load()
if err != nil {
log.Fatalln("Couldn't load env on startup!!")
}
DATABASE_URL = os.Getenv("DATABASE_URL")
DB_DRIVER = os.Getenv("DB_DRIVER")
PORT = os.Getenv("PORT")
}
func DBClient() (*sql.DB, error) {
db, err := sql.Open(DB_DRIVER, DATABASE_URL)
if err != nil {
return nil, err
}
if err := db.Ping(); err != nil {
return nil, err
}
fmt.Println("Connected to DB")
return db, nil
}
在这里,我们首先将环境变量加载到init
函数中,然后我们创建了一个DBClient
函数,在该函数中,我们使用GO的Innouilt库database/sql
用于与数据库创建连接,此函数返回客户端,使用我们在互动/执行操作的客户端,桌子。
现在,让我们更改我们的Server
struct,以使其容纳DB客户端。
type Server struct {
Router *chi.Mux
DB *sql.DB
}
func CreateServer(db *sql.DB) *Server {
server := &Server{
Router: chi.NewRouter(),
DB: db,
}
return server
}
func main() {
db, err := DBClient()
if err != nil {
log.Fatalln("Couldn't connect to DB")
}
server := CreateServer(db)
server.MountHandlers()
fmt.Println("server running on port:5000")
http.ListenAndServe(PORT, server.Router)
}
现在,使用这些更改,您可以开始并检查您是否能够连接到数据库。
➜ go-api git:(main) ✗ make run
Connected to DB
server running on port:5000
注意:如果您遇到任何错误,请检查您是否导入了所有必要的库,并在.env
文件中存在创建凭据
现在,让我们在使用DB客户端的情况下再次编写GetTodos
和AddTodo
功能
由于,我们在Server
struct中存在DB客户端,因此要在GetTodos
和AddTodo
函数中访问它,我们必须将它们作为Server
struct的方法,我们还必须在MountHandlers
函数中进行更改。
func (server *Server) MountHandlers() {
server.Router.Get("/greet", Greet)
todosRouter := chi.NewRouter()
todosRouter.Group(func(r chi.Router) {
// make these as a methods of Server struct as to access DB client
r.Get("/", server.GetTodos)
r.Post("/", server.AddTodo)
})
server.Router.Mount("/todos", todosRouter)
}
type Todo struct {
Id int `json:"id"`
Task string `json:"task"`
Completed bool `json:"completed"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type TodoRequestBody struct {
Task string `json:"task"`
Completed bool `json:"completed"`
}
func scanRow(rows *sql.Rows) (*Todo, error) {
todo := new(Todo)
err := rows.Scan(&todo.Id,
&todo.Task,
&todo.Completed,
&todo.CreatedAt,
&todo.UpdatedAt,
)
if err != nil {
return nil, err
}
return todo, nil
}
func (server *Server) AddTodo(w http.ResponseWriter, r *http.Request) {
todo := new(TodoRequestBody)
if err := json.NewDecoder(r.Body).Decode(todo); err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Please enter a correct Todo!!"))
return
}
query := `INSERT INTO Todos (task, completed) VALUES (?, ?)`
_, err := server.DB.Exec(query, todo.Task, todo.Completed)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Something bad happened on the server :("))
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("Todo added!!"))
}
func (server *Server) GetTodos(w http.ResponseWriter, r *http.Request) {
query := `SELECT * FROM Todos ORDER BY created_at DESC`
rows, err := server.DB.Query(query)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Something bad happened on the server :("))
return
}
var todos []*Todo
for rows.Next() {
todo, err := scanRow(rows)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Something bad happened on the server :("))
return
}
todos = append(todos, todo)
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(todos)
}
在GetTodos
函数中,我们创建了查询,以根据创建的at创建的descender顺序从Todos
表中获取所有戒酒。 DB客户端为我们提供了一种方法Query
,我们可以通过该方法运行查询,其返回行,然后销毁它们并将Todos数组返回为JSON。
在AddTodo
函数中,我们首先将请求主体映射到TodoRequestBody
,然后创建一个插入查询,我们使用DB Client提供的Exec
方法执行此查询。
使用curl
点击下面的端点
➜ go-api git:(main) ✗ curl -X POST 'http://localhost:5000/todos' -d '{"task": "Learn Go", "completed": false}'
Todo added!!
➜ go-api git:(main) ✗ curl -X GET 'http://localhost:5000/todos'
[{"id":1,"task":"Learn Go","completed":false,"created_at":"2023-09-03T15:18:54Z","updated_at":"2023-09-03T15:18:54Z"}]
您还可以直接看到记录到您的MySQL
表中
mysql> SELECT * FROM Todos ORDER BY created_at DESC;
+----+----------+-----------+---------------------+---------------------+
| id | task | completed | created_at | updated_at |
+----+----------+-----------+---------------------+---------------------+
| 1 | Learn Go | 0 | 2023-09-03 15:18:54 | 2023-09-03 15:18:54 |
+----+----------+-----------+---------------------+---------------------+
1 row in set (0.01 sec)
我鼓励您独自编写PUT /todos
,DELETE /todos
端点。
结论
就是这样,在此博客文章中,我们介绍了如何连接和使用MySQL
数据库并在表上执行操作并使用API返回响应。
github :https://github.com/the-arcade-01/go-api
感谢您的阅读直到最后,非常感谢!