Go - Write and test a program III - API
Source: Write and test a program in Go
Write the bank API
For now, we won’t use a database to persist data, so we’ll have to create a global variable to store all the accounts in memory
Set up an account in memory
In bankapi/main.go
, we’ll create the global variable accounts
to store all the accounts in memory. We will add a first account on it.
package main
import (
"github.com/msft/bank"
)
// map of accounts
// key: number
// value: direction of the account
var accounts = map[float64]*bank.Account{}
func main() {
accounts[1001] = &bank.Account{
Customer: bank.Customer{
Name: "John",
Address: "Los Angeles, California",
Phone: "(213) 555 0147",
},
Number: 1001,
}
}
Expose the statement method (API endpoint)
We need the net/http
package for it. We also need the HandleFunc
and ListenAndserve
method from the http
package.
In bankapi/main.go
, we’ll create the statement
method.
func statement(w http.ResponseWriter, req *http.Request) {
// get the account number from the URL
numberqs := req.URL.Query().Get("number")
if numberqs == "" {
fmt.Fprintf(w, "Account number is missing!")
return
}
if number, err := strconv.ParseFloat(numberqs, 64); err != nil {
fmt.Fprintf(w, "Invalid account number!")
} else {
account, ok := accounts[number]
if !ok {
fmt.Fprintf(w, "Account with number %v can't be found!", number)
} else {
fmt.Fprintf(w, account.Statement())
}
}
}
Let’s add the statement
method in the main()
:
func main() {
accounts[1001] = &bank.Account{
Customer: bank.Customer{
Name: "John",
Address: "Los Angeles, California",
Phone: "(213) 555 0147",
},
Number: 1001,
}
http.HandleFunc("/statement", statement) //handle GET requests like http://localhost:8000/statement?number=1001
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
For testing it, we just can to open a browser and go to http://localhost:8000/statement?number=1001
or curl http://localhost:8000/statement\?number=1001
Expose the deposit method (API endpoint)
func deposit(w http.ResponseWriter, req *http.Request) {
numberqs := req.URL.Query().Get("number")
amountqs := req.URL.Query().Get("amount")
// This a very common check... we should extract this, right?
if numberqs == "" {
fmt.Fprintf(w, "Account number is missing!")
return
}
if number, err := strconv.ParseFloat(numberqs, 64); err != nil {
fmt.Fprintf(w, "Invalid account number!")
} else if amount, err := strconv.ParseFloat(amountqs, 64); err != nil {
fmt.Fprintf(w, "Invalid amount number!")
} else {
account, ok := accounts[number]
if !ok {
fmt.Fprintf(w, "Account with number %v can't be found!", number)
} else {
err := account.Deposit(amount)
if err != nil {
fmt.Fprintf(w, "%v", err)
} else {
fmt.Fprintf(w, account.Statement())
}
}
}
}
Then, we update the main()
:
//...
http.HandleFunc("/statement", statement)
http.HandleFunc("/deposit", deposit)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
To test it: http://localhost:8000/deposit?number=1001&amount=100
in url or curl http://localhost:8000/deposit\?number=1001&amount=100
Expose the withdraw method (API endpoint)
func withdraw(w http.ResponseWriter, req *http.Request) {
numberqs := req.URL.Query().Get("number")
amountqs := req.URL.Query().Get("amount")
if numberqs == "" {
fmt.Fprintf(w, "Account number is missing!")
return
}
if number, err := strconv.ParseFloat(numberqs, 64); err != nil {
fmt.Fprintf(w, "Invalid account number!")
} else if amount, err := strconv.ParseFloat(amountqs, 64); err != nil {
fmt.Fprintf(w, "Invalid amount number!")
} else {
account, ok := accounts[number]
if !ok {
fmt.Fprintf(w, "Account with number %v can't be found!", number)
} else {
err := account.Withdraw(amount)
if err != nil {
fmt.Fprintf(w, "%v", err)
} else {
fmt.Fprintf(w, account.Statement())
}
}
}
}
Add that into the main()
method
Then, test it with http://localhost:8000/withdraw?number=1001&amount=100
or curl http://localhost:8000/withdraw\?number=1001&amount=100
Next: part IV