Skip to content

Asura409/Go-journey-documentation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

Documentation of my learning path in Golang

Basics

datatypes and variables

Go like any other language has different ways data can be expressed Numerical: int, float, double bool : true, false text : Strings Container types : Arrays and Slices Custom Types : structs

// ways to declare variables
var num = 5
//or the more compact form
num := 5
// you can also declare multiple variables in go
num1, num2 := 5, 6

//strings for text
name := "John"

// arrays and slices
//arrays have static size i.e the size cannot be changed after declaration
arr := [3]int{1,2,3}

// slices are dynamic and are resizable
slice := []int{1,2,3}

conditionals

there are ywo main conditional structures in go

//if-else statement
age := 18
if age < 18{
    fmt.Print("you cant drive")
}else{
    fmt.Print("you can drive")
}

//switch statement
switch day{
    case 1:
        fmt.Print("Monday")
    case 2:
        fmt.Print("Tuesday")
    case 3:
        fmt.Print("Wednesday")
    case 4:
        fmt.Print("Thursday")
        case 5:
        fmt.Print("Friday")
    case 6:
        fmt.Print("Saturday")
    case 7:
        fmt.Print("Sunday")
    default:
        fmt.Print("Invalid day")
}

loops

like any other language go has repetitive structures known as loops, but unlike others go only has one loop structure which is for

// standard for loop
// prints numbers from 0 to 9
for i:=0; i<10; i++{
    fmt.Print(i)
}

// while loop
i:=0
for i<10{
    fmt.Print(i)
    i++
}

// range based loop
//usually to access indices and values of container structures
// prints the indices of the array with their corresponding values
arr := [3]int{1,2,3}    
for index, value := range arr{
    fmt.Print("index: %d, value: %d\n", index, value)
}

functions

functions are blocks of code that accomplish specific tasks and have at least one return value. Note that variables used un functions are copies and are only native to the function.

// return one value
func add (a int, b int) int{
    return a+b
}
// function with 2 return values
func isEven(num int) bool, error{
    if num%2 == 0{
        return true, nil
    }
    return false, nil
    }

structs and interfaces

these are the features in Go that enables its OOP qualities

 type User struct{
    name string
    age int
}

// above we can see that a user is a type with a name and an age field
// and fields can be referenced using the dot operator

var user User
user.name = "John"
user.age = 20
// so this is a user who's name is John and is 20 years old

Meanwhile an interface is used for types that satisfy a certain condition, it is like go's nifty way of expressing polymorphism

type Animal interface{
    Speak() string
}
type Dog struct{
    name string
}
type Cat struct{
    name string
}
func (d Dog) Speak() string{
    return "Woof!"
}
func (c Cat) Speak() string{
    return "Meow!"
}
func main(){
    dog := Dog{"Fido"}
    cat := Cat{"Whiskers"}
    animals := []Animal{dog, cat}
    for _, animal := range animals{
        fmt.Println(animal.Speak())
    }
}
// dogs and cats can use the animal interface because they meet the "speak" requirement 
// dog goes woof and cat goes meow

pointers

pointers are addresses that store the memory address of a variable

x := 5
var p *int
p = &x
//prints the address of x
fmt.print("value stored in the address of p : %p\n", p )
// we can get the value at x by dereferencing p
fmt.print("value of x : %d\n", *p)

//pointers can also be passed as variables of functions
// when pointers are used any operations that changes the value of the variables in the function will reflect outside the function
func main(){
    x := 5
    y := 10
    swap(&x, &y)
    fmt.Print("x: %d, y: %d\n", x, y)
    // prints x: 10, y: 5   
}

func swap(a *int, b *int){
    temp := *a
    *a = *b
    *b = temp
}

errors

is a type Native to Go which checks if there is an error, possible values are nil or an errorif

func divide(a int, b int) (int, error){
    if b == 0{
        return 0, errors.New("cannot divide by zero")
    }
    return a/b, nil
}
// return error message if b is 0, since divison by 0 is not possible

func sqrt(x float64) (float64, error){
    if x < 0{
        return 0, errors.New("cannot take square root of negative number")
    }
    return math.Sqrt(x), nil
}
// negative numbers have no real square roots

