Cara menggunakan sprintf postgresql

Kita akan menggunakan library gorilla / mux untuk apis dan PostgreSQL DB untuk menyimpan data. Oke langsung saja ke tutorialnya, simak baik-baik.

Yang diperlukan:

Setup Project

Kita buat project direktori terlebih dahulu. terserah kalian beri nama apa.  Contoh disini kita beri nama golang-postgres-crud
Buka terminal di folder yang kita buat tadi. Kita akan menginisialisasikan go modules, perintahnya sebegai berikut

go mod init go-postgres-crud

Go tidak mendukung mekanisme local include seperti kita memanggil skrip kode lokal(local import) dengan memasukan alamat skrip kode tersebut:

import “../../hw.go”  // contoh salah

Untuk itu kita mengunakan nama package, nama package ini hirarkis berdasarkan penempatan pada foldernya dan tentunya kita wajib membuat go modules (go mod init ) bila berurusan dengan banyak local import.

Jika ingin mengakses / memanggil skrip kode lokal(local import) kita hanya memanggil dan menginisialisasikan di line pertama file kita. contoh

import “/modul”

/*
 panggil fungsi yang ada di modul yang kita import
*/ 

Ketika menjalankan go mod init tadi, maka akan membuat sebuah file baru yaitu go.mod

Install Package

Pada projek ini kita menggunakan 3 package.
Buka terminal didalam folder yang kita buat tadi sebelumnya (didalam folder golang-postgres-crud )
Jalankan perintah beberapa perintah dibawah ini

Package g

import “/modul”

/*
 panggil fungsi yang ada di modul yang kita import
*/ 
2 mengimplementasikan request router dan dispatcher untuk menangkap incoming requests untuk respective handler. Jalankan perintah perintah dibawah ini

import “/modul”

/*
 panggil fungsi yang ada di modul yang kita import
*/ 
3

Package

import “/modul”

/*
 panggil fungsi yang ada di modul yang kita import
*/ 
4adalah package sebagai postgres driver. Jalankan perintah perintah dibawah ini

import “/modul”

/*
 panggil fungsi yang ada di modul yang kita import
*/ 
5

Kita akan menggunakan package godotenv untuk membaca file .env . file .env digunakan untuk menyimpan environment variables. Environment variables digunakan untuk menjaga keamanan data sensitif. Jalankan perintah perintah dibawah ini

import “/modul”

/*
 panggil fungsi yang ada di modul yang kita import
*/ 
6

Jika sudah menjalankan perintah diatas, kita buka file go.mod dan cek file tersebut. Semua package telah terinstall terlihat dari list di file tersebut.

Seperti contoh dibawah ini

module go-postgres-crud

go 1.13

require (
	github.com/gorilla/mux v1.8.0
	github.com/joho/godotenv v1.3.0
	github.com/lib/pq v1.8.0
)

Postgres Setup

mengapa menggunakan PostgreSQL? sistem object-relational database open source yang powerful. Ini dikenal karena reliability, ketahanan fitur, dan kinerja.

Silahkan jalankan query dibawah ini.

-- Table: buku

-- DROP TABLE buku;

CREATE TABLE buku
(
 id serial NOT NULL,
 judul_buku character varying NOT NULL,
 penulis character varying,
 tgl_publikasi date,
 CONSTRAINT pk_buku PRIMARY KEY (id )
)
WITH (
 OIDS=FALSE
);
ALTER TABLE buku
 OWNER TO postgres;

Ketika menjalankan query sql diatas, maka akan membuat tabel buku.

Let's Coding

Struktur direktori

Cara menggunakan sprintf postgresql

Folder config, didalamnya ada file config.go yang memiliki fungsi untuk konfigurasi koneksi ke DB PostgreSql.
Folder controller, didalamnya terdapat file buku.go yang memiliki fungsi sebagai wadah komunikasi awal sebelum terjadinya transaksi data ke db.
Folder models, didalamnya terdapat file models.go yang memiliki fungsi sebagai komunikasi ke db yang biasanya terjadi operasi transaksi CRUD data.
Folder router, didalamnya terdapat file router.go yang memiliki fungsi sebagai penunjuk arah route mana yang tersedia dan akan memanggil fungsi controller sesuai route yang diminta(request).
File main.go, adalah file utama dalam projek ini, kita menjalankan projek ini dengan menjalankan / mengeksekusi file ini terlebih dahulu.

