
How to Create RESTful API with Golang?
In this tutorial, I’ve explained: “How to create REST API with Go?” Let’s assume that you want to create a school API. And our resources:
- classes
- students
- teachers
RESTful API Object/Resource Details
As I mentioned, we’ve 3 different resource types. These resources are different and they have different features. So, firstly I should create object models.
Class | Student | Teacher |
---|---|---|
-id -name -max_size -student_count |
-id -name -class -teachers |
-id -name -num_of_students |

Endpoint | Endpoint URI | Method | What does endpoint do? | Returns |
---|---|---|---|---|
CreateStudent | /students | POST | Creates a new student resource(obj) | – 201 CREATED {message, student_id} – 404 NOT FOUND {message, code} |
ListStudents | /students | GET | List all of the student objets | – 200 OK {[]student} – 404 NOT FOUND {message, code} |
GetStudent | /students/id/{id} | GET | Get the specified student | – 200 OK {student} – 404 NOT FOUND {message, code} |
ListClasses | /classes | GET | List all of the class objs | – 200 OK {[]class} – 404 NOT FOUND {message, code} |
CreateClass | /classes | POST | Creates a new class obj | – 201 CREATED {message, class_id} – 404 NOT FOUND {message, code} |
GetClass | /classes/id/{id} | GET | Get the specified class | – 200 OK {class} – 404 NOT FOUND {message, code} |
CreateTeacher | /teachers | POST | Creates a new teacher obj | – 201 CREATED {message, teacher_id} – 404 NOT FOUND {message, code} |
ListTeachers | /teachers | GET | List all of the teachers | – 200 OK {[]class} – 404 NOT FOUND {message, code} |
GetTeacher | /teachers/id/{id} | GET | Get the specified teacher | – 200 OK {teacher} – 404 NOT FOUND {message, code} |
/students?teachers=1,2,3
Code the API
After API design stage, let’s code. Firstly, I’ve created main.go file and then, I should create structs for our resources.
package main
type Student struct {
ID string `json:"id"`
Name string `json:"name"`
Class string `json:"class"`
Teachers string `json:"teachers"`
}
type Teacher struct {
ID string `json:"id"`
Name string `json:"name"`
Num_of_Students string `json:"num_of_students"`
}
type Class struct {
ID string `json:"id"`
Name string `json:"name"`
Max_size int `json:"max_size"`
Student_count int `json:"student_count"`
}
Then, let’s implement our main function to serve API as a service. To serve a localhost server I’ve used Golang’s Gin library. I’ve also implemented some of the endpoints.
func main() {
router := gin.Default() // Router is our server.
router.GET("/students", listStudents) // List all student objects
router.POST("/students", createStudent) // -> Create new student
router.Run("localhost:9090")
}
As you can see in the above code, I’ve used “listStudents” and “createStudents” functions to handle GET and POST requests that cames “/students” endpoint. So, I’ve created this functions:
func createStudent(context *gin.Context) {
var student Student
err := context.BindJSON(&student)
// Create student obj with given json value (if there is no err)
if err == nil && student.ID != "" && student.Name != "" && student.Class != "" && student.Teachers != "" {
students = append(students, student)
// Create custom response if created
context.IndentedJSON(http.StatusCreated, gin.H{"message": "Obj created", "student_id": student.ID})
return
} else {
// Create custom response if not created
context.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Object cannot created!"})
return
}
}
func listStudents(context *gin.Context) {
context.IndentedJSON(http.StatusOK, students)
}
I’ve created a simple data to see on Postman.
var students = []Student{
{ID: "1", Name: "John Doe", Class: "1-b", Teachers: "1,2,4"},
}
All of the source code until this stage:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
var students = []Student{
{ID: "1", Name: "John Doe", Class: "1-b", Teachers: "1,2,4"},
}
type Student struct {
ID string `json:"id"`
Name string `json:"name"`
Class string `json:"class"`
Teachers string `json:"teachers"`
}
type Teacher struct {
ID string `json:"id"`
Name string `json:"name"`
Num_of_Students string `json:"num_of_students"`
}
type Class struct {
ID string `json:"id"`
Name string `json:"name"`
Max_size int `json:"max_size"`
Student_count int `json:"student_count"`
}
func createStudent(context *gin.Context) {
var student Student
err := context.BindJSON(&student)
// Create student obj with given json value (if there is no err)
if err == nil && student.ID != "" && student.Name != "" && student.Class != "" && student.Teachers != "" {
students = append(students, student)
// Create custom response if created
context.IndentedJSON(http.StatusCreated, gin.H{"message": "Obj created", "student_id": student.ID})
return
} else {
// Create custom response if not created
context.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Object cannot created!"})
return
}
}
func listStudents(context *gin.Context) {
context.IndentedJSON(http.StatusOK, students)
}
func main() {
router := gin.Default() // Router is our server.
router.GET("/students", listStudents) // List all student objects
router.POST("/students", createStudent) // -> Create new student
router.Run("localhost:9090")
}
With this code, when you run your program with go run main.go
you can use /students endpoints with POST and GET methods. As you can see in the following screenshot I am able to list students with the GET method.
I am able to add new student with the POST method and fulfilled JSON values. (id, name, class, and teachers must have value. Cannot be empty. I’ve checked them with an if block in createStudent function. E.g: student.ID != ""
)

