Finish tomorrow's WeblogPoMo post
All checks were successful
Build and copy to prod / build-and-copy (push) Successful in 2m4s
All checks were successful
Build and copy to prod / build-and-copy (push) Successful in 2m4s
This commit is contained in:
parent
351931dd4b
commit
406383e3c1
@ -96,5 +96,65 @@ And if I then force a failure, it should also work:
|
|||||||
|
|
||||||
## Adding some output
|
## Adding some output
|
||||||
|
|
||||||
Okay, now I have a database with some data in it, I'd like to expose that data
|
Right, now to actually output the values in the database. First of all, I've defined the Struct for the response:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ping/ping.go
|
||||||
|
|
||||||
|
type PingResponse struct {
|
||||||
|
Site sites.Site
|
||||||
|
Timestamp string
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And now I've added a `List` function that reads the data I need from the database, and places it into an array slice of `PingResponse` values:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func List(db *sql.DB) []PingResponse {
|
||||||
|
rows, err := db.Query(`SELECT sites.url as url, sites.name, ping.timestamp as timestamp, statuses.name as status FROM ping
|
||||||
|
JOIN sites ON ping.site = sites.url
|
||||||
|
JOIN statuses ON ping.status = statuses.id
|
||||||
|
ORDER BY timestamp DESC`)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
pings := make([]PingResponse, 0)
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
p := PingResponse{}
|
||||||
|
rows.Scan(&p.Site.Url, &p.Site.Name, &p.Timestamp, &p.Status)
|
||||||
|
pings = append(pings, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pings
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The interesting parts here are the `defer` statement, and `rows.Scan`. Defer queues that call up until after the function has executed, it's just a way of saying "I will be doing this at the end regardless" as a cleanup operation[^2]. Then `rows.Scan` will automagically insert the values to the variables I pass it, in the order the columns are read from the database[^3].
|
||||||
|
|
||||||
|
Then finally, I can update my handler function so that it uses `json.Marshal` to convert the pings to JSON, and output them to the browser:
|
||||||
|
|
||||||
|
```go
|
||||||
|
http.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
pings := ping.List(db)
|
||||||
|
|
||||||
|
if output, err := json.Marshal(pings); err != nil {
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write(output)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
And that works! You can see it at https://oopsie.lewisdale.dev, with (hopefully) some actual output.
|
||||||
|
|
||||||
[^1]: Yes, despite what I said in another post I've not written any _actual_ tests. I'm human, alright?
|
[^1]: Yes, despite what I said in another post I've not written any _actual_ tests. I'm human, alright?
|
||||||
|
[^2]: I think
|
||||||
|
[^3]: This is where it helps to be explicit with what is selected and avoid `SELECT *`.
|
Loading…
Reference in New Issue
Block a user