Gin is a highly efficient HTTP web framework written in the Go programming language, providing developers with powerful tools for building web applications, RESTful APIs, and microservices. It stands out among other frameworks due to its high request processing speed, flexible configuration, and ease of use.
One of Gin’s key advantages is its performance. Gin uses a minimalist approach to handling HTTP requests, making it one of the fastest frameworks on the market. It is built on the net/http module from Golang’s standard library, ensuring excellent integration with Go’s ecosystem and enabling the use of Go’s concurrency features to handle a large number of simultaneous requests.
Another important advantage of Gin is its simplicity. The syntax and structure of Gin are intuitive, reducing the learning curve for developers and speeding up the development process. Its built-in routing system makes it easy to define and handle routes, while its powerful middleware system allows flexible request handling.
Gin’s flexibility is also worth mentioning. It allows you to extend functionality through plugins and middleware, enabling adaptation to specific project requirements. Built-in support for JSON and other data formats simplifies the creation of RESTful APIs, and tools for handling requests and responses make data management straightforward.
In addition, Gin has an active community and solid documentation, making it an excellent choice for developers looking for a reliable and well-supported framework. There are plenty of resources, including code examples, guides, and libraries, that make the learning and development process easier.
Our application will support basic CRUD operations (Create, Read, Update, Delete) for notes through a RESTful API. During development, we will discuss key aspects of integrating Gin with the GORM ORM library and demonstrate how to ensure the security and performance of our web application. The main features of our application include:
Creating a New Note
Retrieving All Notes
Retrieving a Note by ID
Updating an Existing Note
Deleting a Note
It is assumed that you have Go version 1.22 installed (you can install it using one of these guides: Windows, Ubuntu, MacOS). If you use an earlier version, errors may occur during the project setup and launch process. Additionally, you should have a basic understanding of Git and an account on one of the Git repository hosting services (GitHub, GitLab, Bitbucket, Gitea, etc.).
Run the following command to create the project directory:
mkdir GinApp
Navigate into the newly created directory:
cd GinApp
Run the following command to initialize a new Golang module:
go mod init gin-notes-api
We will install the necessary packages for the project: Gin, GORM, and SQLite (for database interaction) using the following commands:
go get -u github.com/gin-gonic/gin
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
The project structure should look like this:
GinApp/
├── go.mod
├── main.go
├── models/
│ └── note.go
├── handlers/
│ └── note_handlers.go
├── storage/
│ ├── storage.go
│ └── database.go
You can create this structure using your IDE’s file explorer or by running the following command in the terminal:
mkdir -p models handlers storage && touch go.mod main.go models/note.go handlers/note_handlers.go storage/storage.go storage/database.go
Defines the data structure for notes. The Note model describes the fields of a note and is used to interact with the database through the GORM ORM library.
package models
// Definition of the Note structure
type Note struct {
ID int `json:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier, auto-incremented
Title string `json:"title"` // Note title
Content string `json:"content"` // Note content
}
This file contains functions for initializing the database and retrieving the database instance. GORM is used to work with the SQLite database.
package storage
import (
"gorm.io/driver/sqlite" // Driver for SQLite
"gorm.io/gorm" // GORM ORM library
"gin-notes-api/models" // Importing the package with data models
)
// Declare a global variable to store the database instance
var db *gorm.DB
// Function to initialize the database
func InitDatabase() error {
var err error
db, err = gorm.Open(sqlite.Open("notes.db"), &gorm.Config{}) // Connect to SQLite using GORM
if err != nil {
return err // Return an error if the connection fails
}
return db.AutoMigrate(&models.Note{}) // Automatically create the Note table if it doesn’t exist
}
// Function to retrieve the database instance
func GetDB() *gorm.DB {
return db // Return the global db variable containing the database connection
}
This file provides CRUD (Create, Read, Update, Delete) operations for the Note model using GORM to interact with the SQLite database.
package storage
import (
"gin-notes-api/models" // Importing the package with data models
)
// Function to retrieve all notes from the database
func GetAllNotes() []models.Note {
var notes []models.Note
db.Find(¬es) // Use GORM to execute a SELECT query and fill the notes slice
return notes // Return all retrieved notes
}
// Function to retrieve a note by ID
func GetNoteByID(id int) *models.Note {
var note models.Note
if result := db.First(¬e, id); result.Error != nil {
return nil // Return nil if the note with the specified ID is not found
}
return ¬e // Return the found note
}
// Function to create a new note
func CreateNote(title, content string) models.Note {
note := models.Note{
Title: title,
Content: content,
}
db.Create(¬e) // Use GORM to execute an INSERT query and save the new note
return note // Return the created note
}
// Function to update an existing note by ID
func UpdateNote(id int, title, content string) *models.Note {
var note models.Note
if result := db.First(¬e, id); result.Error != nil {
return nil // Return nil if the note with the specified ID is not found
}
note.Title = title
note.Content = content
db.Save(¬e) // Use GORM to execute an UPDATE query and save the updated note
return ¬e // Return the updated note
}
// Function to delete a note by ID
func DeleteNoteByID(id int) bool {
if result := db.Delete(&models.Note{}, id); result.Error != nil {
return false // Return false if deletion fails
}
return true // Return true if the note is successfully deleted
}
This file contains handler functions for processing HTTP requests. These functions are triggered in response to different routes and perform actions such as creating, retrieving, updating, and deleting notes.
package handlers
import (
"net/http" // HTTP package
"strconv" // For converting strings to other data types
"github.com/gin-gonic/gin" // Gin web framework
"gin-notes-api/storage" // Import the storage module for database operations
)
// Handler for retrieving all notes
func GetNotes(c *gin.Context) {
notes := storage.GetAllNotes() // Fetch all notes from storage
c.JSON(http.StatusOK, notes) // Return notes in JSON format with a 200 OK status
}
// Handler for retrieving a note by ID
func GetNoteByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id")) // Convert the ID parameter from string to integer
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ // Return 400 Bad Request if the ID is invalid
"error": "Invalid note ID",
})
return
}
note := storage.GetNoteByID(id) // Fetch the note by ID from storage
if note == nil {
c.JSON(http.StatusNotFound, gin.H{ // Return 404 Not Found if the note is not found
"error": "Note not found",
})
return
}
c.JSON(http.StatusOK, note) // Return the found note in JSON format with a 200 OK status
}
// Handler for creating a new note
func CreateNote(c *gin.Context) {
var input struct {
Title string `json:"title" binding:"required"`
Content string `json:"content" binding:"required"`
}
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{ // Return 400 Bad Request if the input data is invalid
"error": err.Error(),
})
return
}
note := storage.CreateNote(input.Title, input.Content) // Create a new note in storage
c.JSON(http.StatusCreated, note) // Return the created note in JSON format with a 201 Created status
}
// Handler for updating an existing note by ID
func UpdateNoteByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id")) // Convert the ID parameter from string to integer
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ // Return 400 Bad Request if the ID is invalid
"error": "Invalid note ID",
})
return
}
var input struct {
Title string `json:"title" binding:"required"`
Content string `json:"content" binding:"required"`
}
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{ // Return 400 Bad Request if the input data is invalid
"error": err.Error(),
})
return
}
note := storage.UpdateNote(id, input.Title, input.Content) // Update the note in storage
if note == nil {
c.JSON(http.StatusNotFound, gin.H{ // Return 404 Not Found if the note is not found
"error": "Note not found",
})
return
}
c.JSON(http.StatusOK, note) // Return the updated note in JSON format with a 200 OK status
}
// Handler for deleting a note by ID
func DeleteNoteByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id")) // Convert the ID parameter from string to integer
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ // Return 400 Bad Request if the ID is invalid
"error": "Invalid note ID",
})
return
}
if success := storage.DeleteNoteByID(id); !success {
c.JSON(http.StatusNotFound, gin.H{ // Return 404 Not Found if the note is not found
"error": "Note not found",
})
return
}
c.Status(http.StatusNoContent) // Return 204 No Content on successful deletion
}
This file serves as the main entry point of the application. It initializes the database and sets up routes for handling HTTP requests using the Gin web framework.
package main
import (
"log" // Package for logging
"github.com/gin-gonic/gin" // Gin web framework
"gin-notes-api/handlers" // Importing the module with request handlers
"gin-notes-api/storage" // Importing the module for database operations
)
func main() {
// Initialize the database
if err := storage.InitDatabase(); err != nil {
log.Fatalf("Failed to initialize database: %v", err) // Log the error and terminate the program if database initialization fails
}
// Create a new Gin router with default settings
router := gin.Default()
// Define routes and bind them to their respective handlers
router.GET("/notes", handlers.GetNotes) // Route for retrieving all notes
router.GET("/notes/:id", handlers.GetNoteByID) // Route for retrieving a note by ID
router.POST("/notes", handlers.CreateNote) // Route for creating a new note
router.PUT("/notes/:id", handlers.UpdateNoteByID) // Route for updating a note by ID
router.DELETE("/notes/:id", handlers.DeleteNoteByID) // Route for deleting a note by ID
// Start the web server on port 8080
router.Run(":8080")
}
Now we can run the application locally and test its functionality.
To start the application, use the following command:
go run main.go
This request creates a new note with a specified title and content.
curl -X POST http://localhost:8080/notes \
-H "Content-Type: application/json" \
-d '{"title":"Title","content":"Note body"}'
This request retrieves a list of all notes stored in the database.
curl -X GET http://localhost:8080/notes
This request fetches a specific note by its unique ID.
curl -X GET http://localhost:8080/notes/1
This request updates an existing note by its ID, providing a new title and content.
curl -X PUT http://localhost:8080/notes/1 \
-H "Content-Type: application/json" \
-d '{"title":"Updated Title","content":"Updated note body"}'
This request deletes a note with a specific ID.
curl -X DELETE http://localhost:8080/notes/1
To deploy the application using Hostman App Platform, first ensure your project is hosted in a Git repository. This example uses GitHub.
Initialize a Git repository locally in your project directory:
git init -b main
git add .
git commit -m 'First commit'
Push the repository to a remote server using the commands provided when creating a new GitHub repository:
git remote add origin [email protected]:your_user/your_repository.git
git push -u origin main
Go to the App Platform section in Hostman and click Create app.
Under the Type section, choose the Backend tab and select the Gin framework.
Connect your GitHub account by granting access to the repositories, or manually select the necessary repository.
After connecting your GitHub account, select the repository containing your application in the Repository section.
Choose a region where your application will be hosted.
In the Configuration section, select the minimum settings; they are sufficient for this project. You can modify them later if needed.
Leave the default values in the App settings section. For more complex projects, you may specify environment variables and custom build commands.
Specify a name for your application and click Start deploy.
The deployment process can take up to 10 minutes. Once it’s completed, you will see the message “Deployment successfully completed
” in the deployment logs.
Navigate to the Settings tab on the application page to view the domain assigned to your app.
In the same section, you can modify the server configuration, edit deployment settings, and update the domain binding. If you connect a custom domain, a Let’s Encrypt SSL certificate will be automatically issued and renewed 7 days before expiration.
To verify that the application is working correctly, execute a curl request, replacing localhost
with the assigned domain:
curl -X GET https://your_domain/notes
In this tutorial, we have developed a basic web application for managing notes using the Gin framework and GORM library. The created RESTful API supports basic CRUD operations, making the application simple and user-friendly.
Gin proved to be an efficient and easy-to-learn tool. Its routing system and support for concurrent requests made development smoother. GORM facilitated database interaction by automating many tasks.
The application was successfully deployed on the Hostman App Platform, providing a fast and reliable deployment process.
In the future, we can enhance the application by adding new features such as user authentication and advanced note search capabilities.
This project demonstrated how modern development tools like Gin and GORM simplify web application creation.