Building a Simple Blog Mobile App with Flutter, Golang Fiber API and SQLite

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:

  • Basic knowledge of Golang and API development
  • Basic knowledge of Flutter and mobile development

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.

ShellScript
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.

ShellScript
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

  • Avatar

    You got my attention. Waiting for the next one

  • Avatar

    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?

    • Avatar

      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.

  • Avatar

    How hard would be to add featured image? Also does Go support templating like Express.js does?

    • Avatar

      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.

  • Avatar

    How about gin vs echo?

  • Avatar
    דירות דיסקרטיות באשדוד

    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.

Leave a reply:

Your email address will not be published.

Site Footer