Разработка Go(lang) API с echo и MySQL

Разработка Go(lang) API с echo и MySQL
В этой статье мы рассмотрим как создать API на базе MySQL с Go и echo. Ей мы начинаем цикл статей по разработке сайта photographerexcuses.com (название сайта можно перевести как «оправдания фотографов»).

Сайт представляет собой одностраничное веб-приложение на базе Vue.js, получающее данные (оправдания) от API на Go. Мы храним эти данные в базе MySQL.

Выбор веб-фреймворка на Golang

Многие вам скажут, что фреймворк не нужен и будут абсолютно правы. Однако без фреймворка многие вещи делать сложнее. В общем, выбирайте что хотите, а я выбрал echo для этого проекта.

Начнём с создания файла main.go, настройки в нём echo и регистрации необходимых роутов. Обратите внимание, что мы импортируем database/sql с _ перед ним. Это сделано для того, чтобы компилятор пропустил этот неиспользуемый импорт сейчас.

package main

import (
_ "database/sql"
"fmt"
"net/http"

_ "github.com/go-sql-driver/mysql"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"github.com/JonathanMH/goClacks/echo"
)

func main() {
// Echo instance
e := echo.New()
e.Use(goClacks.Terrify) // optional ;)

// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())

e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{echo.GET, echo.PUT, echo.POST, echo.DELETE},
}))

// Route => handler
e.GET("/", func(c echo.Context) error {

return c.JSON(http.StatusOK, "Hi!")
})

e.GET("/id/:id", func(c echo.Context) error {
requested_id := c.Param("id")
fmt.Println(requested_id);
return c.JSON(http.StatusOK, requested_id)
})

e.Logger.Fatal(e.Start(":4000"))
}

Также, возможно, вам не понадобится middleware.CORSWithConfig, тут всё зависит от того, как вы настроите фронтенд. Я рекомендую оставить это хотя бы на время разработки.

Открыв http://localhost:4000 вы должны увидеть строку “Hi!”, а открыв http://localhost:4000/id/42 вы должны увидеть “42”. Всё работает как надо, ведь мы хотели как получать случайный результат, так и определённое оправдание по его ID.

Ответ в формате JSON

Для того, чтобы наш API всегда отвечал определённым образом, создадим специальный тип, которым и будем передавать ответ:

type (
Excuse struct {
Error string `json:"error"`
Id string `json:"id"`
Quote string `json:"quote"`
}
)

Обработка ошибок очень важна в любом проекте, поэтому мы задали специальное поле Error для этого. В коде фронтенда (это будет один из следующих постов) мы сделаем проверку этого поля, чтобы знать, что все SQL-запросы прошли успешно.

Выборка случайного поля в SQL на Go

Разработка Go(lang) API с echo и MySQL

var quote string;
var id string;
err = db.QueryRow("SELECT id, quote FROM excuses ORDER BY RAND() LIMIT 1").Scan(&id, "e)

В этом кусочке кода показана выборка из базы данных из таблицы excuses, а методом .Scan() мы записываем её в инициированные выше переменные.

Затем мы воспользуемся структурой Excuse с передачей в неё значений наших переменных, вернём c как контекст echo, и JSON() со статусом ответа 200 и готовым ответом:

response := Excuse{Id: id, Error: "false", Quote: quote}
return c.JSON(http.StatusOK, response)

В результате мы получим следующий ответ в httpie:

(master)⚡ {33d8302486bd10b0fde64d2037652320e6f176a736d71849c0427b0d7398501a} http localhost:3131

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Length: 78
Content-Type: application/json; charset=utf-8
Date: Sat, 01 Apr 2017 12:51:01 GMT
Vary: Origin
X-Clacks-Overhead: GNU Terry Pratchett

{
"error": "false",
"id": "48",
"quote": "This is just how the industry works now."
}

А X-Clacks-Overhead: GNU Terry Pratchett приходит из middleware goClacks, которое я написал в честь известного фэнтезийного писателя.

Собираем воедино

Нам понадобится SQL только для создания таблицы в базе данных:

CREATE TABLE `excuses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`quote` text,
`author` varchar(191) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

Немного доработанный, хоть и без обработки ошибок, наш код будет выглядеть так, как он описан в конце топика. И вы также можете повторно использовать MySQL-соединение вместо создания нового для каждого запроса.

Также можно будет сделать некоторые интересные вещи, вроде таких как

  • Кэширование последних запросов к базе данных и проверка ответа в памяти перед созданием очередного SQL запроса
  • Ограничения скорости по IP адресу

Ниже полная версия кода приложения:

package main

import (
"database/sql"
"fmt"
"net/http"

_ "github.com/go-sql-driver/mysql"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"github.com/JonathanMH/goClacks/echo"
)

type (
Excuse struct {
Error string `json:"error"`
Id string `json:"id"`
Quote string `json:"quote"`
}
)

func main() {
// Echo instance
e := echo.New()
e.Use(goClacks.Terrify)

// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())

e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{echo.GET, echo.PUT, echo.POST, echo.DELETE},
}))

// Route => handler
e.GET("/", func(c echo.Context) error {
db, err := sql.Open("mysql", "db_user:db_password@tcp(SERVER_IP:PORT)/database_name")

if err != nil {
fmt.Println(err.Error())
response := Excuse{Id: "", Error: "true", Quote: ""}
return c.JSON(http.StatusInternalServerError, response)
}
defer db.Close()

var quote string;
var id string;
err = db.QueryRow("SELECT id, quote FROM excuses ORDER BY RAND() LIMIT 1").Scan(&id, "e)

if err != nil {
fmt.Println(err)
}

fmt.Println(quote);
response := Excuse{Id: id, Error: "false", Quote: quote}
return c.JSON(http.StatusOK, response)
})

e.GET("/id/:id", func(c echo.Context) error {
requested_id := c.Param("id")
fmt.Println(requested_id);
db, err := sql.Open("mysql", "db_user:db_password@tcp(SERVER_IP:PORT)/database_name")

if err != nil {
fmt.Println(err.Error())
response := Excuse{Id: "", Error: "true", Quote: ""}
return c.JSON(http.StatusInternalServerError, response)
}
defer db.Close()

var quote string;
var id string;
err = db.QueryRow("SELECT id, quote FROM excuses WHERE id = ?", requested_id).Scan(&id, "e)

if err != nil {
fmt.Println(err)
}

response := Excuse{Id: id, Error: "false", Quote: quote}
return c.JSON(http.StatusOK, response)
})

e.Logger.Fatal(e.Start(":4000"))
}

Источник: «Building a Go(lang) API with echo and MySQL»

Продолжение: http://tehnojam.ru/category/development/deploj-golang-prilozhenija-bez-docker.html

Leave a Comment