Blogging is a great way to share your thoughts, ideas, and experiences with the world. In this article, I’ll show you how to build a simple blog app using Flutter for the front-end and Golang Fiber API for the back-end with SQLite database.
Prerequisites:
A Short Introduction to Golang and Fiber
Go (Golang) is a statically-typed, concurrent programming language developed by Google. It was released in 2009 and has since gained popularity due to its simplicity, efficiency, and scalability.
Fiber is a fast, modern, and low-overhead web framework for the Go programming language. It was created to provide a high-performance and expressive alternative to existing Go web frameworks. Fiber provides features such as fast HTTP request handling, middleware support, and integrated support for template engines and ORMs.
One of the key benefits of using Fiber is its focus on performance. It uses zero-allocation routers, and its lightweight design means that it has low memory overhead and fast response times. Fiber also provides support for HTTP/2, which can result in faster page load times and improved user experience for web applications.
Another advantage of Fiber is its flexible architecture, which allows developers to easily add custom middleware to handle specific requirements for their applications. This middleware can be used for tasks such as logging, authentication, or custom header manipulation.
Finally, Fiber provides robust support for popular web development technologies, such as template engines and object-relational mapping (ORM) libraries, allowing developers to build sophisticated web applications with minimal effort.
In conclusion, Fiber is a powerful and efficient web framework for the Go programming language. Its focus on performance and flexible architecture makes it a great choice for building fast, reliable, and scalable web applications.
Setting up the Back-end API with Golang Fiber
- First, install Golang if you haven’t already.
- Next, create directory for the api, init module and install the Golang latest Fiber package.
- Create a new Golang project and import the necessary packages.
You can do all that with a single command line, just update it with your username on github. If you want, you can use another module path, just keep in mind that typically, a module path consists of a repository root path, a directory within the repository.
mkdir blog-api && cd blog-api && go mod init "github.com/yourusername" && go get -u github.com/gofiber/fiber/v2
Next, create main.go file and define article struct inside it. Later we will work on the structure, but for now it is enough that everything is in one file.
package main
// Article struct
type Article struct {
Title string `json:"title"`
Content string `json:"content"`
}
func main() {
}
As you can see, currently our article has only the title and the content itself. We will add some image later which will improve the mobile app look.
Next, let’s connect to a Database. Start getting necessary packages by running the following comamnd and by creating a function ConnectDB and initialize the Gorm ORM inside it.
go get -u gorm.io/gorm && go get -u gorm.io/driver/sqlite
var Db *gorm.DB
func ConnectDB() {
db, err := gorm.Open(sqlite.Open("blog.db"), &gorm.Config{})
if err != nil {
panic("Failed to connect database.")
}
// Migrate the schema
db.AutoMigrate(&Article{})
Db = db
}
Next, add import statements for gorm and sqlite driver.
import (
"gorm.io/gorm"
"gorm.io/driver/sqlite"
)
After adding import statements for gorm and sqlite, you can add for fiber as well, and then you can initialize fiber, database and server.
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// Article struct
type Article struct {
gorm.Model
Title string `json:"title"`
Content string `json:"content"`
}
func main() {
// Initialize fiber
app := fiber.New()
// Middleware
app.Use(logger.New())
app.Use(recover.New())
// Initialize Database
ConnectDB()
// Start Server
app.Listen(":3000")
}
var Db *gorm.DB
func ConnectDB() {
db, err := gorm.Open(sqlite.Open("blog.db"), &gorm.Config{})
if err != nil {
panic("Failed to connect database.")
}
// Migrate the schema
db.AutoMigrate(&Article{})
Db = db
}
To run it, use go run main.go command. Currently, API will run on port 3000. Since we didn’t create any route, if you open http://localhost:3000 in your browser, you will get “Cannot GET /”.
Creating routes using fiber is easy. This is an example of a simple route:
// Respond with "Hello, World!" on root path, "/"
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
Now let’s add a POST route to create a new article.
// Initialize Routes
app.Post("/api/articles", func(c *fiber.Ctx) error {
// Bind the request body to an article struct
article := new(Article)
if err := c.BodyParser(&article); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": err.Error(),
})
}
// Save the article to the database
Db.Create(&article)
// Respond with the created article
return c.Status(fiber.StatusCreated).JSON(article)
})
As you can see, we have set /api/articles as our path to create new articles, which basically check your request and returns a 400 error if there is some error, together with the error message, else it will return a 201 status code, with the article itself.
Once done, you can continue and add routes to get all articles, get a single article, update an article and delete an article. This is how the end result would look like.
package main
import (
"strconv"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// Article struct
type Article struct {
gorm.Model
Title string `json:"title"`
Content string `json:"content"`
}
func main() {
// Initialize fiber
app := fiber.New()
// Middleware
app.Use(logger.New())
app.Use(recover.New())
// Initialize Database
ConnectDB()
// Initialize Routes
app.Post("/api/articles", func(c *fiber.Ctx) error {
// Bind the request body to an article struct
article := new(Article)
if err := c.BodyParser(&article); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": err.Error(),
})
}
// Save the article to the database
Db.Create(&article)
// Respond with the created article
return c.Status(fiber.StatusCreated).JSON(article)
})
app.Get("/api/articles", func(c *fiber.Ctx) error {
var articles []Article
db := Db
// Get articles from database
db.Find(&articles)
return c.Status(fiber.StatusOK).JSON(articles)
})
app.Get("/api/articles/:id", func(c *fiber.Ctx) error {
var article Article
db := Db
paramId := c.Params("id")
id, err := strconv.Atoi(paramId)
if err != nil {
c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Cannot parse id",
})
return err
}
// Get article from database
db.Find(&article, id)
// Return result
if int(article.ID) == id {
return c.Status(fiber.StatusOK).JSON(article)
}
//
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "Article not found",
})
})
app.Put("/api/articles/:id", func(c *fiber.Ctx) error {
db := Db
type request struct {
Title *string `json:"title"`
Content *string `json:"content"`
}
paramId := c.Params("id")
id, err := strconv.Atoi(paramId)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Cannot parse id",
})
}
var body request
err = c.BodyParser(&body)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Cannot parse body",
})
}
var article Article
db.First(&article, id)
// Handle 404
if int(article.ID) != id {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "Article not found",
})
}
if body.Title != nil {
article.Title = *body.Title
}
if body.Content != nil {
article.Content = *body.Content
}
db.Save(&article)
return c.Status(fiber.StatusOK).JSON(article)
})
app.Delete("/api/articles/:id", func(c *fiber.Ctx) error {
db := Db
paramId := c.Params("id")
id, err := strconv.Atoi(paramId)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Cannot parse id",
})
}
var article Article
db.First(&article, id)
if int(article.ID) != id {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "Article not found.",
})
}
db.Delete(&article)
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"status": "Artice deleted successfully",
})
})
// Start Server
app.Listen(":3000")
}
var Db *gorm.DB
func ConnectDB() {
db, err := gorm.Open(sqlite.Open("blog.db"), &gorm.Config{})
if err != nil {
panic("Failed to connect database.")
}
// Migrate the schema
db.AutoMigrate(&Article{})
Db = db
}
In the next article, we will go through flutter and create an application that will pull data from this API.
7 comments On Building a Simple Blog Mobile App with Flutter, Golang Fiber API and SQLite
You got my attention. Waiting for the next one
Can you write something about the structures in go, and why using them? Also why everyone is using frameworks and not just doing it natively?
I will write about that topic in the future. Some people prefer using frameworks instead of stdlib of Go for faster and more convenient development, as frameworks often provide pre-built modules and abstractions to handle common tasks. Every technical choice involves making trade-offs. It’s important to consider the options and select the trade-offs that best fit your needs and the goals of your project. Keep in mind that other languages often have established frameworks that are considered the default option. For example, Spring for Java, Django and Flask for Python, Rails for Ruby, ASP.NET for C#, Express for Node, and Symfony and Laravel for PHP.
How hard would be to add featured image? Also does Go support templating like Express.js does?
I am gonna write in future how to add image as well, let’s say it will be a part 3. It does support templating. But mostly go is used for API’s.
How about gin vs echo?
Nice post. I learn something new and challenging on websites I stumbleupon every day. It will always be useful to read through articles from other writers and use a little something from other websites.