Working with goroutine in a rest api – getting undefined error

Issue

I am learning GO and wanted to make a simple rest API.

What I want to do is fire off a goroutine after handling the api request and do the work asynchronously in the background.

Here’s my implementation so far:

package main

import (
    "encoding/json"
    "log"
    "net/http"

    "github.com/julienschmidt/httprouter"
)

// APIResponse represents common structure of every api call response
type APIResponse struct {
    Status string `json:"status"`
    Error  string `json:"error,omitempty"`
    Data   string `json:"data,omitempty"`
}

// Action represents what could be passed to the goroutine
type Action = func()

// ActionQueue represents a buffered channel
type ActionQueue = chan Action

func main() {
    r := httprouter.New()
    r.GET("/test", test)

    var apiServerPort = ":80"
    err := http.ListenAndServe(apiServerPort, r)
    if err != nil {
        log.Fatal("ListenAndServe:", err)
    } else {
        log.Printf("Started server on port %s", apiServerPort)
    }

    var queueSize = 10
    queue := make(ActionQueue, queueSize)
    for i := 0; i < queueSize; i++ {
        go worker(queue)
    }
    log.Printf("Started %d queue workers", queueSize)
}

func test(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
    successResponse(w, http.StatusOK, "Hello World")
    queue <- func() {
        log.Println("Hello from queue worker, initiated by api call")
    }
}

func successResponse(w http.ResponseWriter, statusCode int, successData string) {
    sendResponse(w, statusCode, "", successData)
}

func errorResponse(w http.ResponseWriter, statusCode int, errorData string) {
    sendResponse(w, statusCode, errorData, "")
}

func sendResponse(w http.ResponseWriter, statusCode int, errorData string, responseData string) {
    w.WriteHeader(statusCode)
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(&APIResponse{Status: http.StatusText(statusCode), Error: errorData, Data: responseData})
}

func worker(queue ActionQueue) {
    for action := range queue {
        action()
    }
}

When I try to run this code, I am getting the following error (on this line queue <- func() { ... }):

./main.go:46:2: undefined: queue

How can I make the queue channel available to my request handler (i.e. httprouter GET request handler func)?

Secondly, I cannot see the output from my log.Printf() calls in the console output (stdout), e.g. server status message when the app runs. Any ideas why?

Solution

Couple of things are wrong, first your main function is not ordered correctly, you want to initialize you channel and start workers before you run err := http.ListenAndServe(apiServerPort, r) so something like this

func main() {
    var queueSize = 10
    queue := make(ActionQueue, queueSize)
    for i := 0; i < queueSize; i++ {
        go worker(queue)
    }
    log.Printf("Started %d queue workers", queueSize)

    // routers and stuff...
}

Then queue variable is not defined in the test() function, which is why you are getting ./main.go:46:2: undefined: queue. You could fix it with a higher order function e.g.

func test(queue ActionQueue) httprouter.Handle {
    return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
        successResponse(w, http.StatusOK, "Hello World")
        queue <- func() {
            log.Println("Hello from queue worker, initiated by api call")
        }
    }
}

and then just bind it to the route with r.GET("/test", test(queue))

Answered By – DarkPhoton

Answer Checked By – Candace Johnson (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.