packages

packages are files that provide some level of abstraction for certain operations net/http : web programming golang-jwt : JWT Authentication godotenv : read content from .env files gorm : Go Object Relational Model e.t.c.

Web Framework

these are packages made specifically for the rich features that provide good web abstractions during development Go has a multitude of such frameworks but we'll be focusing on fiber

you nee to get the following package

go get github.com/gofiber/fiber/v2
//basic fiber app
package main

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

func main() {
	app := fiber.New()
    app.Get("/", func(c *fiber.Ctx) error {
		return c.SendString("Hello, World!")
	})
    app.Listen(":3000")
}

HTTP METHODS

Documentation of My Learning Path in Golang

Web Framework (Continued)

HTTP Methods in Fiber

Fiber supports all standard HTTP methods:

package main

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

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

    // GET method
    app.Get("/api/data", func(c *fiber.Ctx) error {
        return c.SendString("GET request received")
    })

    // POST method
    app.Post("/api/data", func(c *fiber.Ctx) error {
        return c.SendString("POST request received")
    })

    // PUT method
    app.Put("/api/data/:id", func(c *fiber.Ctx) error {
        return c.SendString("PUT request for ID: " + c.Params("id"))
    })

    // DELETE method
    app.Delete("/api/data/:id", func(c *fiber.Ctx) error {
        return c.SendString("DELETE request for ID: " + c.Params("id"))
    })

    // PATCH method
    app.Patch("/api/data/:id", func(c *fiber.Ctx) error {
        return c.SendString("PATCH request for ID: " + c.Params("id"))
    })

    // All method (matches any HTTP method)
    app.All("/api/all", func(c *fiber.Ctx) error {
        return c.SendString("This matches any HTTP method")
    })

    app.Listen(":3000")
}

Handlers and Routing

Handlers in Fiber are functions that process HTTP requests. Here's a more structured approach:

package main

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

// User struct for JSON parsing
type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

// Handler functions
func homeHandler(c *fiber.Ctx) error {
    return c.SendString("Welcome to the homepage!")
}

func getUserHandler(c *fiber.Ctx) error {
    id := c.Params("id")
    return c.JSON(fiber.Map{"id": id, "name": "John Doe"})
}

func createUserHandler(c *fiber.Ctx) error {
    user := new(User)
    if err := c.BodyParser(user); err != nil {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
    }
    return c.JSON(user)
}

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

    // Route grouping
    api := app.Group("/api")
    
    // Basic route
    app.Get("/", homeHandler)
    
    // API routes
    api.Get("/users/:id", getUserHandler)
    api.Post("/users", createUserHandler)

    app.Listen(":3000")
}

Middleware

Middleware functions execute between the request and response:

package main

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

// Logger middleware
func logger(c *fiber.Ctx) error {
    fmt.Printf("%s - %s\n", c.Method(), c.Path())
    return c.Next()
}

// Auth middleware
func authRequired(c *fiber.Ctx) error {
    token := c.Get("Authorization")
    if token != "secret-token" {
        return c.Status(fiber.StatusUnauthorized).SendString("Unauthorized")
    }
    return c.Next()
}

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

    // Global middleware
    app.Use(logger)

    // Public route
    app.Get("/public", func(c *fiber.Ctx) error {
        return c.SendString("Public content")
    })

    // Protected route with auth middleware
    app.Get("/private", authRequired, func(c *fiber.Ctx) error {
        return c.SendString("Private content")
    })

    app.Listen(":3000")
}

Cookies and Sessions

package main

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/session"
    "github.com/gofiber/storage/redis"
)