Jangan lupa!!!
Untuk penamaan function harus diawali dengan huruf kapital.
Karena untuk pemanggilan dari file skrip kode lain

Config

Didalam folder config kita akan membuat file yaitu config.go,
Silahkan copy code dibawah ini;

package config

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/joho/godotenv" // package used to read the .env file
	_ "github.com/lib/pq"      // postgres golang driver
)

// kita buat koneksi dgn db posgres
func CreateConnection() *sql.DB {
	// load .env file
	err := godotenv.Load(".env")

	if err != nil {
		log.Fatalf("Error loading .env file")
	}

	// Kita buka koneksi ke db
	db, err := sql.Open("postgres", os.Getenv("POSTGRES_URL"))

	if err != nil {
		panic(err)
	}

	// check the connection
	err = db.Ping()

	if err != nil {
		panic(err)
	}

	fmt.Println("Sukses Konek ke Db!")
	// return the connection
	return db
}

type NullString struct {
	sql.NullString
}

func (s NullString) MarshalJSON() ([]byte, error) {
	if !s.Valid {
		return []byte("null"), nil
	}
	return json.Marshal(s.String)
}

func (s *NullString) UnmarshalJSON(data []byte) error {
	if string(data) == "null" {
		s.String, s.Valid = "", false
		return nil
	}
	s.String, s.Valid = string(data), true
	return nil
}

Penjelasan:

Function yang utama disini adalah CreateConnection(). Function ini sebagai konektor ke database Postgres.
Kita akan mengload / memanggil file .env yang dimana terdapat konfigurasi untuk menyambung projek kita ke databasenya.
Setelah itu kita buka koneksi ke db postgres.
Kita cek dengan Ping() apakah sudah terkoneksi apa belum.
Jika sudah terkoneksi maka dia akan menampilkan text "Sukses Konek ke Db!" di terminal.

Fungsi lain yang kita buat ada 2, kedua fungsi ini berguna untuk menampung jika struct / data bertipe NULL maka dia akan mengisikan data(string) kosong.

Router

Didalam folder router kita akan membuat file yaitu router.go,
Silahkan copy code dibawah ini;

package router

import (
	"go-postgres-crud/controller"

	"github.com/gorilla/mux"
)

func Router() *mux.Router {

	router := mux.NewRouter()

	router.HandleFunc("/api/buku", controller.AmbilSemuaBuku).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/buku/{id}", controller.AmbilBuku).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/buku", controller.TmbhBuku).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/buku/{id}", controller.UpdateBuku).Methods("PUT", "OPTIONS")
	router.HandleFunc("/api/buku/{id}", controller.HapusBuku).Methods("DELETE", "OPTIONS")

	return router
}

Penjelasan:

Function yang utama disini adalah Router(). Function ini sebagai memberi arah yang didapat dari request user.
Jika request mengarah ke /api/buku maka dia akan memanggil controller functionnya AmbilSemuaBuku dengan method GET
dan seterusnya.

Controller

Didalam folder controller kita akan membuat file yaitu buku.go,
Silahkan copy code dibawah ini;

package controller

import (
	"encoding/json" // package untuk enkode dan mendekode json menjadi struct dan sebaliknya
	"fmt"
	"strconv" // package yang digunakan untuk mengubah string menjadi tipe int

	"log"
	"net/http" // digunakan untuk mengakses objek permintaan dan respons dari api

	"go-postgres-crud/models" //models package dimana Buku didefinisikan

	"github.com/gorilla/mux" // digunakan untuk mendapatkan parameter dari router
	_ "github.com/lib/pq"    // postgres golang driver
)

type response struct {
	ID      int64  `json:"id,omitempty"`
	Message string `json:"message,omitempty"`
}

type Response struct {
	Status  int           `json:"status"`
	Message string        `json:"message"`
	Data    []models.Buku `json:"data"`
}

// TambahBuku
func TmbhBuku(w http.ResponseWriter, r *http.Request) {

	// create an empty user of type models.User
	// kita buat empty buku dengan tipe models.Buku
	var buku models.Buku

	// decode data json request ke buku
	err := json.NewDecoder(r.Body).Decode(&buku)

	if err != nil {
		log.Fatalf("Tidak bisa mendecode dari request body.  %v", err)
	}

	// panggil modelsnya lalu insert buku
	insertID := models.TambahBuku(buku)

	// format response objectnya
	res := response{
		ID:      insertID,
		Message: "Data buku telah ditambahkan",
	}

	// kirim response
	json.NewEncoder(w).Encode(res)
}

