从GO API中的MySQL存储和检索数据
#初学者 #api #go

在上一篇博客文章中,我们为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表中添加了新字段,idcreated_atupdated_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客户端的情况下再次编写GetTodosAddTodo功能
由于,我们在Server struct中存在DB客户端,因此要在GetTodosAddTodo函数中访问它,我们必须将它们作为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 /todosDELETE /todos端点。

结论

就是这样,在此博客文章中,我们介绍了如何连接和使用MySQL数据库并在表上执行操作并使用API​​返回响应。

github https://github.com/the-arcade-01/go-api

感谢您的阅读直到最后,非常感谢!