Introduction :
Hey there π, In this tutorial, we are going to create a Web API with CRUD operations using Go-Fiber.
What are we building?
We are going to build a To-Do list API, with CRUD operations.
Prerequisitesπ― :
To continue with the tutorial, firstly you need to have Golang, Fiber and PostgreSQL installed. If you've not gone through the previous tutorials on the Fiber Web Framework series you can see them here :)
Installations :
- Golang
- Go-Fiber: We'll see this ahead in the tutorial.
- PostgreSQL
- GORM: We'll understand it from scratch in this tutorial. π
Getting Started π:
Let's get started by creating the main project directory todo-list-api
by using the following command.
mkdir todo-list-api
cd todo-list-api
Now initialize a mod file. (If you publish a module, this must be a path from which your module can be downloaded by Go tools. That would be your code's repository.)
go mod init <repository-name>
In my case repository name is github.com/Siddheshk02/todo-list-api
.
To install the Fiber Framework run the following command :
go get -u github.com/gofiber/fiber/v2
To install the Gorm and to install the Gorm Postgres driver, run the following commands resp. :
go get -u gorm.io/gorm
go get -u gorm.io/driver/postgres
Initializing π»:
Let's set up our server by creating a new instance of Fiber. For this create a file main.go
and add the following code to it :
package main
import "github.com/gofiber/fiber/v2"
func main() {
app := fiber.New() // Creating a new instance of Fiber.
list := app.Group("/list")
list.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Welcome to the Todo-List-API Tutorial :)")
}) // "/" - Default route to return the given string.
After Running main.go ,
Routes :
Now Let's create a new folder/package routes
which will contain routes.go
, for all the functions, called by the API endpoints.
routes.go
:
package routes
import "github.com/gofiber/fiber/v2"
func GetTask(c *fiber.Ctx) error {
return c.SendString("A Single Task") // for getting a single task.
}
func GetAllTasks(c *fiber.Ctx) error {
return c.SendString("ALL Tasks") // for getting all the tasks.
}
func AddTask(c *fiber.Ctx) error {
return c.SendString("Added a Task") // for adding a new task.
}
func DeleteTask(c *fiber.Ctx) error {
return c.SendString("Deleted a Task") // for deleting a task.
}
func UpdateTask(c *fiber.Ctx) error {
return c.SendString("Updated a Task") // for updating a task.
}
Now let's update the main.go
according to the functions in routes.go
,
package main
import (
"github.com/Siddheshk02/todo-list-api/routes"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
list := app.Group("/list")
list.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Welcome to the Todo-List-API Tutorial :)")
}) // "/" - Default route to return the given string.
list.Get("/tasks", routes.GetAllTasks) //Get endpoint for fetching all the tasks.
list.Get("/task/:id", routes.GetTask) //Get endpoint for fetching a single task.
list.Post("/add_task", routes.AddTask) //Post endpoint for add a new task.
list.Delete("/delete_task/:id", routes.DeleteTask) //Delete endpoint for removing an existing task.
list.Patch("/update_task/:id", routes.UpdateTask) //Patch endpoint for updating an existing task.
app.Listen(":8000")
}
It is a convention when building API to prefix all the routes with /list
which will mark them as list-API. For this, we are using the app.Group()
function. You can define all the REST endpoints for a particular resource using the resource name after that.
Now, similar to the routes
package let's create a folder/package database
and create dbconn.go
file in it.
In the dbconn.go
, let's define the Task entity and add Database Credentials:
type Task struct {
gorm.Model
Name string `json:"name"`
Status string `json:"status"`
}
const (
host = "localhost"
port = 5432
user = "postgres"
password = "<your-password>"
dbname = "todo-list-api"
)
var dsn string = fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=disable TimeZone=Asia/Shanghai",
host, port, user, password, dbname)
func InitDB() error {
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return err
}
db.AutoMigrate(&Task{})
return nil
}
InitDB
, the function is defined which will try to establish a connection with the database. If the database table is not present, it will create a new database table with the name task
.
AutoMigrate
call helps in creating the table if it is not already present. Database migration is usually things that change the structure of the database over time and this helps in making sure that the database structure is properly migrated to the latest version.
The function InitDB
, is called in the main.go
. Update the main.go
with the following code.
func main() {
app := fiber.New()
dbErr := database.InitDB()
if dbErr != nil {
panic(dbErr)
}
list := app.Group("/list")
list.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Welcome to the Todo-List-API Tutorial :)")
}) // "/" - Default route to return the given string.
...
}
POST :
Now, let's create the function CreateTask
in the dbconn.go
file, for adding a new Task.
func CreateTask(name string, status string) (Task, error) {
var newTask = Task{Name: name, Status: status}
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return newTask, err
}
db.Create(&Task{Name: name, Status: status})
return newTask, nil
}
To call the function CreateTask
, create a AddTask
function in the routes.go
,
func AddTask(c *fiber.Ctx) error {
newTask := new(database.Task)
err := c.BodyParser(newTask)
if err != nil {
c.Status(400).JSON(&fiber.Map{
"data": nil,
"success": false,
"message": err,
})
return err
}
result, err := database.CreateTask(newTask.Name, newTask.Status)
if err != nil {
c.Status(400).JSON(&fiber.Map{
"data": nil,
"success": false,
"message": err,
})
return err
}
c.Status(200).JSON(&fiber.Map{
"data": result,
"success": true,
"message": "Task added!",
})
return nil
}
It is a good practice to return a predefined structure as a response to the API request along with the Status code and message.
We use the BodyParser
function to convert the POST request data into our model format.
I am using the Postman tool for sending requests, you can use any tool for sending JSON to the POST request.
GET :
Now, adding a function for fetching the Tasks from the record. For this, let's update the dbconn.go
file add a GetallTasks
function in it, which will get the list of the tasks.
func GetallTasks() ([]Task, error) {
var tasks []Task
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return tasks, err
}
db.Find(&tasks)
return tasks, nil
}
Now update the GetAllTasks
function in the routes.go
file,
func GetAllTasks(c *fiber.Ctx) error {
result, err := database.GetallTasks()
if err != nil {
return c.Status(500).JSON(&fiber.Map{
"data": nil,
"success": false,
"message": err,
})
}
return c.Status(200).JSON(&fiber.Map{
"data": result,
"success": true,
"message": "All Tasks",
})
}
In the GET request, we don't need to send any data. We just want the list of all the tasks which is added. If there is any error from the Database call, send the status code 500.
GET a Single Task :
Now, for getting a single task through the 'Id' let's add a new function Gettask
in the dbconn.go
file.
func Gettask(id string) (Task, error) {
var task Task
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return task, err
}
db.Where("ID = ?", id).First(&task)
return task, nil
}
db.Where()
is used to check for the Task with that particular 'id'.
Let's create a GetTask()
function in the routes.go
file.
func GetTask(c *fiber.Ctx) error {
id := c.Params("id")
if id == "" {
return c.Status(500).JSON(&fiber.Map{
"message": "id cannot be empty",
})
}
result, err := database.Gettask(id)
if err != nil {
return c.Status(500).JSON(&fiber.Map{
"data": nil,
"success": false,
"message": err,
})
}
return c.Status(200).JSON(&fiber.Map{
"data": result,
"success": true,
"message": "",
})
}
Here we are getting the 'id' with the Fiber function called param
, which accepts an argument which is the name of the parameter expecting. If the 'id' is not present, an error is sent to the user with the Fiber function.
This 'id' is passed when the GetTask()
function is called.
DELETE :
For deleting a task from the list let's add a Deletetask
function in the dbconn.go
file.
func Deletetask(id string) error {
var task Task
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return err
}
db.Where("ID = ?", id).Delete(&task)
return nil
}
This function is similar to the Gettask()
function only difference is, here we are using Delete(&task)
to delete the particular task.
Now, update the DeleteTask()
function in the routes.go
file with the following code,
func DeleteTask(c *fiber.Ctx) error {
id := c.Params("id")
if id == "" {
return c.Status(500).JSON(&fiber.Map{
"message": "id cannot be empty",
})
}
err := database.Deletetask(id)
if err != nil {
return c.Status(500).JSON(&fiber.Map{
"data": nil,
"success": false,
"message": err,
})
}
return c.Status(200).JSON(&fiber.Map{
"data": nil,
"success": true,
"message": "Task Deleted Successfully",
})
}
Similar to the function GetTask()
, we are taking the 'id' parameter of the task that needs to be Deleted. If the 'id' is not present, an error is sent to the user with the Fiber function.
UPDATE :
For this, create a new function Updatetask
in the dbconn.go
file and add the following code,
func Updatetask(name string, status string, id string) (Task, error) {
var newTask = Task{Name: name, Status: status}
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return newTask, err
}
db.Where("ID = ?", id).Updates(&Task{Name: newTask.Name, Status: newTask.Status})
return newTask, nil
}
Here, we have passed the name, status as well as the id of the Task that needs to Update. Updates()
function is used for updating multiple columns of the Table.
Now, let's update the UpdateTask()
function in the routes.go
file through which we are going to call and pass the parameters(name, status and id) to Updatetask()
function in the dbconn.go
file.
func UpdateTask(c *fiber.Ctx) error {
id := c.Params("id")
if id == "" {
return c.Status(500).JSON(&fiber.Map{
"message": "id cannot be empty",
})
}
newTask := new(database.Task)
err := c.BodyParser(newTask)
if err != nil {
c.Status(400).JSON(&fiber.Map{
"data": nil,
"success": false,
"message": err,
})
return err
}
result, err := database.Updatetask(newTask.Name, newTask.Status, id)
if err != nil {
c.Status(400).JSON(&fiber.Map{
"data": nil,
"success": false,
"message": err,
})
return err
}
c.Status(200).JSON(&fiber.Map{
"data": result,
"success": true,
"message": "Task Updated!",
})
return nil
}
Here, The name of the Task with 'id: 1' was Fiber Tutorial Series
now it is updated to Todo-list-API
, keeping the status
value same.
So, now you've successfully created the CRUD Operations - Create, Read, Update, Delete.
You can find the complete code repository for this tutorial here πGithub
Conclusion π:
I hope you must have understand how to create a REST API using Go-Fiber, PostgreSQL DB and GORM. Now, try building something with your ideas that will make you learn faster :)
To get more information about Golang concepts, projects, etc. and to stay updated on the Tutorials do follow Siddhesh on Twitter and GitHub.
Until then Keep Learning, Keep Building ππ
Top comments (0)