package main import ( "database/sql" "fmt" _ "github.com/jackc/pgx/v4/stdlib" // stdlib is the package that provides the standard library driver. To be able to use database sql package ) type PostgresConfig struct { Host string Port string User string Password string Database string SSLMode string } func (cfg PostgresConfig) String() string { return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.Database, cfg.SSLMode) } func main() { cfg := PostgresConfig{ Host: "localhost", Port: "5432", User: "baloo", Password: "junglebook", Database: "lenslocked", SSLMode: "disable", } // pgx = Driver name defined by github.
The first difference in this piece of code is that the “PostgresConfig type” is replacing the long string in the sql.Open() section, as it was in the previous version of the code.
The second and most important difference is that we are using db.Exec().
As far as I understand, it seems that db.Exec is used when the SQL query does not return rows, for example when doing “delete”, “update”, “insert”. On the other hand, when requiring a “select”, then db.
Contents Contents Why Am I Doing All this? Lab Info in CLI Requirements TLDR (Execution with Source Code) Current State Execution Flow Caveats Progress and Roadmap What Did I Learn? What Is Next? Why Am I Doing All this? Some time ago I decided to try to decrease my context(task) switching between different tools and programs when I am working.
I see that most of my colleagues rely on the browser or third party applications heavily for certain tasks.
Two things are required to interact with the database:
A library A driver Our library to interact with the database is ““database/sql” which is from the standard library.
While the driver is “github.com/jackc/pgx/v4” which provides also a toolkit or library to interact with the database, but in this case we will it only for getting the driver.
The below code is just one example:
package main import ( "database/sql" "fmt" _ "github.
if we check the users.go controller:
type Users struct { Templates struct { New views.Template } }
We can see that the views.Template is used. Which mean that we are locked in (forced) to use that package views.
If we replace views.Template with an interface, we could pass anything as long as it satisfies the interface. In other words, as long as it passes the methods the interface needs, then it will work.
This is how my main.go looks like:
package main import ( "fmt" "net/http" "github.com/go-chi/chi/v5" "github.com/rabocse/lenslocked/controllers" "github.com/rabocse/lenslocked/templates" "github.com/rabocse/lenslocked/views" ) func main() { r := chi.NewRouter() r.Get("/", controllers.StaticHandler(views.Must(views.ParseFS(templates.FS, "home.gohtml", "tailwind.gohtml")))) r.Get("/contact", controllers.StaticHandler(views.Must(views.ParseFS(templates.FS, "contact.gohtml", "tailwind.gohtml")))) r.Get("/faq", controllers.FAQ(views.Must(views.ParseFS(templates.FS, "faq.gohtml", "tailwind.gohtml")))) r.NotFound(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "Page Not Found", http.StatusNotFound) }) fmt.Println("Starting the server on :3000...") http.ListenAndServe(":3000", r) } Compared to the previous versions, now I am using a tailwind.gohtml instead of the layout-xxxx.gohtml files.
There are two possible approaches:
With “Dangling” HTML tags. Layout Page 1. With “Dangling” HTML tags First, the layout-parts.gohtml is created:
├── templates │ ├── contact.gohtml │ ├── faq.gohtml │ ├── fs.go │ ├── home.gohtml │ └── layout-parts.gohtml This is how the home.gohtml looks:
{{template "header"}} <h1>Welcome to my awesome site!</h1> {{template "footer"}} This is how the layout-parts.gohtml looks:
{{{define "header"}}} <html> <body> {{end}} {{define "footer"}} <p>Copyright Alex Escobar 2023</p> </body> </html> {{end}} And, the only change that main.
So far, the templates that I have been using reside in the templates directory from my localhost, which means of course, that if I shipped my binary and try to execute it in a different PC (or even the same PC but different directory), it will fail because it will not find the contact, faq and home.gohtml files. For reference:
└── section-6 ├── controllers │ └── static.go ├── go.mod ├── go.
Within template.go, we can add the following “Must” fuction:
func Must(t Template, err error) Template { if err != nil { panic(err) } return t } That will allow us to call it in main.go, like this:
package main import ( "fmt" "net/http" "path/filepath" "github.com/go-chi/chi/v5" "github.com/rabocse/lenslocked/controllers" "github.com/rabocse/lenslocked/views" ) func main() { r := chi.NewRouter() r.Get("/", controllers.StaticHandler(views.Must(views.Parse(filepath.Join("templates", "home.gohtml"))))) r.Get("/contact", controllers.StaticHandler(views.Must(views.Parse(filepath.Join("templates", "contact.gohtml"))))) r.Get("/faq", controllers.StaticHandler(views.Must(views.Parse(filepath.Join("templates", "faq.gohtml"))))) r.NotFound(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "Page Not Found", http.
Validate Template at Startup - Go We are starting to follow the MVC pattern therefore the following structure:
└── section-6 ├── controllers │ └── static.go ├── go.mod ├── go.sum ├── main.go ├── templates │ ├── contact.gohtml │ ├── faq.gohtml │ └── home.gohtml └── views └── template.go Notice the following:
The templates are within the template directory.
views directory contains template.go which have the following:
package views import ( "fmt" "html/template" "log" "net/http" ) func Parse(filepath string) (Template, error) { tpl, err := template.
Below is an example of using Go templates for a web server:
package main import ( "fmt" "log" "net/http" "path/filepath" "text/template" "github.com/go-chi/chi/v5" ) func homeHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") tplPath := filepath.Join("templates", "home.gohtml") tpl, err := template.ParseFiles(tplPath) if err != nil { log.Printf("parsing template: %v", err) http.Error(w, "There was an error parsing the template", http.StatusInternalServerError) return } err = tpl.Execute(w, nil) if err != nil { log.Printf("executing template: %v", err) http.
Given the following code:
package main import ( "fmt" "net/http" "github.com/go-chi/chi/v5" ) func homeHandler(w http.ResponseWriter, r *http.Request) { bio := `<script>alert("You have been hacked!! XD") </script>` w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprint(w, "<h1>Welcome to my site!!!! </h1> <p>Bio:"+bio+"</p>") } func contactHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprint(w, "<h1> Contact Page </h1><p> To get in touch, email me at <a href=\"mailto:example@example.test\"> exampke@example.test </a>.") } func faqHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.
A bit of Go Templates This is a file called hello.gohtml:
<h1> Hello, {{.Name}} </h1> While this is the go code:
package main import ( "html/template" "os" ) type User struct { Name string } func main() { t, err := template.ParseFiles("hello.gohtml") if err != nil { panic(err) } user := User{ Name: "Alex Rabocse", } t.Execute(os.Stdout, user) if err != nil { panic(err) } } Notes If we “go run” the previous file, this is what we will get: go run main.
Web Dev With Go - Section 3 package main import ( "fmt" "net/http" "github.com/go-chi/chi/v5" ) func homeHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprint(w, "<h1>Welcome to my site!!!! </h1>") } func contactHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprint(w, "<h1> Contact Page </h1><p> To get in touch, email me at <a href=\"mailto:example@example.test\"> exampke@example.test </a>.") } func faqHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprint(w, `<h1> FAG Page </h1><p> Why blah bla bla blah?
Web Dev With Go - Section 1 Basic Handler package main import ( "fmt" "net/http" ) func home(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "<h1>Welcome to my site! </h1>") } func main() { http.HandleFunc("/", home) fmt.Println("Starting the server on :3000...") http.ListenAndServe(":3000", nil) } Notes func home receives two arguments:
http.ResponseWriter *http.Request The first parameter is an interface while the second one is a pointer.
It seems that the first one (http.ResponseWriter) is an interface since it is the response that the server will provide, so it could potentially be “whatever”, therefore an interface.
Every now and then I have to Google for this…So here it is:
For the shell: (the manual method)
~/.bash_profile should contain export GOPATH=$HOME/go and also export PATH=$GOPATH/bin:$PATH.
In my case, I had to do that in the .zprofile file.
Contents Contents WARNING: Why Was I Doing All this? Salesforce Backlog CLI Requirements TLDR (Execution with Docker) Current State Script’s Data Flow Caveats Progress and Roadmap What Did I Learn? What Is Next? WARNING: Dear non existing reader, I am not going to bullshit you. If, for any strange reason, you checked my Github repo or my DockerHub some time ago, you will realize that I am copying and pasting most of that content here.
In some cases is more handy to pass data as enviroment variables to my program instead of using flags for such. That is why I started to look how to do this. Here is an example:
package main import ( "fmt" "os" "strings" ) func main() { // Sets the enviroment variable os.Setenv("OUTPUT", "Blablabla") // Gets the enviroment variable fmt.Println("SCRIPT-OUTPUT:", os.Getenv("OUTPUT")) // Gets the enviroment variable fmt.Println("SHELL:", os.Getenv("SHELL")) fmt.Println() for _, env := range os.
Below is the main.go file for a program that counts words or lines from Standard Input. A flag is used to specific what to count:
package main import ( "bufio" "flag" "fmt" "io" "os" ) func count(r io.Reader, countLines bool) int { scanner := bufio.NewScanner(r) if !countLines { scanner.Split(bufio.ScanWords) } wc := 0 // For every word of line scanned, add 1 to the counter for scanner.Scan() { wc++ } return wc } func main() { // Defining a boolean flag -l to count lines instead of words lines := flag.
Below is the main.go file to with a function to count words:
package main import ( "bufio" "fmt" "io" "os" ) func count(r io.Reader) int { // A scanner is used to read text from a Reader (such as files) scanner := bufio.NewScanner(r) // Define the scanner split type to words (default is split by lines) scanner.Split(bufio.ScanWords) // Defining a counter wc := 0 // For every word scanned, increment the counter for scanner.
Contents Why Am I Doing All this? What Will We Find Here? 1.GetAuthToken.go 2. GetClientBundle.go First Some Concepts: 1. What is MKE? 2. What is the Client Bundle? Current State For the Scripts How To Use the Scripts The “Fun” Part: How Did I Crappy Code it? 1. Tried With Curl 2. Used Postman to get and Idea 3. So, what was the purpose of this? 4. Does this stuff work?
Page Not Found - Go In my previous TIL post about custom routing, I did not specify any page or message once a request is asking for a resource that is not found in the server. Below is the way how it could be added:
package main import ( "fmt" "net/http" ) func homeHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Add("Fake", "my fake header") fmt.Fprint(w, "<h1> Welcome to my site </h1>") } func contactHandler(w http.
Custom Routing (With If statements and Switch) - Go Here is a basic example about how to do custom routing with if statements:
package main import ( "fmt" "net/http" ) func homeHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Add("Fake", "my fake header") fmt.Fprint(w, "<h1> Welcome to my site </h1>") } func contactHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprint(w, "<h1> Contact Page </h1><p> To get in touch, email me at <a href=\"mailto:rabocse@alexrabocse.
http.Request Type - Go This is just some additional notes while going through the Go Web Dev course:
“r” is http.Request type, then we accessed its field called “URL”, which happens to have also a field called “Path”.
package main import ( "fmt" "net/http" ) func pathHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, r.URL.Path) } func main() { http.HandleFunc("/", pathHandler) fmt.Println("Starting the server on :3000...") http.ListenAndServe(":3000", nil) } So, what this program will do is just to print the URL path that is being accessed.
Adding Another Page With Go Handlers To add another page, we added another “handler”.
Notice how each path is associated with a “handler”:
/ = homeHandler
/contact = contactHandler
Here is the source code:
package main import ( "fmt" "net/http" ) func homeHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Add("Fake", "my fake header") fmt.Fprint(w, "<h1> Welcome to my site </h1>") } func contactHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.
HTTP Headers With Go I was looking how to add HTTP headers to the HTTP responses and here it is how:
package main import ( "fmt" "net/http" ) func handlerFunc(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Add("Fake", "my fake header") w.Header().Set("Content-Type", "text/plain") // w.WriteHeader(20000) fmt.Fprint(w, "<h1> Welcome to my site </h1>") } func main() { http.HandleFunc("/", handlerFunc) fmt.Println("Starting the server on :3000...") http.ListenAndServe(":3000", nil) } The one doing the magic is the Header() method.
Web Dev With Go My notes from Jon Calhoun’s course.
I will work with Jon’s course since I believe it will help to complete some potential projects I have in mind.
Basic Web Server Here is the source code for the server:
package main import ( "fmt" "net/http" ) func handlerFunc(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "<h1> Welcome to my site </h1>") } func main() { http.HandleFunc("/", handlerFunc) fmt.Println("Starting the server on :3000.
Go - http.Get vs http.NewRequest The difference between “http.Get” and http.NewRequest is that the second one allows us to get control over the HTTP heades.
Turned out that I had no idea about http.Get, I guess it has to do with the fact that everything I have worked with requires authentication, therefore additional headers in the HTTP requests are needed.
Official Go Documentation:
https://pkg.go.dev/net/http