// AmbilBuku mengambil single data dengan parameter id
func AmbilBuku(w http.ResponseWriter, r *http.Request) {
	// kita set headernya
	w.Header().Set("Context-Type", "application/x-www-form-urlencoded")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	// dapatkan idbuku dari parameter request, keynya adalah "id"
	params := mux.Vars(r)

	// konversi id dari tring ke int
	id, err := strconv.Atoi(params["id"])

	if err != nil {
		log.Fatalf("Tidak bisa mengubah dari string ke int.  %v", err)
	}

	// memanggil models ambilsatubuku dengan parameter id yg nantinya akan mengambil single data
	buku, err := models.AmbilSatuBuku(int64(id))

	if err != nil {
		log.Fatalf("Tidak bisa mengambil data buku. %v", err)
	}

	// kirim response
	json.NewEncoder(w).Encode(buku)
}

// Ambil semua data buku
func AmbilSemuaBuku(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Context-Type", "application/x-www-form-urlencoded")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	// memanggil models AmbilSemuaBuku
	bukus, err := models.AmbilSemuaBuku()

	if err != nil {
		log.Fatalf("Tidak bisa mengambil data. %v", err)
	}

	var response Response
	response.Status = 1
	response.Message = "Success"
	response.Data = bukus

	// kirim semua response
	json.NewEncoder(w).Encode(response)
}

func UpdateBuku(w http.ResponseWriter, r *http.Request) {

	// kita ambil request parameter idnya
	params := mux.Vars(r)

	// konversikan ke int yang sebelumnya adalah string
	id, err := strconv.Atoi(params["id"])

	if err != nil {
		log.Fatalf("Tidak bisa mengubah dari string ke int.  %v", err)
	}

	// buat variable buku dengan type models.Buku
	var buku models.Buku

	// decode json request ke variable buku
	err = json.NewDecoder(r.Body).Decode(&buku)

	if err != nil {
		log.Fatalf("Tidak bisa decode request body.  %v", err)
	}

	// panggil updatebuku untuk mengupdate data
	updatedRows := models.UpdateBuku(int64(id), buku)

	// ini adalah format message berupa string
	msg := fmt.Sprintf("Buku telah berhasil diupdate. Jumlah yang diupdate %v rows/record", updatedRows)

	// ini adalah format response message
	res := response{
		ID:      int64(id),
		Message: msg,
	}

	// kirim berupa response
	json.NewEncoder(w).Encode(res)
}

func HapusBuku(w http.ResponseWriter, r *http.Request) {

	// kita ambil request parameter idnya
	params := mux.Vars(r)

	// konversikan ke int yang sebelumnya adalah string
	id, err := strconv.Atoi(params["id"])

	if err != nil {
		log.Fatalf("Tidak bisa mengubah dari string ke int.  %v", err)
	}

	// panggil fungsi hapusbuku , dan convert int ke int64
	deletedRows := models.HapusBuku(int64(id))

	// ini adalah format message berupa string
	msg := fmt.Sprintf("buku sukses di hapus. Total data yang dihapus %v", deletedRows)

	// ini adalah format reponse message
	res := response{
		ID:      int64(id),
		Message: msg,
	}

	// send the response
	json.NewEncoder(w).Encode(res)
}

Penjelasan:

kali ini saya tidak menjelaskan banyak, karena tiap line kode sudah beri penjelasannya.

Dari kode diatas terdapat 2 struct yaitu response dan Response yang berguna untuk menampilkan data berupa JSON.
struct response untuk menampung / menginisialisasi jika message/pesannya error atau sukses ketika memproses Add / Delete data
struct Response untuk menampung / menginisialisasi jika ada request Insert / update / display data

Dari kode diatas terdapat 5 function yang memiliki tugas yang berbeda-beda.
Function TmbhBuku untuk menambah data buku baru (Insert)
Function AmbilBuku untuk mengambil salah satu data buku (GET)
Function AmbilSemuaBuku untuk mengambil semua data buku (GET)
Funtion UpdateBuku untuk mengupdate salah satu / banyak data buku (PUT)
Function HapusBuku untuk menghapus salah satu / banyak data buku (DELETE)

