loading...
Cover image for Building a text editor in Go: Setting up the backend

Building a text editor in Go: Setting up the backend

faraazahmad profile image Syed Faraaz Ahmad Updated on ・4 min read

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

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.

  1. A copy of an http.ResponseWriter instance, to handle send responses from the server.

  2. *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.

Nice

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:

http.ServeFile.

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))
}

I'm done

Until next time ...?

Cover image by Eduardo Higareda

Posted on by:

faraazahmad profile

Syed Faraaz Ahmad

@faraazahmad

A passionate full-stack web developer.

Discussion

markdown guide
 

I'm all for shoe-horning languages I like to do things that they aren't optimised for to create tools that already exist!

But actually, I am. And this is pretty cool.
I think it might make more sense to use one of the Go Gui libraries to do this though (speaking of things the language isn't optimised for).

Looks good so far!

 

Thank you! I totally hear you, but I want the series to be super friendly for beginners and IMO HTML is the easiest way to hack together a basic UI.

Maybe I'll create another series about GUI programming in Go πŸ˜‰

 

When will the next part be available?

 

Soon! I'll be done with my semester exams in a week so it'll be out around that time.

I'm really glad you like this series! What would you say I need to improve?

 

Nice! An informative read! You GO Syed! πŸ”₯

 

Thank you! I'll Go write the next part πŸ˜‰

 

http.ListenAndServe finally starts the server and listens for requests on the port number provided, (8081) in our case

There is a typo: it's 3000.