Больше не нужно искать работу мечты — присоединяйтесь к команде Клауда

Как создать и развернуть приложение на Gin: быстрый и простой деплой с Apps

Никита Долгих
Никита Долгих
Технический писатель
28 июня 2024 г.
32
14 минут чтения
Средний рейтинг статьи: 5

Gin — это высокоэффективный HTTP-веб-фреймворк, написанный на языке программирования Go, который предоставляет разработчикам мощные инструменты для создания веб-приложений, RESTful API и микросервисов. Он выделяется среди других фреймворков своей высокой скоростью обработки запросов, гибкостью настройки и простотой использования.

Одним из ключевых преимуществ Gin является его производительность. Gin использует минималистичный подход к обработке HTTP-запросов, что позволяет ему быть одним из самых быстрых фреймворков на рынке. Он основан на модуле net/http стандартной библиотеки Go, что обеспечивает отличную интеграцию с существующей экосистемой Go и позволяет использовать возможности конкурентного программирования Go для обработки большого количества одновременных запросов.

Другим важным преимуществом Gin является его простота. Синтаксис и структура Gin интуитивно понятны, что снижает порог вхождения для разработчиков и ускоряет процесс разработки. Встроенная система маршрутизации позволяет легко определять и обрабатывать маршруты, а мощная система middlewares позволяет гибко управлять обработкой запросов.

Гибкость Gin также заслуживает особого внимания. Он предоставляет возможность расширения функциональности через плагины и middlewares, что позволяет адаптировать его под специфические требования проекта. Встроенная поддержка JSON и других форматов данных упрощает создание RESTful API, а инструменты для работы с запросами и ответами позволяют легко управлять данными.

В дополнение к этому, Gin обладает активным сообществом и хорошей документацией, что делает его отличным выбором для разработчиков, ищущих надежный и поддерживаемый фреймворк. Существует множество ресурсов, включая примеры кода, руководства и библиотеки, которые облегчают процесс обучения и разработки.

Создание приложения

Описание функционала

Наше приложение будет поддерживать основные CRUD-операции (создание, чтение, обновление, удаление) для заметок через RESTful API. В ходе разработки мы обсудим ключевые аспекты интеграции Gin и ORM-библиотеки GORM, а также продемонстрируем, как обеспечить безопасность и производительность нашего веб-приложения. Основные возможности нашего приложения включают:

  1. Создание новой заметки:

    • Пользователь сможет добавить новую заметку, отправив POST-запрос с заголовком и содержимым заметки.

    • Приложение сохранит новую заметку в базе данных и вернет её уникальный идентификатор.

  2. Получение всех заметок:

    • Пользователь сможет запросить список всех заметок, отправив GET-запрос.

    • Приложение вернет все заметки из базы данных в формате JSON.

  3. Получение заметки по ID:

    • Пользователь сможет получить конкретную заметку по её идентификатору, отправив GET-запрос с указанным ID.

    • Приложение найдет заметку в базе данных и вернет её в формате JSON.

  4. Обновление существующей заметки:

    • Пользователь сможет обновить существующую заметку, отправив PUT-запрос с новым заголовком и содержимым заметки.

    • Приложение обновит данные заметки в базе данных и вернет обновленную заметку.

  5. Удаление заметки:

    • Пользователь сможет удалить заметку по её идентификатору, отправив DELETE-запрос с указанным ID.

    • Приложение удалит заметку из базы данных и вернет статус успешного выполнения операции.

Подготовка проекта

Подразумевается, что у вас установлен Go версии 1.22 (установить его можно, например, по одной из этих инструкций Windows, Ubuntu, MacOS). При использовании более ранних версий, в процессе запуска проекта могут возникнуть ошибки. Также у вас есть базовое представление о Git и аккаунт на одном из сервисов хостинга Git репозиториев (GitHub, GitLab, Bitbucket, Gitea и т.п.). 

  1. Создадим директорию проекта командой:

mkdir GinTW

И перейдем в нее, выполнив:

cd GinTW
  1. Инициируем новый модуль Go командой: 

go mod init gin-notes-api
  1. Установим необходимые для проекта пакеты: Gin, GORM и SQLite для работы с базой данных, командами:

go get -u github.com/gin-gonic/gin
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
  1. Создадим структуру проекта. Она должна выглядеть вот так:

GinTW/
├── go.mod
├── main.go
├── models/
│   └── note.go
├── handlers/
│   └── note_handlers.go
├── storage/
│   └── storage.go
│   └── database.go

