Хранение и получение данных из MySQL в API-интерфейсах Go
Мы будем использовать реальную базу данных, например MySQL, для хранения и получения данных для своих таблиц.
Предварительные требования
Установка 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 , и .
.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 библиотеку database/sql для создания соединения с базой данных, эта функция возвращает клиента, с помощью которого мы взаимодействуем/выполняем операции над таблицами.
Теперь внесем изменения в нашу структуру Server, чтобы она содержала функцию D
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.
Теперь давайте снова напишем функции GetTodos и AddTodo с использованием клиента DB.
Поскольку наш клиент БД находится в структуре Server, то для доступа к нему в функциях GetTodos и AddTodo необходимо сделать их методами структуры Server, а также внести изменения в функцию 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 мы создали запрос для получения всех Todos из таблицы Todos, отсортированных в порядке убывания по времени создания. Клиент DB предоставляет нам метод Query, с помощью которого мы можем выполнить наш запрос, который возвращает строки, затем мы деструктурируем их и возвращаем массив todos в виде json.
В функции AddTodo мы сначала берем тело запроса и отображаем его в TodoRequestBody, затем создаем запрос на вставку, выполняем этот запрос с помощью метода 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.