Models

Didalam folder models kita akan membuat file yaitu models.go,
Silahkan copy code dibawah ini;

package models

import (
	"database/sql"
	"fmt"
	"go-postgres-crud/config"
	"log"

	_ "github.com/lib/pq" // postgres golang driver
)

// Buku schema dari tabel Buku
// kita coba dengan jika datanya null
// jika return datanya ada yg null, silahkan pake NullString, contohnya dibawah
// Penulis       config.NullString `json:"penulis"`
type Buku struct {
	ID            int64  `json:"id"`
	Judul_buku    string `json:"judul_buku"`
	Penulis       string `json:"penulis"`
	Tgl_publikasi string `json:"tgl_publikasi"`
}

func TambahBuku(buku Buku) int64 {

	// mengkoneksikan ke db postgres
	db := config.CreateConnection()

	// kita tutup koneksinya di akhir proses
	defer db.Close()

	// kita buat insert query
	// mengembalikan nilai id akan mengembalikan id dari buku yang dimasukkan ke db
	sqlStatement := `INSERT INTO buku (judul_buku, penulis, tgl_publikasi) VALUES ($1, $2, $3) RETURNING id`

	// id yang dimasukkan akan disimpan di id ini
	var id int64

	// Scan function akan menyimpan insert id didalam id id
	err := db.QueryRow(sqlStatement, buku.Judul_buku, buku.Penulis, buku.Tgl_publikasi).Scan(&id)

	if err != nil {
		log.Fatalf("Tidak Bisa mengeksekusi query. %v", err)
	}

	fmt.Printf("Insert data single record %v", id)

	// return insert id
	return id
}

// ambil satu buku
func AmbilSemuaBuku() ([]Buku, error) {
	// mengkoneksikan ke db postgres
	db := config.CreateConnection()

	// kita tutup koneksinya di akhir proses
	defer db.Close()

	var bukus []Buku

	// kita buat select query
	sqlStatement := `SELECT * FROM buku`

	// mengeksekusi sql query
	rows, err := db.Query(sqlStatement)

	if err != nil {
		log.Fatalf("tidak bisa mengeksekusi query. %v", err)
	}

	// kita tutup eksekusi proses sql qeurynya
	defer rows.Close()

	// kita iterasi mengambil datanya
	for rows.Next() {
		var buku Buku

		// kita ambil datanya dan unmarshal ke structnya
		err = rows.Scan(&buku.ID, &buku.Judul_buku, &buku.Penulis, &buku.Tgl_publikasi)

		if err != nil {
			log.Fatalf("tidak bisa mengambil data. %v", err)
		}

		// masukkan kedalam slice bukus
		bukus = append(bukus, buku)

	}

	// return empty buku atau jika error
	return bukus, err
}

// mengambil satu buku
func AmbilSatuBuku(id int64) (Buku, error) {
	// mengkoneksikan ke db postgres
	db := config.CreateConnection()

	// kita tutup koneksinya di akhir proses
	defer db.Close()

	var buku Buku

	// buat sql query
	sqlStatement := `SELECT * FROM buku WHERE id=$1`

	// eksekusi sql statement
	row := db.QueryRow(sqlStatement, id)

	err := row.Scan(&buku.ID, &buku.Judul_buku, &buku.Penulis, &buku.Tgl_publikasi)

	switch err {
	case sql.ErrNoRows:
		fmt.Println("Tidak ada data yang dicari!")
		return buku, nil
	case nil:
		return buku, nil
	default:
		log.Fatalf("tidak bisa mengambil data. %v", err)
	}

	return buku, err
}

// update user in the DB
func UpdateBuku(id int64, buku Buku) int64 {

	// mengkoneksikan ke db postgres
	db := config.CreateConnection()

	// kita tutup koneksinya di akhir proses
	defer db.Close()

	// kita buat sql query create
	sqlStatement := `UPDATE buku SET judul_buku=$2, penulis=$3, tgl_publikasi=$4 WHERE id=$1`

	// eksekusi sql statement
	res, err := db.Exec(sqlStatement, id, buku.Judul_buku, buku.Penulis, buku.Tgl_publikasi)

	if err != nil {
		log.Fatalf("Tidak bisa mengeksekusi query. %v", err)
	}

	// cek berapa banyak row/data yang diupdate
	rowsAffected, err := res.RowsAffected()

	//kita cek
	if err != nil {
		log.Fatalf("Error ketika mengecheck rows/data yang diupdate. %v", err)
	}

	fmt.Printf("Total rows/record yang diupdate %v\n", rowsAffected)

	return rowsAffected
}