Создать структуру вы можете средствами IDE или выполнив следующую команду в терминале:

mkdir -p models handlers storage && touch go.mod main.go models/note.go handlers/note_handlers.go storage/storage.go storage/database.go

Структура приложения

  • models/note.go

Определяет структуру данных для заметок. Модель Note описывает поля заметки и используется для работы с базой данных через ORM-библиотеку GORM.

package models

// Определение структуры заметки
type Note struct {
	ID  	int	`json:"id" gorm:"primaryKey;autoIncrement"` // Уникальный идентификатор заметки, автоматически инкрементируемый
	Title   string `json:"title"`                           	// Заголовок заметки
	Content string `json:"content"`                         	// Содержание заметки
}
  • storage/database.go

Этот файл содержит функции для инициализации базы данных и получения экземпляра базы данных. Здесь используется ORM-библиотека GORM для работы с базой данных SQLite.

package storage

import (
	// Импортирование необходимых пакетов
	"gorm.io/driver/sqlite" // Драйвер для работы с SQLite
	"gorm.io/gorm"      	// Основная библиотека GORM для работы с ORM
	"gin-notes-api/models" // Импортирование пакета с определением моделей данных
)

// Объявление глобальной переменной для хранения экземпляра базы данных
var db *gorm.DB

// Функция инициализации базы данных
func InitDatabase() error {
	var err error
	// Открытие подключения к базе данных SQLite с использованием GORM
	db, err = gorm.Open(sqlite.Open("notes.db"), &gorm.Config{})
	if err != nil {
    	// Возвращение ошибки, если подключение не удалось
    	return err
	}
	// Автоматическое создание таблицы для модели Note, если она еще не существует
	return db.AutoMigrate(&models.Note{})
}

// Функция для получения экземпляра базы данных
func GetDB() *gorm.DB {
	// Возвращение глобальной переменной db, содержащей подключение к базе данных
	return db
}
  • storage/storage.go

Этот код обеспечивает выполнение основных операций CRUD (создание, чтение, обновление, удаление) для модели Note, используя GORM для взаимодействия с базой данных SQLite.

package storage

import (
	"gin-notes-api/models" // Импортирование пакета с определением моделей данных
)

// Функция для получения всех заметок из базы данных
func GetAllNotes() []models.Note {
	var notes []models.Note
	// Использование GORM для выполнения SQL-запроса SELECT и заполнения среза notes
	db.Find(¬es)
	return notes // Возвращение всех найденных заметок
}

// Функция для получения заметки по ID
func GetNoteByID(id int) *models.Note {
	var note models.Note
	// Использование GORM для выполнения SQL-запроса SELECT с условием WHERE id = id заметки
	if result := db.First(¬e, id); result.Error != nil {
    	return nil // Возвращение nil, если заметка с указанным ID не найдена
	}
	return ¬e // Возвращение найденной заметки
}

// Функция для создания новой заметки
func CreateNote(title, content string) models.Note {
	note := models.Note{
    	Title:   title,
    	Content: content,
	}
	// Использование GORM для выполнения SQL-запроса INSERT и сохранения новой заметки в базе данных
	db.Create(¬e)
	return note // Возвращение созданной заметки
}

// Функция для обновления существующей заметки по ID
func UpdateNote(id int, title, content string) *models.Note {
	var note models.Note
	// Использование GORM для выполнения SQL-запроса SELECT с условием WHERE id = id заметки
	if result := db.First(¬e, id); result.Error != nil {
    	return nil // Возвращение nil, если заметка с указанным ID не найдена
	}
	note.Title = title
	note.Content = content
	// Использование GORM для выполнения SQL-запроса UPDATE и сохранения обновленной заметки в базе данных
	db.Save(¬e)
	return ¬e // Возвращение обновленной заметки
}

// Функция для удаления заметки по ID
func DeleteNoteByID(id int) bool {
	// Использование GORM для выполнения SQL-запроса DELETE с условием WHERE id = id заметки
	if result := db.Delete(&models.Note{}, id); result.Error != nil {
    	return false // Возвращение false, если удаление не удалось
	}
	return true // Возвращение true при успешном удалении заметки
}
  • handlers/note_handlers.go

Этот файл содержит функции-обработчики (хендлеры) для обработки HTTP-запросов. Эти функции вызываются в ответ на различные маршруты и выполняют действия, такие как создание, получение, обновление и удаление заметок.

package handlers

