Create an API server (REST)


There are many ways to build an API server but I will start with REST this time. To build the server faster, I will use Gin to implement the REST API.

First, create a Go module

mkdir rest-api
cd rest-api
go mod init gorest
touch main.go

I'm going to create the server for making a TODO program

type TODO struct {
	Title string
	Done  bool
}

type TODOS []*TODO

func (todos *TODOS) AddItem(title string) {
	for _, todo := range *todos {
		if todo.Title == title {
			return
		}
	}
	*todos = append(*todos, &TODO{Title: title})
}

func (todos TODOS) MarkDone(title string) {
	for _, todo := range todos {
		if todo.Title == title {
			todo.Done = true
			return
		}
	}
}

func (todos *TODOS) RemoveItem(title string) {
	pos := -1
	for i, todo := range *todos {
		if todo.Title == title {
			pos = i
			break
		}
	}
	if pos == -1 {
		return
	}
	*todos = append((*todos)[:pos], (*todos)[pos+1:]...)
}

Next, create endpoints for the server

func main() {
	r := gin.Default()
	r.GET("todos", func(ctx *gin.Context) {})
	r.POST("todo", func(ctx *gin.Context) {})
	r.PATCH("todo", func(ctx *gin.Context) {})
	r.DELETE("todo", func(ctx *gin.Context) {})
}

Then implement each endpoint.

Get all TODOs

r.GET("todos", func(ctx *gin.Context) {
  ctx.JSON(http.StatusOK, todos.GetTODOList())
})

Add a new TODO

r.POST("todo", func(ctx *gin.Context) {
  var todo TODO
  if err := ctx.ShouldBindJSON(&todo); err != nil {
    log.Println(err)
    ctx.String(http.StatusUnprocessableEntity, "Wrong format")
    return
  }
  todos.AddItem(todo.Title)
  ctx.String(http.StatusCreated, "The new TODO has been added")
})

Mark a TODO as done

r.PATCH("todo", func(ctx *gin.Context) {
  var todo TODO
  if err := ctx.ShouldBindJSON(&todo); err != nil {
    ctx.String(http.StatusUnprocessableEntity, "Wrong format")
    return
  }
  todos.MarkDone(todo.Title)
  ctx.String(http.StatusOK, "Your TODO has been marked as done")
})

Delete a TODO

r.DELETE("todo", func(ctx *gin.Context) {
  var todo TODO
  if err := ctx.ShouldBindJSON(&todo); err != nil {
    ctx.String(http.StatusUnprocessableEntity, "Wrong format")
    return
  }
  todos.RemoveItem(todo.Title)
  ctx.String(http.StatusOK, "Your TODO is deleted successfully")
})

Now run the server by this (the default port is 8080)

func main() {
  r := gin.Default()
  .
  .
  .
  r.Run()
}

Test the server with REST Client

@API = http://localhost:8080

###

POST {{API}}/todo
Content-Type: application/json

{
  "Title": "Wake up at 6 a.m."
}

###

POST {{API}}/todo
Content-Type: application/json

{
  "Title": "Eat breakfast"
}

###

PATCH {{API}}/todo
Content-Type: application/json

{
  "Title": "Wake up at 6 a.m."
}

###

GET {{API}}/todos

###

DELETE {{API}}/todo
Content-Type: application/json

{
  "Title": "Eat breakfast"
}

There is more to do about the server like CORS to make it usable with other applications. Please follow this link. Thank you for reading!

sources