Api with go and sqlite

  • compiles to machine code
  • garbage collected
  • goroutines / green threads
  • channels for thread coordination
  • statically typed
  • no classes or type inheritance
  • interfaces
  • simple language, simple tools
mkdir fiberapi
cd fiberapi
go
go version
go mod init "github.com/OzanOcak/fiberapi"
go get -u gorm.io/driver/mysql
go get -u gorm.io/gorm
go get github.com/gofiber/fiber/v2

Fiber is a Go web framework built on top of Fasthttp, the fastest HTTP engine for Go. Gorm is a ORM library for golang

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
	"log"
	"github.com/gofiber/fiber/v2"
)

func aloha(arg *fiber.Ctx)error{
	return arg.SendString("Hello world \n")
}

func main(){
	app := fiber.New()

	app.Get("/api",aloha)
	log.Fatal(app.Listen(":8000"))
}
go mod tidy
go run main.go
curl localhost/api -X GET -I
mkdir database
touch database/database.go

open database.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package database

import (
	"log"
	"os"

	//"github.com/OzanOcak/api/models"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

type DbInstance struct {
	Db *gorm.DB
}

var Database DbInstance

func ConnectDb() {
	db, err := gorm.Open(sqlite.Open("api.db"), &gorm.Config{})

	if err != nil {
		log.Fatal("Failed to connect to the database! \n", err)
		os.Exit(2)
	}

	log.Println("Connected Successfully to Database")
	db.Logger = logger.Default.LogMode(logger.Info)
	log.Println("Running Migrations")

	//db.AutoMigrate(&models.User{}, &models.Product{}, &models.Order{})

	Database = DbInstance{
		Db: db,
	}
}
	go get gorm.io/driver/sqlite
	go getgorm.io/gorm
	go get gorm.io/gorm/logger

    mkdir models
	touch models/{user.go,product.go,order.go}

order.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package models

import "time"

type Order struct {
	ID           uint `json:"id" gorm:"primaryKey"`
	CreatedAt    time.Time
	ProductRefer int     `json:"product_id"`
	Product      Product `gorm:"foreignKey:ProductRefer"`
	UserRefer    int     `json:"user_id"`
	User         User    `gorm:"foreignKey:UserRefer"`
}

product.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package models

import "time"

type Product struct {
	ID           uint `json:"id" gorm:"primaryKey"`
	CreatedAt    time.Time
	Name         string `json:"name"`
	SerialNumber string `json:"serial_number"`
}

user.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package models

import "time"

type User struct {
	ID        uint `json:"id" gorm:"primaryKey"`
	CreatedAt time.Time
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
}

now we can migrate models in database.go

1
2
3
"github.com/OzanOcak/fiberapi/models"

db.AutoMigrate(&models.User{}, &models.Product{}, &models.Order{})

and connect database in main function

1
database.ConnectDb()

click sql tool after download sql vscode plugin, add new connection

with SQLite plugin in vs code , we can see db table within vs code


1
2
mkdir routes
touch routes/user.go

user.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package routes

import (
	"api/database"
	"api/models"

	"github.com/gofiber/fiber/v2"
)

type User struct {
	ID uint `json:"id"`
	FirstName string `json:"first_name"`
	LastName string `json:"last_name"`
}

func CreateResponseUser(userModel models.User) User{
	return User{ID: userModel.ID, FirstName: userModel.FirstName, LastName: userModel.LastName}
}
func CreateUser(c *fiber.Ctx) error{
  var user models.User

  if err := c.BodyParser(&user); err != nil {
      return c.Status(404).JSON(err.Error())
  }
  database.Database.Db.Create(&user)
  responseUser := CreateResponseUser(user)

  return c.Status(200).JSON(responseUser);
}

main.go within setupRoutes and in main connect db

1
	app.Post("/api/users",routes.CreateUser)
 curl localhost:8000/users -X POST -d '{"first_name":"john","last_name":"doe"}' -H 'content-type:application/json'

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 func GetUsers(c *fiber.Ctx) error{
	 users := []models.User{}

	 database.Database.Db.Find(& users)
	 responseUsers := []User{}
	 for _,user := range users {
		 responseUser := CreateResponseUser(user)
		 responseUsers = append(responseUsers, responseUser)
	 }
	 return c.Status(200).JSON(responseUsers)

 }

1
 	app.Get("/api/users",routes.GetUsers)
curl localhost:8000/api/users -X GET

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func findUser(id int, user *models.User) error{
    database.Database.Db.Find(&user, "id=?",id)
	if user.ID == 0 {
		return errors.New("user does not exist ")
	}
	return nil
}

func GetUser(c *fiber.Ctx) error{
	id, err := c.ParamsInt("id")
	var user models.User

	if err !=nil {
		return c.Status(400).JSON("please enure that :id is an integer")
	}
	if err := findUser(id, &user); err != nil {
		return c.Status(400).JSON(err.Error())
	}

	responseUser := CreateResponseUser(user)

	return c.Status(200).JSON(responseUser)
}

and call the function in main function

1
	app.Get("/api/users/:id",routes.GetUser)
curl localhost:8000/users/2 -X GET

routes/user.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
func UpdateUser(c *fiber.Ctx) error {
	id, err := c.ParamsInt("id")

	var user models.User

	if err != nil {
		return c.Status(400).JSON("Please ensure that :id is an integer")
	}

	err = findUser(id, &user)

	if err != nil {
		return c.Status(400).JSON(err.Error())
	}

	type UpdateUser struct {
		FirstName string `json:"first_name"`
		LastName  string `json:"last_name"`
	}

	var updateData UpdateUser

	if err := c.BodyParser(&updateData); err != nil {
		return c.Status(500).JSON(err.Error())
	}

	user.FirstName = updateData.FirstName
	user.LastName = updateData.LastName

	database.Database.Db.Save(&user)

	responseUser := CreateResponseUser(user)

	return c.Status(200).JSON(responseUser)

}

main.go

1
app.Put("/users/:id",routes.UpdateUser)
curl localhost:8000/users/3 -X PUT -d '{"first_name":"tom","last_name":"cruse"}' -H 'content-type:application/json'

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func DeleteUser(c *fiber.Ctx) error {
	id, err := c.ParamsInt("id")

	var user models.User

	if err != nil {
		return c.Status(400).JSON("Please ensure that :id is an integer")
	}

	err = findUser(id, &user)

	if err != nil {
		return c.Status(400).JSON(err.Error())
	}

	if err = database.Database.Db.Delete(&user).Error; err != nil {
		return c.Status(404).JSON(err.Error())
	}
	return c.Status(200).JSON("Successfully deleted User")
}
1
app.DELETE("/users/:id",routes.DeleteUser)
curl localhost:8000/users/3 -X DELETE

routes/product.go

models/product.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package models

import "time"

type Product struct {
	ID           uint `json:"id" gorm:"primaryKey"`
	CreatedAt    time.Time
	Name         string `json:"name"`
	SerialNumber string `json:"serial_number"`
}

routes/product.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package routes

import (
	"github.com/OzanOcak/fiberapi/database"
	"github.com/OzanOcak/fiberapi/models"
	"github.com/gofiber/fiber/v2"
)

type Product struct {
	ID           uint   `json:"id"`
	Name         string `json:"name"`
	SerialNumber string `json:"serial_number"`
}

func CreateResponseProduct(product models.Product) Product {
	return Product {ID: product.ID, Name: product.Name, SerialNumber: product.SerialNumber}
}

func CreateProduct(c *fiber.Ctx) error {
	var product models.Product

	if err := c.BodyParser(&product); err != nil {
		return c.Status(400).JSON(err.Error())
	}

	database.Database.Db.Create(&product)
	responseProduct := CreateResponseProduct(product)
	return c.Status(200).JSON(responseProduct)
}

main.go

1
	app.Post("/products",routes.CreateProduct)

terminal

 curl localhost:8000/products -X POST -d '{"serial_number":"0002316","name":"Letrange"}' -H 'content-type:application/json'

product.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func GetProducts(c *fiber.Ctx) error {
	products := []models.Product{}
	database.Database.Db.Find(&products)
	responseProducts := []Product{}
	for _, product := range products {
		responseProduct := CreateResponseProduct(product)
		responseProducts = append(responseProducts, responseProduct)
	}

	return c.Status(200).JSON(responseProducts)
}
1
app.GET("/products",routes.GetProducts)
curl localhost:8000/products

1
	app.Post("/orders",routes.CreateOrder)
curl localhost:8000/orders -X POST -d '{"user_id":4,"product_id":1}' -H 'content-type:application/json'
1
	app.Get("/orders",routes.GetOrders)
curl localhost:8000/orders
1
	app.Get("/orders/:id",routes.GetOrder)
curl localhost:8000/orders/1