func main() {
    // Session storage using Redis
    store := session.New(session.Config{
        Storage: redis.New(redis.Config{
            Host:     "localhost",
            Port:     6379,
            Username: "",
            Password: "",
            Database: 1,
        }),
    })

    app := fiber.New()

    app.Get("/set-cookie", func(c *fiber.Ctx) error {
        c.Cookie(&fiber.Cookie{
            Name:     "token",
            Value:    "random-value",
            Expires:  time.Now().Add(24 * time.Hour),
            HTTPOnly: true,
            Secure:   true,
        })
        return c.SendString("Cookie set")
    })

    app.Get("/get-cookie", func(c *fiber.Ctx) error {
        return c.SendString("Cookie value: " + c.Cookies("token"))
    })

    app.Get("/set-session", func(c *fiber.Ctx) error {
        sess, err := store.Get(c)
        if err != nil {
            return err
        }
        sess.Set("username", "john_doe")
        if err := sess.Save(); err != nil {
            return err
        }
        return c.SendString("Session set")
    })

    app.Get("/get-session", func(c *fiber.Ctx) error {
        sess, err := store.Get(c)
        if err != nil {
            return err
        }
        username := sess.Get("username")
        return c.SendString("Session username: " + username.(string))
    })

    app.Listen(":3000")
}

Session-Based Authentication

package main

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/session"
)

var store = session.New()

type User struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

func login(c *fiber.Ctx) error {
    var user User
    if err := c.BodyParser(&user); err != nil {
        return err
    }

    // In a real app, verify credentials against database
    if user.Username != "admin" || user.Password != "password123" {
        return c.Status(fiber.StatusUnauthorized).SendString("Invalid credentials")
    }

    sess, err := store.Get(c)
    if err != nil {
        return err
    }
    sess.Set("authenticated", true)
    sess.Set("username", user.Username)
    if err := sess.Save(); err != nil {
        return err
    }

    return c.SendString("Logged in successfully")
}

func authMiddleware(c *fiber.Ctx) error {
    sess, err := store.Get(c)
    if err != nil {
        return err
    }
    if auth, ok := sess.Get("authenticated").(bool); !ok || !auth {
        return c.Status(fiber.StatusUnauthorized).SendString("Unauthorized")
    }
    return c.Next()
}

func profile(c *fiber.Ctx) error {
    sess, err := store.Get(c)
    if err != nil {
        return err
    }
    username := sess.Get("username")
    return c.JSON(fiber.Map{"username": username})
}

func logout(c *fiber.Ctx) error {
    sess, err := store.Get(c)
    if err != nil {
        return err
    }
    if err := sess.Destroy(); err != nil {
        return err
    }
    return c.SendString("Logged out successfully")
}

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

    app.Post("/login", login)
    app.Get("/profile", authMiddleware, profile)
    app.Get("/logout", authMiddleware, logout)

    app.Listen(":3000")
}

PostgreSQL Database with GORM

package main

import (
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
    "github.com/gofiber/fiber/v2"
)

type User struct {
    gorm.Model
    Username string `gorm:"unique;not null"`
    Email    string `gorm:"unique;not null"`
    Password string `gorm:"not null"`
}

var DB *gorm.DB

func initDB() {
    dsn := "host=localhost user=postgres password=postgres dbname=golang port=5432 sslmode=disable TimeZone=UTC"
    var err error
    DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }
    DB.AutoMigrate(&User{})
}

func createUser(c *fiber.Ctx) error {
    user := new(User)
    if err := c.BodyParser(user); err != nil {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
    }

    result := DB.Create(&user)
    if result.Error != nil {
        return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
    }

    return c.JSON(user)
}

func getUser(c *fiber.Ctx) error {
    id := c.Params("id")
    var user User
    result := DB.First(&user, id)
    if result.Error != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
    }
    return c.JSON(user)
}

func updateUser(c *fiber.Ctx) error {
    id := c.Params("id")
    var user User
    result := DB.First(&user, id)
    if result.Error != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
    }

    updates := new(User)
    if err := c.BodyParser(updates); err != nil {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
    }

    DB.Model(&user).Updates(updates)
    return c.JSON(user)
}

func deleteUser(c *fiber.Ctx) error {
    id := c.Params("id")
    result := DB.Delete(&User{}, id)
    if result.Error != nil {
        return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
    }
    return c.SendString("User deleted successfully")
}

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

    app.Post("/users", createUser)
    app.Get("/users/:id", getUser)
    app.Put("/users/:id", updateUser)
    app.Delete("/users/:id", deleteUser)

    app.Listen(":3000")
}

About

just an overview of my learning path for Golang

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published