func HapusBuku(id int64) int64 {

	// mengkoneksikan ke db postgres
	db := config.CreateConnection()

	// kita tutup koneksinya di akhir proses
	defer db.Close()

	// buat sql query
	sqlStatement := `DELETE FROM buku WHERE id=$1`

	// eksekusi sql statement
	res, err := db.Exec(sqlStatement, id)

	if err != nil {
		log.Fatalf("tidak bisa mengeksekusi query. %v", err)
	}

	// cek berapa jumlah data/row yang di hapus
	rowsAffected, err := res.RowsAffected()

	if err != nil {
		log.Fatalf("tidak bisa mencari data. %v", err)
	}

	fmt.Printf("Total data yang terhapus %v", rowsAffected)

	return rowsAffected
}

Penjelasan:

kali ini saya tidak menjelaskan banyak, karena tiap line kode sudah beri penjelasannya.

Dari kode diatas terdapat 1 struct yaitu Buku Struct yang berguna untuk menampung / display data berupa JSON.

Dari kode diatas terdapat 5 function yang memiliki tugas yang berbeda-beda.
Function TambahBuku untuk menambah data buku baru
Function AmbilSatuBuku untuk mengambil salah satu data buku
Function AmbilSemuaBuku untuk mengambil semua data buku
Funtion UpdateBuku untuk mengupdate salah satu / banyak data buku
Function HapusBuku untuk menghapus salah satu / banyak data buku

Main.go

File main.go adalah server utama kita. erver akan running pada port 8080 dan menjalankan Router.

Buat file main.go, dan copy kode dibawah ini;

package main

import (
	"fmt"
	"go-postgres-crud/router"
	"log"
	"net/http"
)

func main() {
	r := router.Router()
	// fs := http.FileServer(http.Dir("build"))
	// http.Handle("/", fs)
	fmt.Println("Server dijalankan pada port 8080...")

	log.Fatal(http.ListenAndServe(":8080", r))
}

Jalankan server melalui terminal/cmd dengan perintah sebagai berikut

go run main.go

Test API

Kita akan menguji endpoint API dengan menggunakan insomnia.

Crate data buku (POST)

URL: http://127.0.0.1:8080/api/buku
Body: raw/json

Isikan json data sepperti dibawah ini

import “/modul”

/*
 panggil fungsi yang ada di modul yang kita import
*/ 
0
Cara menggunakan sprintf postgresql

Ambil data salah satu buku (GET)

URL: http://127.0.0.1:8080/api/buku/{id}

Cara menggunakan sprintf postgresql

Ambil semua data buku (GET)

URL: http://127.0.0.1:8080/api/buku/

Cara menggunakan sprintf postgresql

Update salah satu buku (PUT)

URL: http://127.0.0.1:8080/api/buku/{id}
Body: raw/json

Isikan json data sepperti dibawah ini

import “/modul”

/*
 panggil fungsi yang ada di modul yang kita import
*/ 
1
Cara menggunakan sprintf postgresql

Hapus salah satu buku (DELETE)

URL: http://127.0.0.1:8080/api/buku/{id}

Cara menggunakan sprintf postgresql

Sebelum dihapus

Cara menggunakan sprintf postgresql

Sesudah dihapus

Cara menggunakan sprintf postgresql


Oke cukup sekian,


Kita telah mengetahui cara membuat CRUD sederhana golang menggunakan database PostgreSql Jika kalian ingin mempelajari tertarik mempelajari Golang silahkan eksplorasi dokumentasinya ya dan sampai jumpa di konten Golang berikutnya.

Wassalamualaikum Warahmatullahi Wabarakatuh.
File project bisa dilihat disini

Cara menggunakan sprintf postgresql

Apabila tutorial diatas membantu kamu dalam belajar, dan jika kamu memiliki sedikit rezeki, mohon dukungannya dalam bentuk segelas kopi melalui URL dibawah ini: