An Interest In:
Web News this Week
- April 17, 2024
- April 16, 2024
- April 15, 2024
- April 14, 2024
- April 13, 2024
- April 12, 2024
- April 11, 2024
Golang for Web: Build your first REST API with Golang & Fiber
This is the first part of this series on Golang for Web. My goal is to show the newcomers how easily we can build REST APIs with Go by breaking down big apps into small ones. Here, we're going to build a simple TODO app webserver with Gofiber by separating all routes, controllers, etc.
Go: An open-source programming language
Go is an open-source programming language that makes it easy to build simple, reliable, and efficient software.
Go is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. Go is syntactically similar to C, but with memory safety, garbage collection, structural typing, and CSP-style concurrency. (Source: Wikipedia)
Golang for Web
As a MERN stack developer, I found Fiber Web Framework is very similar to express as they are claiming and this is pretty easy for a JS developer to get started with Golang to build a nice and amazing REST API.
Fiber is an Express inspired web framework built on top of Fasthttp, the fastest HTTP engine for Go. Designed to ease things up for fast development with zero memory allocation and performance in mind.
Check their amazing benchmark performance here - https://docs.gofiber.io/extra/benchmarks
What will we build?
Okay enough talk It's time to show some action with Fiber. Today we are going to build a basic TODO app with Fiber. I'm pretty sure after this, you can easily start building REST APIs with Golang
Prerequisites
- A basic knowledge of Golang syntax.
- Go version 1.14 or above installed on your machine. Install from here
- Postman or any other related app installed on your machine. Download from here
If you have these, Let's get started
Lets Begin
1. Setup our Project
First, lets create a new directory named fiber-todo
and open this directory in VS Code (or any other Code Editor/ IDE)
Now at the root directory open your terminal and run the command :
go mod init github.com/<Your_Username>/<Repo_Name>
Example :
go mod init github.com/devsmranjan/golang-fiber-basic-todo-app
Let's install fiber in our project by running :
go get -u github.com/gofiber/fiber/v2
Now inside our root directory create a file named main.go
and create two directories routes
& controllers
.
2. Create our first server
Now inside main.go
write the following code :
package mainimport ( "github.com/gofiber/fiber/v2")func main() {}
Okay, you will get some errors here but stay tuned with me
Now inside the main()
method let's initiate fiber.
app := fiber.New()
Add our first route by :
app.Get("/", func(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "message": "You are at the endpoint ", })})
and finally, listen to the server at 8000 port and catch the error if any.
err := app.Listen(":8000")if err != nil { panic(err)}
Here is our final code :
package mainimport ( "github.com/gofiber/fiber/v2")func main() { app := fiber.New() // give response when at / app.Get("/", func(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "message": "You are at the endpoint ", }) }) // Listen on server 8000 and catch error if any err := app.Listen(":8000") // handle error if err != nil { panic(err) }}
Now to run our server, open terminal and in our root directory run :
go run main.go
It will show something like this
Fiber v2.2.0 http://127.0.0.1:8000 Handlers ............. 2 Threads ............. 4 Prefork ....... Disabled PID ............. 60983
Open your browser now, and goto localhost:8000
. You will get the output like this
{ "message": "You are at the endpoint ", "success": true}
Hureeey!!! We did it
Additionally, if you want to add logger middleware, then run
go get -u github.com/gofiber/fiber/v2/middleware/logger
Import logger in our project
import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" // new)
and finally, connect this middleware with our app
app.Use(logger.New())
Here is our final code :
package mainimport ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" // new)func main() { app := fiber.New() app.Use(logger.New()) // new // give response when at / app.Get("/", func(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "message": "You are at the endpoint ", }) }) // Listen on server 8000 and catch error if any err := app.Listen(":8000") // handle error if err != nil { panic(err) }}
Now after running our server again, goto localhost:8000
and you can see the terminal is showing the log of our request like below :
21:44:48 | 200 | 0s | 127.0.0.1 | GET | /
3. Decide Endpoints for our TODO app
localhost:8000/api/todos
request: GETdescription: To get all todos
localhost:8000/api/todos/:id
request: GETdescription: Get todo by id
localhost:8000/api/todos
request: POSTinput: { title : String}description: Create new todo
localhost:8000/api/todos/:id
request: PUTinput: { title : String, completed : Boolean}description: Update todo
localhost:8000/api/todos/:id
request: DELETEdescription: Delete todo
4. Build our first API endpoint
Step 1 :
Let's write our first controller for our todo app.
Open controllers
directory and create a file named todo.go
Now inside controllers/todo.go
let's do our required imports.
package controllersimport ( "github.com/gofiber/fiber/v2")
Add Todo
structure
type Todo struct { Id int `json:"id"` Title string `json:"title"` Completed bool `json:"completed"`}
Let's add some predefined todos
var todos = []*Todo{ { Id: 1, Title: "Walk the dog ", Completed: false, }, { Id: 2, Title: "Walk the cat ", Completed: false, },}
and now finally let's create our controller to get all the todos
func GetTodos(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "data": map[string][]*Todo{ "todos": todos, }, })}
Here is our final code in controllers/todo.go
:
package controllersimport ( "github.com/gofiber/fiber/v2")type Todo struct { Id int `json:"id"` Title string `json:"title"` Completed bool `json:"completed"`}var todos = []*Todo{ { Id: 1, Title: "Walk the dog ", Completed: false, }, { Id: 2, Title: "Walk the cat ", Completed: false, },}// get all todosfunc GetTodos(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "data": map[string][]*Todo{ "todos": todos, }, })}
Step 2 :
Now let's write our first route for our todo app and connect our controllers.
Open the routes
directory and create a file named todo.go
Now inside routes/todo.go
let's do our required imports
package routesimport ( "github.com/gofiber/fiber/v2" "github.com/devsmranjan/golang-fiber-basic-todo-app/controllers" // replace)
Replace github.com/devsmranjan/golang-fiber-basic-todo-app/controllers
with your github repo url like github.com/<Your_Username>/<Repo_Name>/controllers
Create our first route to get all todos
func TodoRoute(route fiber.Router) { route.Get("", controllers.GetTodos)}
Here is our final code in routes/todo.go
:
package routesimport ( "github.com/gofiber/fiber/v2" "github.com/devsmranjan/golang-fiber-basic-todo-app/controllers" // replace)func TodoRoute(route fiber.Router) { route.Get("", controllers.GetTodos)}
Step 3 :
Okay here is the final step.
Go back to main.go
and import routes
import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/devsmranjan/golang-fiber-basic-todo-app/routes" // new // replace)
Replace github.com/devsmranjan/golang-fiber-basic-todo-app/routes
with your github repo url i.e github.com/<Your_Username>/<Repo_Name>/routes
Now let's create a separate function to handle all our routes.
func setupRoutes(app *fiber.App) {}
Move all routes from main()
method to setupRoutes()
method.
func setupRoutes(app *fiber.App) { // moved from main method app.Get("/", func(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "message": "You are at the endpoint ", }) })}
Now call setupRoutes()
method from main()
method.
setupRoutes(app)
Here is our final main()
looks like.
func main() { app := fiber.New() app.Use(logger.New()) // setup routes setupRoutes(app) // new // Listen on server 8000 and catch error if any err := app.Listen(":8000") // handle error if err != nil { panic(err) }}
Now let's make a routes group named api
inside our setupRoutes()
method
api := app.Group("/api")
Add response for /api
route
api.Get("", func(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "message": "You are at the api endpoint ", })})
Now finally, connect all the todo routes to our api
route group
routes.TodoRoute(api.Group("/todos"))
Final setupRoutes()
method looks like :
func setupRoutes(app *fiber.App) { // give response when at / app.Get("/", func(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "message": "You are at the endpoint ", }) }) // api group api := app.Group("/api") // give response when at /api api.Get("", func(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "message": "You are at the api endpoint ", }) }) // connect todo routes routes.TodoRoute(api.Group("/todos"))}
Now let's run our server and go to http://localhost:8000/api/todos
. The output will be like below.
{ "data": { "todos": [ { "id": 1, "title": "Walk the dog ", "completed": false }, { "id": 2, "title": "Walk the cat ", "completed": false } ] }, "success": true}
5. Let's create other controllers
I guess you got the idea, how all are working. Right???
Now let's create other controllers.
But before that let's add some required imports in controllers/todo.go
:
import ( "fmt" // new "strconv" // new "github.com/gofiber/fiber/v2")
Okay. Let's create a controller to create a todo
func CreateTodo(c *fiber.Ctx) error { type Request struct { Title string `json:"title"` } var body Request err := c.BodyParser(&body) // if error if err != nil { fmt.Println(err) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse JSON", }) } // create a todo variable todo := &Todo{ Id: len(todos) + 1, Title: body.Title, Completed: false, } // append in todos todos = append(todos, todo) return c.Status(fiber.StatusCreated).JSON(fiber.Map{ "success": true, "data": map[string]*Todo{ "todo": todo, }, })}
A controller to get a single todo by id
func GetTodo(c *fiber.Ctx) error { // get parameter value paramId := c.Params("id") // convert parameter value string to int id, err := strconv.Atoi(paramId) // if error in parsing string to int if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse Id", }) } // find todo and return for _, todo := range todos { if todo.Id == id { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "data": map[string]*Todo{ "todo": todo, }, }) } } // if todo not available return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ "success": false, "message": "Todo not found", })}
A controller to update a todo
func UpdateTodo(c *fiber.Ctx) error { // find parameter paramId := c.Params("id") // convert parameter string to int id, err := strconv.Atoi(paramId) // if parameter cannot parse if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse id", }) } // request structure type Request struct { Title *string `json:"title"` Completed *bool `json:"completed"` } var body Request err = c.BodyParser(&body) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse JSON", }) } var todo *Todo for _, t := range todos { if t.Id == id { todo = t break } } if todo.Id == 0 { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ "success": false, "message": "Not found", }) } if body.Title != nil { todo.Title = *body.Title } if body.Completed != nil { todo.Completed = *body.Completed } return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "data": map[string]*Todo{ "todo": todo, }, })}
A controller to delete a todo
func DeleteTodo(c *fiber.Ctx) error { // get param paramId := c.Params("id") // convert param string to int id, err := strconv.Atoi(paramId) // if parameter cannot parse if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse id", }) } // find and delete todo for i, todo := range todos { if todo.Id == id { todos = append(todos[:i], todos[i+1:]...) return c.Status(fiber.StatusNoContent).JSON(fiber.Map{ "success": true, "message": "Deleted Succesfully", }) } } // if todo not found return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ "success": false, "message": "Todo not found", })}
Here is our final code in controllers/todo.go
:
package controllersimport ( "fmt" "strconv" "github.com/gofiber/fiber/v2")type Todo struct { Id int `json:"id"` Title string `json:"title"` Completed bool `json:"completed"`}var todos = []*Todo{ { Id: 1, Title: "Walk the dog ", Completed: false, }, { Id: 2, Title: "Walk the cat ", Completed: false, },}// get all todosfunc GetTodos(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "data": map[string][]*Todo{ "todos": todos, }, })}// Create a todofunc CreateTodo(c *fiber.Ctx) error { type Request struct { Title string `json:"title"` } var body Request err := c.BodyParser(&body) // if error if err != nil { fmt.Println(err) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse JSON", }) } // create a todo variable todo := &Todo{ Id: len(todos) + 1, Title: body.Title, Completed: false, } // append in todos todos = append(todos, todo) return c.Status(fiber.StatusCreated).JSON(fiber.Map{ "success": true, "data": map[string]*Todo{ "todo": todo, }, })}// get a single todo// PARAM: idfunc GetTodo(c *fiber.Ctx) error { // get parameter value paramId := c.Params("id") // convert parameter value string to int id, err := strconv.Atoi(paramId) // if error in parsing string to int if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse Id", }) } // find todo and return for _, todo := range todos { if todo.Id == id { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "data": map[string]*Todo{ "todo": todo, }, }) } } // if todo not available return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ "success": false, "message": "Todo not found", })}// Update a todo// PARAM: idfunc UpdateTodo(c *fiber.Ctx) error { // find parameter paramId := c.Params("id") // convert parameter string to int id, err := strconv.Atoi(paramId) // if parameter cannot parse if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse id", }) } // request structure type Request struct { Title *string `json:"title"` Completed *bool `json:"completed"` } var body Request err = c.BodyParser(&body) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse JSON", }) } var todo *Todo for _, t := range todos { if t.Id == id { todo = t break } } if todo.Id == 0 { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ "success": false, "message": "Not found", }) } if body.Title != nil { todo.Title = *body.Title } if body.Completed != nil { todo.Completed = *body.Completed } return c.Status(fiber.StatusOK).JSON(fiber.Map{ "success": true, "data": map[string]*Todo{ "todo": todo, }, })}// Delete a todo// PARAM: idfunc DeleteTodo(c *fiber.Ctx) error { // get param paramId := c.Params("id") // convert param string to int id, err := strconv.Atoi(paramId) // if parameter cannot parse if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "Cannot parse id", }) } // find and delete todo for i, todo := range todos { if todo.Id == id { todos = append(todos[:i], todos[i+1:]...) return c.Status(fiber.StatusNoContent).JSON(fiber.Map{ "success": true, "message": "Deleted Succesfully", }) } } // if todo not found return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ "success": false, "message": "Todo not found", })}
6. Create other routes
Let's create other routes for our todo app.
Now, inside TodoRoute()
add all below routes
route.Post("", controllers.CreateTodo)route.Put("/:id", controllers.UpdateTodo)route.Delete("/:id", controllers.DeleteTodo)route.Get("/:id", controllers.GetTodo)
Finally, our final code looks like :
package routesimport ( "github.com/gofiber/fiber/v2" "github.com/devsmranjan/golang-fiber-basic-todo-app/controllers" // replace)func TodoRoute(route fiber.Router) { route.Get("", controllers.GetTodos) route.Post("", controllers.CreateTodo) // new route.Put("/:id", controllers.UpdateTodo) // new route.Delete("/:id", controllers.DeleteTodo) // new route.Get("/:id", controllers.GetTodo) // new}
Now we are all set to run our server
And let's test our API in Postman.
7. Test our endpoints
To get all todos, give a GET
request to localhost:8000/api/todos
To get todo by id, give a GET
request to localhost:8000/api/todos/:id
Here replace :id
with a todo id
To create a new todo, give a POST
request to localhost:8000/api/todos
with a title: <String>
in the request body.
Okay. Let's check if our todo is successfully created or not.
Give a GET
request to localhost:8000/api/todos
to get all todos again.
Yeeeeeeeeah !!!
Now, let's update a todo by giving a PUT
request to localhost:8000/api/todos/:id
with a title: <String>
or completed: <Boolean>
or both in the request body.
Here replace :id
with a todo id
To delete a todo, give a DELETE
request to localhost:8000/api/todos/:id
Here replace :id
with a todo id
Congooooooo We did it
Conclusion
For more information, I suggest taking a deeper look at the documentation here https://docs.gofiber.io/
Here is my GitHub link for this project - https://github.com/devsmranjan/golang-fiber-basic-todo-app
Thank you for reading my article . I hope you have learned something here.
Happy coding and stay tuned for my next post in this series!
Thanks! Don't forget to give a and follow :)
Original Link: https://dev.to/devsmranjan/golang-build-your-first-rest-api-with-fiber-24eh
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To