import (
	"net/http"                   	// Пакет для работы с HTTP
	"strconv"                    	// Пакет для конвертации строк в другие типы данных

	"github.com/gin-gonic/gin"   	// Веб-фреймворк Gin
	"gin-notes-api/storage" // Импортирование модуля для работы с базой данных
)

// Обработчик для получения всех заметок
func GetNotes(c *gin.Context) {
	notes := storage.GetAllNotes() // Получение всех заметок из хранилища
	c.JSON(http.StatusOK, notes)   // Возвращение заметок в формате JSON с кодом 200 (OK)
}

// Обработчик для получения заметки по ID
func GetNoteByID(c *gin.Context) {
	// Конвертация параметра ID из строки в целое число
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
    	// Возвращение ошибки 400 (Bad Request), если ID некорректен
    	c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid note ID"})
    	return
	}
	// Получение заметки по ID из хранилища
	note := storage.GetNoteByID(id)
	if note == nil {
    	// Возвращение ошибки 404 (Not Found), если заметка не найдена
    	c.JSON(http.StatusNotFound, gin.H{"error": "Note not found"})
    	return
	}
	// Возвращение найденной заметки в формате JSON с кодом 200 (OK)
	c.JSON(http.StatusOK, note)
}

// Обработчик для создания новой заметки
func CreateNote(c *gin.Context) {
	// Структура для хранения входных данных
	var input struct {
    	Title   string `json:"title" binding:"required"`
    	Content string `json:"content" binding:"required"`
	}
	// Привязка входных данных в формате JSON к структуре input
	if err := c.ShouldBindJSON(&input); err != nil {
    	// Возвращение ошибки 400 (Bad Request), если входные данные некорректны
    	c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    	return
	}
	// Создание новой заметки в хранилище
	note := storage.CreateNote(input.Title, input.Content)
	// Возвращение созданной заметки в формате JSON с кодом 201 (Created)
	c.JSON(http.StatusCreated, note)
}

// Обработчик для обновления существующей заметки по ID
func UpdateNoteByID(c *gin.Context) {
	// Конвертация параметра ID из строки в целое число
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
    	// Возвращение ошибки 400 (Bad Request), если ID некорректен
    	c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid note ID"})
    	return
	}
	// Структура для хранения входных данных
	var input struct {
    	Title   string `json:"title" binding:"required"`
    	Content string `json:"content" binding:"required"`
	}
	// Привязка входных данных в формате JSON к структуре input
	if err := c.ShouldBindJSON(&input); err != nil {
    	// Возвращение ошибки 400 (Bad Request), если входные данные некорректны
    	c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    	return
	}
	// Обновление заметки в хранилище
	note := storage.UpdateNote(id, input.Title, input.Content)
	if note == nil {
    	// Возвращение ошибки 404 (Not Found), если заметка не найдена
    	c.JSON(http.StatusNotFound, gin.H{"error": "Note not found"})
    	return
	}
	// Возвращение обновленной заметки в формате JSON с кодом 200 (OK)
	c.JSON(http.StatusOK, note)
}

// Обработчик для удаления заметки по ID
func DeleteNoteByID(c *gin.Context) {
	// Конвертация параметра ID из строки в целое число
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
    	// Возвращение ошибки 400 (Bad Request), если ID некорректен
    	c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid note ID"})
    	return
	}
	// Удаление заметки из хранилища
	if success := storage.DeleteNoteByID(id); !success {
    	// Возвращение ошибки 404 (Not Found), если заметка не найдена
    	c.JSON(http.StatusNotFound, gin.H{"error": "Note not found"})
    	return
	}
	// Возвращение кода 204 (No Content) при успешном удалении
	c.Status(http.StatusNoContent)
}
  • main.go

Этот файл является основной точкой входа в приложение. В нем происходит инициализация базы данных и настройка маршрутов для обработки HTTP-запросов с использованием веб-фреймворка Gin.

package main

import (
	"log"                     	// Пакет для логирования
	"github.com/gin-gonic/gin"	// Веб-фреймворк Gin
	"gin-notes-api/handlers" // Импортирование модуля с обработчиками запросов
	"gin-notes-api/storage"  // Импортирование модуля для работы с базой данных
)

