“Developing a REST API with Go and Cloud Run”
Daftar Isi
Pengantar
Dua belas tahun yang lalu, Lily memulai rantai klinik hewan Teori Hewan Peliharaan. Seiring berkembangnya rantai klinik, Lily menghabiskan lebih banyak waktu di telepon dengan perusahaan asuransi daripada merawat hewan peliharaan. Andai saja perusahaan asuransi dapat melihat total perawatan secara online.
Di lab sebelumnya dalam seri ini, Ruby, konsultan komputer, dan Patrick, Insinyur DevOps, memindahkan basis data pelanggan Pet Theory ke basis data Firestore tanpa server di cloud, lalu membuka akses sehingga pelanggan dapat membuat janji temu secara online. Karena tim Ops Pet Theory adalah satu orang, mereka membutuhkan solusi tanpa server yang tidak memerlukan banyak pemeliharaan berkelanjutan.
Enable Google APIs
Developing the REST API
- Aktifkan project
gcloud config set project $(gcloud projects list --format='value(PROJECT_ID)' --filter='qwiklabs-gcp')
- Clone repository
git clone https://github.com/rosera/pet-theory.git && cd pet-theory/lab08
- Buat file main.go
nano main.go package main import ( "fmt" "log" "net/http" "os" ) func main() { port := os.Getenv("PORT") if port == "" { port = "8080" } http.HandleFunc("/v1/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "{status: 'running'}") }) log.Println("Pets REST API listening on port", port) if err := http.ListenAndServe(":"+port, nil); err != nil { log.Fatalf("Error launching Pets REST API server: %v", err) } }
- Buat file
Dockerfile
nano Dockerfile FROM gcr.io/distroless/base-debian10 WORKDIR /usr/src/app COPY server . CMD [ "/usr/src/app/server" ]
- Build binary
go build -o server
- Deploy simple rest API
gcloud builds submit \ --tag gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.1
- Cek via Navigation menu > Container Registry
- deploy
gcloud run deploy rest-api \ --image gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.1 \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --max-instances=2
- Cek via browser
Import test customer data
- Masuk ke Navigation Menu > Firestore.
- Pilih native mode
- Pilih “nam5” (United States) multi-region
- Pilih Create Database
- Buat bucket
- Import ke bucket
gsutil cp -r gs://spls/gsp645/2019-10-06T20:10:37_43617 gs://$GOOGLE_CLOUD_PROJECT-customer
- Import data ke firestore
gcloud beta firestore import gs://$GOOGLE_CLOUD_PROJECT-customer/2019-10-06T20:10:37_43617/
Connect the REST API to the Firestore database
- cek project
echo $GOOGLE_CLOUD_PROJECT
- Buka main.go . dan replace dengan code berikut -> ganti project ID dengan yang di atas
package main import ( "context" "encoding/json" "fmt" "log" "net/http" "os" "cloud.google.com/go/firestore" "github.com/gorilla/handlers" "github.com/gorilla/mux" "google.golang.org/api/iterator" ) var client *firestore.Client func main() { var err error ctx := context.Background() client, err = firestore.NewClient(ctx, "PROJECT_ID") if err != nil { log.Fatalf("Error initializing Cloud Firestore client: %v", err) } port := os.Getenv("PORT") if port == "" { port = "8080" } r := mux.NewRouter() r.HandleFunc("/v1/", rootHandler) r.HandleFunc("/v1/customer/{id}", customerHandler) log.Println("Pets REST API listening on port", port) cors := handlers.CORS( handlers.AllowedHeaders([]string{"X-Requested-With", "Authorization", "Origin"}), handlers.AllowedOrigins([]string{"https://storage.googleapis.com"}), handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "OPTIONS", "PATCH", "CONNECT"}), ) if err := http.ListenAndServe(":"+port, cors(r)); err != nil { log.Fatalf("Error launching Pets REST API server: %v", err) } }
- Tambahkan code berikut di paling bawah
func rootHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "{status: 'running'}") } func customerHandler(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] ctx := context.Background() customer, err := getCustomer(ctx, id) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"status": "fail", "data": '%s'}`, err) return } if customer == nil { w.WriteHeader(http.StatusNotFound) msg := fmt.Sprintf("`Customer \"%s\" not found`", id) fmt.Fprintf(w, fmt.Sprintf(`{"status": "fail", "data": {"title": %s}}`, msg)) return } amount, err := getAmounts(ctx, customer) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"status": "fail", "data": "Unable to fetch amounts: %s"}`, err) return } data, err := json.Marshal(amount) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"status": "fail", "data": "Unable to fetch amounts: %s"}`, err) return } fmt.Fprintf(w, fmt.Sprintf(`{"status": "success", "data": %s}`, data)) } type Customer struct { Email string `firestore:"email"` ID string `firestore:"id"` Name string `firestore:"name"` Phone string `firestore:"phone"` } func getCustomer(ctx context.Context, id string) (*Customer, error) { query := client.Collection("customers").Where("id", "==", id) iter := query.Documents(ctx) var c Customer for { doc, err := iter.Next() if err == iterator.Done { break } if err != nil { return nil, err } err = doc.DataTo(&c) if err != nil { return nil, err } } return &c, nil } func getAmounts(ctx context.Context, c *Customer) (map[string]int64, error) { if c == nil { return map[string]int64{}, fmt.Errorf("Customer should be non-nil: %v", c) } result := map[string]int64{ "proposed": 0, "approved": 0, "rejected": 0, } query := client.Collection(fmt.Sprintf("customers/%s/treatments", c.Email)) if query == nil { return map[string]int64{}, fmt.Errorf("Query is nil: %v", c) } iter := query.Documents(ctx) for { doc, err := iter.Next() if err == iterator.Done { break } if err != nil { return nil, err } treatment := doc.Data() result[treatment["status"].(string)] += treatment["cost"].(int64) } return result, nil }
Deploying a new Revision
- Rebuild code
go build -o server
- Build new image
gcloud builds submit \ --tag gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.2
- deploy update image
gcloud run deploy rest-api \ --image gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.2 \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --max-instances=2
- akses browser
Penutup
Sahabat Blog Learning & Doing demikianlah penjelasan mengenai Developing a REST API with Go and Cloud Run. Semoga Bermanfaat . Sampai ketemu lagi di postingan berikut nya.