Building a text editor in Go: Setting up the backend
Syed Faraaz Ahmad
Posted on November 10, 2019
Here we will be using (the programming language) Go, if you don't have it installed, you can do so from its official website. If you're also looking to learn the language on the Go (hah!), or want to brush up some of its concepts, I suggest you take a look here.
Let's start working on that backend.
For backend, we'll be using an HTTP server built in Go. So create a file named backend.go
and add the following code to it.
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, Editor")
})
log.Fatal(http.ListenAndServe(":3000", nil))
}
Hold up, let me explain.
Routing
For those who don’t know, URL routing is the practice of taking a URL pattern and mapping it to some function that does a task when a user reaches that URL. For example, if I write
http.HandleFunc("/hello", doStuff)
and visit the '/hello' path, the function doStuff
will be executed, simple enough.
http.HandleFunc
does just that, registers a handler for the route whose pattern has been provided. It takes in 2 arguments, a string representing the URL pattern you want to route to, and a handler function that, in turn, takes 2 of its own arguments.
A copy of an
http.ResponseWriter
instance, to handle send responses from the server.*http.Request
, a pointer of the request made to the server, that contains information about the request, like URL parameters, message body, etc.
In the end, we have log.Fatal(http.ListenAndServe(":3000", nil))
. log.Fatal
is equivalent to printing and exiting the program, so any code you write after it will not be reachable. Here, try running this seperate program:
package main
import (
"fmt"
"log"
)
func main() {
log.Fatal("I use Arch btw")
fmt.Println("Yeah nobody cares")
}
You'll get an output like this:
2019/11/10 03:27:26 I use Arch btw
exit status 1
or if you're running on ideone, you'll get a runtime error. You can execute the line below by removing the log.Fatal
line as well as "log" from the import
block.
http.ListenAndServe
finally starts the server and listens for requests on the port number provided, (3000) in our case, and returns an error if there is one. In case, there is an error (or if you press Ctrl+c
to stop the server), log.Fatal
is there to stop the print the error and stop the execution. You can learn more about http.ListenAndServe
here.
Now run the server using
go run backend.go
and visit localhost:3000
in your browser. You should see a blank page with "Hello, Editor" written. Congratulations, you have succesfully built an HTTP server in Go.
Now go (bad pun count = 2) execute a mad king or something, then come back when you're done, we'll add some functionality.
Creating routes and their handlers
Since we have 2 pages — open
and edit
— we'll create 2 separate routes and handlers for these.
// backend.go
// ...
func fileOpenHandler(w http.ResponseWriter, r *http.Request) {
// do stuff
}
func fileEditHandler(w http.ResponseWriter, r *http.Request) {
// do stuff
}
func main() {
// ...
http.HandleFunc("/file/open", fileOpenHandler)
http.HandleFunc("/file/edit", fileEditHandler)
// ...
}
If we visit these parts, nothing happens because the function bodies are empty. When we open these links, we want our server to serve the HTML files we've created for these paths. Luckily there's a function for that:
Let's add that to our handlers, like so:
// backend.go
// ...
func fileOpenHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "open.html")
}
func fileEditHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "edit.html")
}
// ...
That's it, now when you open localhost:3000/file/open
or localhost:3000/file/edit
you'll see the HTML pages you created (make sure they are in the same directory as this Go file).
Also put the handler function for the "/" into its own function and call it homeHandler
. Here's the whole backend.go
file:
package main
import (
"fmt"
"log"
"net/http"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, Editor")
}
func fileOpenHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "open.html")
}
func fileEditHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "edit.html")
}
func main() {
// decare routes
http.HandleFunc("/", homeHandler)
http.HandleFunc("/file/open", fileOpenHandler)
http.HandleFunc("/file/edit", fileEditHandler)
// run server
fmt.Println("Server listening at localost:3000")
log.Fatal(http.ListenAndServe(":3000", nil))
}
Until next time ...?
Cover image by Eduardo Higareda
Posted on November 10, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.