Restful API with Go (Gin)
In this blog, we are developing a restful api with you. Little bit of instruction about what is rest is crucial. Then, continuing with the developing API is our main task.
What is Rest API? (REpresentational State Transfer)
An API is an application programming interface. It is consists of a set of rules and used to allow programs to talk to each other. REST determines how the API looks like. When the developers create their API, they follow the set of constraints. This brings some specific URLs to get a piece of data. Each URL is called a request. The data sent back to you is called a response.
Our programming language is Go. Beside, Gin Web Framework helps us. I created a serie which is learning go from documentation. This serie includes 5 basic blog to understand basis of Go. If you currently new, It might be good for you. Let’s start.
Gin simplifies many coding tasks associated with building web applications, including web services. In this tutorial, you’ll use Gin to route requests, retrieve request details, and marshal JSON for responses.
We’ll build a task creator app API. Each of the task will be consist of id, title, person and information. Everybody can add a task and read all tasks. Directly, we can say that we are designing endpoints. 🥳
/task
- GET - get list of all task
- POST - Add a new task/task
- Get task by ID
We designed how it looks like. Let’s fun part begins. (In here, I used macOS and iterm2 application. If you are a windows user, you can continue with cmd. Linux is same as macOS.)
Open a terminal and create a folder where you want — I created on Desktop. Then, initialize the go. Open this folder in text editor.
// terminal
$ cd Desktop
$ mkdir task-app-web
$ cd task-app-web
$ go mod init example/task-app-web
$ touch main.go// main.go
package maintype task struct {
ID string `json:"id"` // we will use json so we need specify what a field’s name should be
Title string `json:"title"`
Person string `json:"artist"`
Information string `json:"information"`
}// Also we can create a simple data. In real life, the data come from database.
// In here, we will store data in localy.var tasks = []task{
{ID: "BS-1", Title: "Create an API", Person: "Kuzey", Information: "Create a simple task api that people create some tasks and reach all of the task."}, {ID: "BS-2", Title: "Write a blog", Person: "Kuzey", Information: "Write a blog that people who interested in Go can write create their API."}, {ID: "BS-3", Title: "Make a coffe for us", Person: "John", Information: "29.12.2021 we have a coffe talk session. Can we make a coffe for us?"}, {ID: "BS-4", Title: "Order some food", Person: "May", Information: "Order some food for lunch."},
}func main() {}
In here, we describe our objects how to look like. The task struct shows us parameters, and tasks is a slice that inholds task. Now, we need to use gin framework for using GET HTTP method. Firstly, import the packages, then write a function which name is getTask(). We will use getTask() function insede the router.GET() in main function. (Don’t panic, we will see each of them)
// main.go
package mainimport (
"net/http" "github.com/gin-gonic/gin"
)type task struct {
ID string `json:"id"` // we will use json so we need specify what a field’s name should be
Title string `json:"title"`
Person string `json:"artist"`
Information string `json:"information"`
}// Also we can create a simple data. In real life, the data come from database.
// In here, we will store data in localy.
var tasks = []task{
{ID: "BS-1", Title: "Create an API", Person: "Kuzey", Information: "Create a simple task api that people create some tasks and reach all of the task."},
{ID: "BS-2", Title: "Write a blog", Person: "Kuzey", Information: "Write a blog that people who interested in Go can write create their API."},
{ID: "BS-3", Title: "Make a coffe for us", Person: "John", Information: "29.12.2021 we have a coffe talk session. Can we make a coffe for us?"},
{ID: "BS-4", Title: "Order some food", Person: "May", Information: "Order some food for lunch."},
}// getTasks responds with the list of all albums as JSON.
func getTasks(c *gin.Context) {
c.IndentedJSON(http.StatusOK, tasks)
}func main() {}
In main function, we need to create a router with using gin. Inside gin, Default() function helps up to create a simple router (https://pkg.go.dev/github.com/gin-gonic/gin#Default). And, use the GET function to associate the GET HTTP method.
// main.go
package mainimport (
"net/http" "github.com/gin-gonic/gin"
)type task struct {
ID string `json:"id"` // we will use json so we need specify what a field’s name should be
Title string `json:"title"`
Person string `json:"artist"`
Information string `json:"information"`
}// Also we can create a simple data. In real life, the data come from database.
// In here, we will store data in localy.
var tasks = []task{
{ID: "BS-1", Title: "Create an API", Person: "Kuzey", Information: "Create a simple task api that people create some tasks and reach all of the task."},
{ID: "BS-2", Title: "Write a blog", Person: "Kuzey", Information: "Write a blog that people who interested in Go can write create their API."},
{ID: "BS-3", Title: "Make a coffe for us", Person: "John", Information: "29.12.2021 we have a coffe talk session. Can we make a coffe for us?"},
{ID: "BS-4", Title: "Order some food", Person: "May", Information: "Order some food for lunch."},
}// getTasks responds with the list of all albums as JSON.
func getTasks(c *gin.Context) {
c.IndentedJSON(http.StatusOK, tasks)
}func main() {
router := gin.Default()
router.GET("/albums", getTasks) router.Run("localhost:8080")
}
Run attaches the router to a http.Server and starts listening and serving HTTP requests. It is a shortcut for http.ListenAndServe(addr, router) Note: this method will block the calling goroutine indefinitely unless an error happens. (https://pkg.go.dev/github.com/gin-gonic/gin#Engine.Run)
// terminal
$ go get .
go get: added github.com/gin-gonic/gin
$ go run .
$ curl <http://localhost:8080/tasks>
So, we can reach the tasks data with GET request. Now, adding a new task in tasks our second target. It can be called by handler to add a new item. postTask is new function to append sent data to tasks.
// main.go
package mainimport (
"net/http" "github.com/gin-gonic/gin"
)type task struct {
ID string `json:"id"` // we will use json so we need specify what a field’s name should be
Title string `json:"title"`
Person string `json:"artist"`
Information string `json:"information"`
}// Also we can create a simple data. In real life, the data come from database.
// In here, we will store data in localy.
var tasks = []task{
{ID: "BS-1", Title: "Create an API", Person: "Kuzey", Information: "Create a simple task api that people create some tasks and reach all of the task."},
{ID: "BS-2", Title: "Write a blog", Person: "Kuzey", Information: "Write a blog that people who interested in Go can write create their API."},
{ID: "BS-3", Title: "Make a coffe for us", Person: "John", Information: "29.12.2021 we have a coffe talk session. Can we make a coffe for us?"},
{ID: "BS-4", Title: "Order some food", Person: "May", Information: "Order some food for lunch."},
}// getTasks responds with the list of all albums as JSON.
func getTasks(c *gin.Context) {
c.IndentedJSON(http.StatusOK, tasks)
}// postTask
func postTask(c *gin.Context) {
var newTask task // Call BindJSON to bind the received JSON to newTask.
if err := c.BindJSON(&newTask); err != nil {
return
} // Add the new album to the slice.
tasks = append(tasks, newTask)
c.IndentedJSON(http.StatusCreated, newTask)
}func main() {
router := gin.Default()
router.GET("/tasks", getTasks)
router.POST("/tasks", postTasks) router.Run("localhost:8080")
}
Context.BindJSON bind he request body to newTask. Also, 201 status code add to the response.
// terminal
$ curl <http://localhost:8080/tasks> \\
--include \\
--header "Content-Type: application/json" \\
--request "POST" \\
--data '{"id": "BS-5","title": "POST request","person": "kuzey","information": "create a tasks in post request" }'
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Date: Thu, 30 Dec 2021 19:48:34 GMT
Content-Length: 72$ curl <http://localhost:8080/tasks>
[
{
"id": "BS-1",
"title": "Create an API",
"person": "Kuzey",
"information": "Create a simple task api that people create some tasks and reach all of the task."
},
{
"id": "BS-2",
"title": "Write a blog",
"person": "Kuzey",
"information": "Write a blog that people who interested in Go can write create their API."
},
{
"id": "BS-3",
"title": "Make a coffe for us",
"person": "John",
"information": "29.12.2021 we have a coffe talk session. Can we make a coffe for us?"
},
{
"id": "BS-4",
"title": "Order some food",
"person": "May",
"information": "Order some food for lunch."
},
{
"id": "BS-5",
"title": "POST request",
"person": "kuzey",
"information": "create a tasks in post request"
}
]%
Sometime, we want to reach specific item. In this function, we will create a GET request with specific id. Let’s say function name is getTaskByID.
// main.go
package mainimport (
"net/http" "github.com/gin-gonic/gin"
)type task struct {
ID string `json:"id"` // we will use json so we need specify what a field’s name should be
Title string `json:"title"`
Person string `json:"artist"`
Information string `json:"information"`
}// Also we can create a simple data. In real life, the data come // from database.
// In here, we will store data in localy.
var tasks = []task{
{ID: "BS-1", Title: "Create an API", Person: "Kuzey", Information: "Create a simple task api that people create some tasks and reach all of the task."},
{ID: "BS-2", Title: "Write a blog", Person: "Kuzey", Information: "Write a blog that people who interested in Go can write create their API."},
{ID: "BS-3", Title: "Make a coffe for us", Person: "John", Information: "29.12.2021 we have a coffe talk session. Can we make a coffe for us?"},
{ID: "BS-4", Title: "Order some food", Person: "May", Information: "Order some food for lunch."},
}// getTasks responds with the list of all albums as JSON.
func getTasks(c *gin.Context) {
c.IndentedJSON(http.StatusOK, tasks)
}// postTask
func postTask(c *gin.Context) {
var newTask task // Call BindJSON to bind the received JSON to newTask.
if err := c.BindJSON(&newTask); err != nil {
return
} // Add the new album to the slice.
tasks = append(tasks, newTask)
c.IndentedJSON(http.StatusCreated, newTask)
}// get task by Id
func getTaskByID(c *gin.Context) {
id := c.Param("id")
println(id)
// Loop over the list of tasks, looking for
// an album whose ID value matches the parameter.
for _, a := range tasks {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "task not found"})
}func main() {
router := gin.Default()
router.GET("/tasks", getTasks)
router.POST("/tasks", postTask)
router.GET("/tasks/:id", getTaskByID) router.Run("localhost:8080")
}
Context.Param retrieved the id path parameter from the URL. When you map this handler to a path, you’ll include a placeholder for the parameter in the path. http.StatisNotFound returns the HTTP 404.
// terminal
$ go run .
$ curl <http://localhost:8080/tasks/BS-2>
{
"id": "BS-2",
"title": "Write a blog",
"artist": "Kuzey",
"information": "Write a blog that people who interested in Go can write create their API."
}%$ curl <http://localhost:8080/tasks/BS-6>
{
"message": "task not found"
}%
This blog post is personal notes 🙂 If you are more interested in it, you need to check https://go.dev/doc/tutorial/web-service-gin site.