To get a certain API object we can use URI parameters. For example, if you want to get an object that has ID=1 you can use “GET /students/1”.
Let’s code this mechanism… In the following code snippet I’ve implemented getStudent and getStudentByID functions.
func getStudentByID(id string) (*Student, error) {
for i, s := range students {
if s.ID == id {
return &students[i], nil
}
}
return nil, errors.New("Student not found!")
}
func getStudent(context *gin.Context) {
id := context.Param("id")
fmt.Println("idddd: " + id)
student, err := getStudentByID(id)
if err != nil {
context.IndentedJSON(http.StatusNotFound, gin.H{"message": "Student not found!"})
return
}
context.IndentedJSON(http.StatusOK, student)
}
- getStudent(): This function parses the URI context and gets its ID param. Then sends the value of this param to getStudentByID function.
- getStudentByID(): This function iterates all of the elements in students array and looks for the student object that is given by ID. It searchs student object that matches ID came from getStudent function.
I’ve also added a new line to controle /students/<ID> endpoint requests.
router.GET("/students/:id", getStudent) // Get the specified student with ID
Final Source Code (Until here):
package main
import (
"errors"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
var students = []Student{
{ID: "1", Name: "John Doe", Class: "1-b", Teachers: "1,2,4"},
}
type Student struct {
ID string `json:"id"`
Name string `json:"name"`
Class string `json:"class"`
Teachers string `json:"teachers"`
}
type Teacher struct {
ID string `json:"id"`
Name string `json:"name"`
Num_of_Students string `json:"num_of_students"`
}
type Class struct {
ID string `json:"id"`
Name string `json:"name"`
Max_size int `json:"max_size"`
Student_count int `json:"student_count"`
}
func createStudent(context *gin.Context) {
var student Student
err := context.BindJSON(&student)
// Create student obj with given json value (if there is no err)
if err == nil && student.ID != "" && student.Name != "" && student.Class != "" && student.Teachers != "" {
students = append(students, student)
// Create custom response if created
context.IndentedJSON(http.StatusCreated, gin.H{"message": "Obj created", "student_id": student.ID})
return
} else {
// Create custom response if not created
context.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Object cannot created!"})
return
}
}
func listStudents(context *gin.Context) {
context.IndentedJSON(http.StatusOK, students)
}
func getStudentByID(id string) (*Student, error) {
for i, s := range students {
if s.ID == id {
return &students[i], nil
}
}
return nil, errors.New("Student not found!")
}
func getStudent(context *gin.Context) {
id := context.Param("id")
fmt.Println("idddd: " + id)
student, err := getStudentByID(id)
if err != nil {
context.IndentedJSON(http.StatusNotFound, gin.H{"message": "Student not found!"})
return
}
context.IndentedJSON(http.StatusOK, student)
}
func main() {
router := gin.Default() // Router is our server.
router.GET("/students", listStudents) // List all student objects
router.POST("/students", createStudent) // Create new student
router.GET("/students/:id", getStudent) // Get the specified student with ID
router.Run("localhost:9090")
}
Finally, you can find all of the source code in https://github.com/rbozburun/GoRestAPIExercises/blob/main/main-SimpleSchoolAPI.go file.
This API is not well optimized, I will optimize and update this post with a new post 🙂
In this article, we’ve developed REST API with Go. Golang. has perfect libraries to create RESTful APIs and other back-end services…