func main() {
	// Инициализация базы данных
	if err := storage.InitDatabase(); err != nil {
    	log.Fatalf("Failed to initialize database: %v", err) // Логирование ошибки и завершение программы, если инициализация базы данных не удалась
	}

	// Создание нового роутера Gin с настройками по умолчанию
	router := gin.Default()

	// Определение маршрутов и привязка их к соответствующим обработчикам
	router.GET("/notes", handlers.GetNotes)         	// Маршрут для получения всех заметок
	router.GET("/notes/:id", handlers.GetNoteByID)  	// Маршрут для получения заметки по ID
	router.POST("/notes", handlers.CreateNote)      	// Маршрут для создания новой заметки
	router.PUT("/notes/:id", handlers.UpdateNoteByID)   // Маршрут для обновления заметки по ID
	router.DELETE("/notes/:id", handlers.DeleteNoteByID) // Маршрут для удаления заметки по ID

	// Запуск веб-сервера на порту 8080
	router.Run(":8080")
}

Теперь мы можем запустить приложение локально и протестировать его функциональность.

Для запуска используем команду:

go run main.go

Примеры curl-запросов для тестирования функционала

Создать новую заметку:

curl -X POST http://localhost:8080/notes -H "Content-Type: application/json" -d '{"title":"Заголовок","content":"Тело заметки"}'

Получить все заметки:

curl -X GET http://localhost:8080/notes

Получить заметку по ID:

curl -X GET http://localhost:8080/notes/1

Обновить заметку по ID:

curl -X PUT http://localhost:8080/notes/1 -H "Content-Type: application/json" -d '{"title":"Обновленный заголовок","content":"Обновленное тело заметки"}'

Удалить заметку по ID:

curl -X DELETE http://localhost:8080/notes/1

Image1

Деплой приложения в Timeweb Cloud Apps

Для развертывания приложения с помощью Timeweb Cloud Apps, необходимо разместить проект в git-репозитории. В данном примере используется GitHub.

Создание и загрузка репозитория

Создайте новый репозиторий на GitHub и инициализируйте его локально в директории вашего проекта:

git init -b main
git add .
git commit -m 'First commit'

Теперь можем загрузить репозиторий на удаленный сервер, воспользовавшись командами, которые указаны при создании нового репозитория github:

git remote add origin git@github.com:ваш_пользователь/ваш_репозиторий.git
git push -u origin main

Настройка Timeweb Cloud Apps

  • Перейдите в раздел Apps и нажмите «Создать». 

  • В разделе «Тип» выберите вкладку «Backend» и найдите фреймворк Gin.

  • Подключите ваш GitHub-аккаунт, предоставив доступ к репозиториям, или выберите необходимый репозиторий вручную.

Image2

  • После подключения Github-аккаунта, разделе «Репозиторий» выберите ваш репозиторий с приложением.

Image3

  • Выберите регион, в котором будет размещено приложение.

  • В разделе «Конфигурация» выберите минимальные настройки, их будет достаточно для текущего проекта. При необходимости, конфигурацию можно будет изменить позже.

  • В разделе «Настройки приложения» оставьте значения по умолчанию. В более сложных проектах можно указать переменные окружения и команду для сборки приложения.

  • Укажите имя будущего приложения и нажмите «Запустить деплой».

Процесс деплоя может занять до 10 минут. После завершения вы увидите сообщение «Deployment successfully completed» в логах деплоя.

Перейдите в раздел «Настройки» на странице приложения, где будет указан домен, привязанный к вашему приложению. 

Image4

В этом же разделе можно изменить конфигурацию сервера, отредактировать настройки деплоя и изменить привязку домена. При подключении своего домена, для него автоматически будет установлен сертификат Let’s Encrypt, который будет автоматически продлеваться за 7 дней до истечения срока.

Для проверки, что приложение работает корректно, можем выполнить curl-запрос, изменив localhost на привязанный домен:

curl -X GET https://ваш_домен/notes

Заключение

Мы разработали базовое веб-приложение для управления заметками с использованием фреймворка Gin и библиотеки GORM. Созданный RESTful API поддерживает основные CRUD-операции, что делает приложение простым и удобным в использовании.

Gin показал себя как эффективный и простой в освоении инструмент. Его система маршрутизации и поддержка одновременных запросов упростили процесс разработки. GORM помог легко работать с базой данных, автоматизировав многие задачи.

Приложение успешно развернуто на платформе Timeweb Cloud Apps, что сделало процесс деплоя быстрым и надежным. Timeweb Cloud Apps предоставил все необходимые инструменты для комфортного управления приложением.

В будущем можно добавить новые функции, такие как аутентификация пользователей и улучшенные возможности поиска заметок.

Этот проект продемонстрировал, как современные инструменты разработки, такие как Gin и GORM, могут упростить создание веб-приложений.

28 июня 2024 г.
32
14 минут чтения
Средний рейтинг статьи: 5
Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
Комментарии 0