Membina API dengan Go, PostgreSQL, Google Cloud dan CockroachDB

Linda Hamilton
Lepaskan: 2024-10-24 07:02:30
asal
773 orang telah melayarinya

Saya membina API dengan Go dan PostgreSQL, menyediakan saluran paip CI/CD dengan Google Cloud Run, Cloud Build, Secret Manager dan Artifact Registry serta menyambungkan instance Cloud Run ke CockroachDB.

API adalah berdasarkan permainan Crisis Core: Final Fantasy VII, untuk mensimulasikan "Materia Fusion". Khalayak sasaran artikel ini adalah untuk pembangun yang hanya ingin mengetahui cara membina dan menggunakan API. Saya mempunyai satu lagi artikel di mana saya bercakap tentang semua yang saya pelajari semasa mengerjakan projek ini, perkara yang tidak berjaya dan memahami serta menterjemah peraturan gabungan materia permainan (pautan akan datang tidak lama lagi).

Pautan untuk rujukan mudah

  • Repo GitHub dan README
  • Dokumentasi dan ujian Swagger (OpenAPI)
  • Koleksi Posmen Awam
  • Sumber model domain

Objektif API

3 titik akhir — pemeriksaan kesihatan (GET), senarai semua materia (GET) dan simulasi gabungan materia (POST)

Model Domain

Materia (baik tunggal dan jamak) ialah bola kristal yang berfungsi sebagai sumber sihir. Terdapat 144 materia yang berbeza dalam permainan, dan ia secara umum diklasifikasikan kepada 4 kategori: "Sihir", "Perintah", "Sokongan" dan "Bebas". Walau bagaimanapun, untuk tujuan mengetahui peraturan pelakuran materia, lebih mudah untuk mempunyai 32 kategori dalaman berdasarkan tingkah laku gabungan mereka dan 8 gred dalam kategori tersebut (lihat rujukan) .

Materia menjadi 'Mastered' apabila ia digunakan untuk tempoh tertentu. Tempoh tidak penting di sini.

Paling penting, 2 bahan boleh dicantum untuk menghasilkan bahan baru. Peraturan yang mengawal gabungan dipengaruhi oleh:

  • Sama ada atau kedua-dua bahan dikuasai.
  • Materia yang manakah didahulukan (seperti dalam X Y tidak semestinya sama dengan Y X).
  • Kategori dalaman bahan.
  • Gred bahan.

Building an API with Go, PostgreSQL, Google Cloud and CockroachDB

Dan terdapat BANYAK pengecualian, dengan beberapa peraturan mempunyai 3 tahap logik if-else bersarang. Ini menghapuskan kemungkinan mencipta jadual ringkas dalam DB dan meneruskan 1000 peraturan ke dalamnya, atau menghasilkan Satu Formula Untuk Memerintah Mereka Semua.

Ringkasnya, kita perlukan:

  1. Materia jadual dengan nama lajur(rentetan), material_type(ENUM) (32 kategori dalaman), gred(integer), display_materia_type(ENUM) (4 kategori yang digunakan dalam permainan), perihalan(rentetan) dan id( integer) sebagai kunci utama penambahan automatik.
  2. Struktur data untuk merangkum format peraturan asas MateriaTypeA MateriaTypeB = MateriaTypeC.
  3. Kod untuk menggunakan peraturan asas dan kompleks untuk menentukan Materia keluaran dari segi kategori dan gred dalamannya.

1. Menyediakan DB PostgreSQL Tempatan

Sebaik-baiknya anda boleh memasang DB dari tapak web itu sendiri. Tetapi alat pgAdmin tidak dapat menyambung ke DB atas sebab tertentu, jadi saya menggunakan Homebrew.

Pemasangan

brew install postgresql@17
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Ini akan memasang sejumlah besar fail binari CLI untuk membantu menggunakan DB.

Pilihan: tambahkan /opt/homebrew/opt/postgresql@17/bin pada pembolehubah $PATH.

# create the DB
createdb materiafusiondb
# step into the DB to perform SQL commands
psql materiafusiondb
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Buat pengguna dan kebenaran

-- create an SQL user to be used by the Go server
CREATE USER go_client WITH PASSWORD 'xxxxxxxx';

-- The Go server doesn't ever need to add data to the DB. 
-- So let's give it just read permission.
CREATE ROLE readonly_role;
GRANT USAGE ON SCHEMA public TO readonly_role;

-- This command gives SELECT access to all future created tables. 
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_role;

-- If you want to be more strict and give access only to tables that already exist, use this:
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role;

GRANT readonly_role TO go_client;
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Buat jadual

CREATE TYPE display_materia_type AS ENUM ('Magic', 'Command', 'Support', 'Independent');

CREATE TYPE materia_type AS ENUM ('Fire', 'Ice', 'Lightning', 'Restore', 'Full Cure', 'Status Defense', 'Defense', 'Absorb Magic', 'Status Magic', 'Fire & Status', 'Ice & Status', 'Lightning & Status', 'Gravity', 'Ultimate', 'Quick Attack', 'Quick Attack & Status', 'Blade Arts', 'Blade Arts & Status', 'Fire Blade', 'Ice Blade', 'Lightning Blade', 'Absorb Blade', 'Item', 'Punch', 'SP Turbo', 'HP Up', 'AP Up', 'ATK Up', 'VIT Up', 'MAG Up', 'SPR Up', 'Dash', 'Dualcast', 'DMW', 'Libra', 'MP Up', 'Anything');

CREATE TABLE materia (
    id integer NOT NULL,
    name character varying(50) NOT NULL,
    materia_type materia_type NOT NULL,
    grade integer NOT NULL,
    display_materia_type display_materia_type,
    description text
    CONSTRAINT materia_pkey PRIMARY KEY (id)
);

-- The primary key 'id' should auto-increment by 1 for every row entry.
CREATE SEQUENCE materia_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER SEQUENCE materia_id_seq OWNED BY materia.id;

ALTER TABLE ONLY materia ALTER COLUMN id SET DEFAULT nextval('materia_id_seq'::REGCLASS);
Salin selepas log masuk
Salin selepas log masuk

Tambah data

Buat helaian Excel dengan pengepala jadual dan data, dan eksportnya sebagai fail CSV. Kemudian jalankan arahan:

COPY materia(name,materia_type,grade,display_materia_type,description) FROM
 '<path_to_csv_file>/materiadata.csv' DELIMITER ',' CSV HEADER;
Salin selepas log masuk
Salin selepas log masuk

2. Mencipta Pelayan Go

Buat kod boilerplate menggunakan autostrada.dev. Tambahkan pilihan api, postgresql, httprouter , env var config, tinted logging, git, live reload, makefile. Kami akhirnya mendapat struktur fail seperti ini:

? codebase
├─ cmd
│  └─ api
│     ├─ errors.go
│     ├─ handlers.go
│     ├─ helpers.go
│     ├─ main.go
│     ├─ middleware.go
│     └─ server.go
├─ internal
│  ├─ database --- db.go
│  ├─ env --- env.go
│  ├─ request --- json.go
│  ├─ response --- json.go
│  └─ validator
│     ├─ helpers.go
│     └─ validators.go
├─ go.mod
├─ LICENSE
├─ Makefile
├─ README.md
└─ README.html
Salin selepas log masuk
Salin selepas log masuk

fail .env

Penjana boilerplate telah mencipta kod untuk mengambil pembolehubah persekitaran dan menambahkannya pada kod, tetapi kami boleh memudahkan untuk menjejak dan mengemas kini nilai.

Buat /.env fail. Tambahkan nilai berikut:

HTTP_PORT=4444
DB_DSN=go_client:<password>@localhost:5432/materiafusiondb?sslmode=disable
API_TIMEOUT_SECONDS=5
API_CALLS_ALLOWED_PER_SECOND=1
Salin selepas log masuk
Salin selepas log masuk

Tambah pustaka godotenv:

go get github.com/joho/godotenv
Salin selepas log masuk
Salin selepas log masuk

Tambah yang berikut pada main.go:

// At the beginning of main():
err := godotenv.Load(".env") // Loads environment variables from .env file
if err != nil { // This will be true in prod, but that's fine.
  fmt.Println("Error loading .env file")
}


// Modify config struct:
type config struct {
  baseURL string
  db      struct {
    dsn string
  }
  httpPort                 int
  apiTimeout               int
  apiCallsAllowedPerSecond float64
}

// Modify run() to use the new values from .env:
cfg.httpPort = env.GetInt("HTTP_PORT")
cfg.db.dsn = env.GetString("DB_DSN")
cfg.apiTimeout = env.GetInt("API_TIMEOUT_SECONDS")
cfg.apiCallsAllowedPerSecond = float64(env.GetInt("API_CALLS_ALLOWED_PER_SECOND"))

// cfg.baseURL = env.GetString("BASE_URL") - not required
Salin selepas log masuk
Salin selepas log masuk

Middleware dan Laluan

Boilerplate sudah mempunyai middleware untuk pulih daripada panik. Kami akan menambah 3 lagi: Pemeriksaan Jenis Kandungan, pengehadan kadar dan perlindungan tamat masa API.

Tambah perpustakaan pondok tol:

go get github.com/didip/tollbooth
Salin selepas log masuk
Salin selepas log masuk

Kemas kini

func (app *application) contentTypeCheck(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  if r.Header.Get("Content-Type") != "application/json" {
   app.unsupportedMediaType(w, r)

   return
  }
  next.ServeHTTP(w, r)
 })
}


func (app *application) rateLimiter(next http.Handler) http.Handler {
 limiter := tollbooth.NewLimiter(app.config.apiCallsAllowedPerSecond, nil)
 limiter.SetIPLookups([]string{"X-Real-IP", "X-Forwarded-For", "RemoteAddr"})

 return tollbooth.LimitHandler(limiter, next)
}


func (app *application) apiTimeout(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  timeoutDuration := time.Duration(app.config.apiTimeout) * time.Second

  ctx, cancel := context.WithTimeout(r.Context(), timeoutDuration)
  defer cancel()

  r = r.WithContext(ctx)

  done := make(chan struct{})

  go func() {
   next.ServeHTTP(w, r)
   close(done)
  }()

  select {
  case <-done:
   return
  case <-ctx.Done():
   app.gatewayTimeout(w, r)
   return
  }
 })
}
Salin selepas log masuk
Salin selepas log masuk

Perisian tengah perlu ditambahkan pada laluan. Ia boleh ditambah sama ada pada semua laluan, atau pada laluan tertentu. Dalam kes kami, semakan Jenis Kandungan (iaitu, mewajibkan pengepala input untuk memasukkan Jenis Kandungan: application/json) hanya diperlukan untuk permintaan POST. Jadi ubah suai route.go seperti berikut:

func (app *application) routes() http.Handler {
 mux := httprouter.New()

 mux.NotFound = http.HandlerFunc(app.notFound)
 mux.MethodNotAllowed = http.HandlerFunc(app.methodNotAllowed)

 // Serve the Swagger UI. Uncomment this line later
 // mux.Handler("GET", "/docs/*any", httpSwagger.WrapHandler)

 mux.HandlerFunc("GET", "/status", app.status)
 mux.HandlerFunc("GET", "/materia", app.getAllMateria)

 // Adding content-type check middleware to only the POST method
 mux.Handler("POST", "/fusion", app.contentTypeCheck(http.HandlerFunc(app.fuseMateria)))

 return app.chainMiddlewares(mux)
}

func (app *application) chainMiddlewares(next http.Handler) http.Handler {
 middlewares := []func(http.Handler) http.Handler{
  app.recoverPanic,
  app.apiTimeout,
  app.rateLimiter,
 }

 for _, middleware := range middlewares {
  next = middleware(next)
 }

 return next
}

Salin selepas log masuk

Ralat pengendalian

Tambah kaedah berikut pada /api/errors.go untuk membantu fungsi middleware:

func (app *application) unsupportedMediaType(w http.ResponseWriter, r *http.Request) {
 message := fmt.Sprintf("The %s Content-Type is not supported", r.Header.Get("Content-Type"))
 app.errorMessage(w, r, http.StatusUnsupportedMediaType, message, nil)
}

func (app *application) gatewayTimeout(w http.ResponseWriter, r *http.Request) {
 message := "Request timed out"
 app.errorMessage(w, r, http.StatusGatewayTimeout, message, nil)
}
Salin selepas log masuk

Fail struktur Permintaan dan Respons

/api/dtos.go :

package main

// MateriaDTO provides Materia details - Name, Description and Type (Magic / Command / Support / Independent)
type MateriaDTO struct {
 Name        string `json:"name" example:"Thunder"`
 Type        string `json:"type" example:"Magic"`
 Description string `json:"description" example:"Shoots lightning forward dealing thunder damage."`
}

// StatusDTO provides status of the server
type StatusDTO struct {
 Status string `json:"Status" example:"OK"`
}

// ErrorResponseDTO provides Error message
type ErrorResponseDTO struct {
 Error string `json:"Error" example:"The server encountered a problem and could not process your request"`
}
Salin selepas log masuk

/api/requests.go :

brew install postgresql@17
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Pengesah, daripada kod yang dijana, akan digunakan kemudian untuk mengesahkan medan input untuk titik akhir gabungan.

Struktur Data untuk peraturan gabungan

Buat fail /internal/crisis-core-materia-fusion/constants.go

Tambah yang berikut:

# create the DB
createdb materiafusiondb
# step into the DB to perform SQL commands
psql materiafusiondb
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

senarai penuh 32 Jenis Materia boleh didapati di sini.

Buat fail /internal/crisis-core-materia-fusion/models.go

Tambah yang berikut:

-- create an SQL user to be used by the Go server
CREATE USER go_client WITH PASSWORD 'xxxxxxxx';

-- The Go server doesn't ever need to add data to the DB. 
-- So let's give it just read permission.
CREATE ROLE readonly_role;
GRANT USAGE ON SCHEMA public TO readonly_role;

-- This command gives SELECT access to all future created tables. 
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_role;

-- If you want to be more strict and give access only to tables that already exist, use this:
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role;

GRANT readonly_role TO go_client;
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

senarai penuh peraturan boleh didapati di sini.

Pengendali untuk bahan dalam api/handlers.go

CREATE TYPE display_materia_type AS ENUM ('Magic', 'Command', 'Support', 'Independent');

CREATE TYPE materia_type AS ENUM ('Fire', 'Ice', 'Lightning', 'Restore', 'Full Cure', 'Status Defense', 'Defense', 'Absorb Magic', 'Status Magic', 'Fire & Status', 'Ice & Status', 'Lightning & Status', 'Gravity', 'Ultimate', 'Quick Attack', 'Quick Attack & Status', 'Blade Arts', 'Blade Arts & Status', 'Fire Blade', 'Ice Blade', 'Lightning Blade', 'Absorb Blade', 'Item', 'Punch', 'SP Turbo', 'HP Up', 'AP Up', 'ATK Up', 'VIT Up', 'MAG Up', 'SPR Up', 'Dash', 'Dualcast', 'DMW', 'Libra', 'MP Up', 'Anything');

CREATE TABLE materia (
    id integer NOT NULL,
    name character varying(50) NOT NULL,
    materia_type materia_type NOT NULL,
    grade integer NOT NULL,
    display_materia_type display_materia_type,
    description text
    CONSTRAINT materia_pkey PRIMARY KEY (id)
);

-- The primary key 'id' should auto-increment by 1 for every row entry.
CREATE SEQUENCE materia_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER SEQUENCE materia_id_seq OWNED BY materia.id;

ALTER TABLE ONLY materia ALTER COLUMN id SET DEFAULT nextval('materia_id_seq'::REGCLASS);
Salin selepas log masuk
Salin selepas log masuk

Cache dalam pelayan

Kami menggunakan cache dalam pelayan kerana:

  1. Data yang diambil daripada DB tidak pernah berubah.
  2. Data yang sama digunakan oleh kedua-dua titik akhir materia dan gabungan.

Kemas kini main.go:

COPY materia(name,materia_type,grade,display_materia_type,description) FROM
 '<path_to_csv_file>/materiadata.csv' DELIMITER ',' CSV HEADER;
Salin selepas log masuk
Salin selepas log masuk

Kemas kini api/helpers.go:

? codebase
├─ cmd
│  └─ api
│     ├─ errors.go
│     ├─ handlers.go
│     ├─ helpers.go
│     ├─ main.go
│     ├─ middleware.go
│     └─ server.go
├─ internal
│  ├─ database --- db.go
│  ├─ env --- env.go
│  ├─ request --- json.go
│  ├─ response --- json.go
│  └─ validator
│     ├─ helpers.go
│     └─ validators.go
├─ go.mod
├─ LICENSE
├─ Makefile
├─ README.md
└─ README.html
Salin selepas log masuk
Salin selepas log masuk

Pengendali untuk gabungan dalam api/handlers.go

HTTP_PORT=4444
DB_DSN=go_client:<password>@localhost:5432/materiafusiondb?sslmode=disable
API_TIMEOUT_SECONDS=5
API_CALLS_ALLOWED_PER_SECOND=1
Salin selepas log masuk
Salin selepas log masuk

Kod pengendali lengkap boleh didapati di sini.

Swagger UI dan dokumen definisi OpenAPI

Tambah perpustakaan Swagger:

go get github.com/joho/godotenv
Salin selepas log masuk
Salin selepas log masuk

Dalam routes.go nyahkomen baris Swagger, dan tambahkan import:

// At the beginning of main():
err := godotenv.Load(".env") // Loads environment variables from .env file
if err != nil { // This will be true in prod, but that's fine.
  fmt.Println("Error loading .env file")
}


// Modify config struct:
type config struct {
  baseURL string
  db      struct {
    dsn string
  }
  httpPort                 int
  apiTimeout               int
  apiCallsAllowedPerSecond float64
}

// Modify run() to use the new values from .env:
cfg.httpPort = env.GetInt("HTTP_PORT")
cfg.db.dsn = env.GetString("DB_DSN")
cfg.apiTimeout = env.GetInt("API_TIMEOUT_SECONDS")
cfg.apiCallsAllowedPerSecond = float64(env.GetInt("API_CALLS_ALLOWED_PER_SECOND"))

// cfg.baseURL = env.GetString("BASE_URL") - not required
Salin selepas log masuk
Salin selepas log masuk

Dalam pengendali, DTO dan fail model, tambahkan ulasan untuk dokumentasi Swagger. Rujuk ini untuk semua pilihan.

Di terminal, jalankan:

go get github.com/didip/tollbooth
Salin selepas log masuk
Salin selepas log masuk

Ini mencipta folder api/docs, dengan definisi tersedia untuk Go, JSON dan YAML.

Untuk mengujinya, mulakan pelayan setempat dan buka http://localhost:4444/docs.


Struktur folder akhir:

func (app *application) contentTypeCheck(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  if r.Header.Get("Content-Type") != "application/json" {
   app.unsupportedMediaType(w, r)

   return
  }
  next.ServeHTTP(w, r)
 })
}


func (app *application) rateLimiter(next http.Handler) http.Handler {
 limiter := tollbooth.NewLimiter(app.config.apiCallsAllowedPerSecond, nil)
 limiter.SetIPLookups([]string{"X-Real-IP", "X-Forwarded-For", "RemoteAddr"})

 return tollbooth.LimitHandler(limiter, next)
}


func (app *application) apiTimeout(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  timeoutDuration := time.Duration(app.config.apiTimeout) * time.Second

  ctx, cancel := context.WithTimeout(r.Context(), timeoutDuration)
  defer cancel()

  r = r.WithContext(ctx)

  done := make(chan struct{})

  go func() {
   next.ServeHTTP(w, r)
   close(done)
  }()

  select {
  case <-done:
   return
  case <-ctx.Done():
   app.gatewayTimeout(w, r)
   return
  }
 })
}
Salin selepas log masuk
Salin selepas log masuk

3. Menyediakan instance PostgreSQL jauh dalam CockroachDB

  1. Gunakan langkah dari sini.
  2. Selepas mencipta sijil, cipta /certs/root.crt dalam projek dan tambah sijil di sana. Kami akan membuat rujukan kepada fail ini kemudian dalam konfigurasi Google Run.
  3. AWAS! Kami TIDAK menolak folder ini ke repositori jauh. Tambahkan sijil/ folder ke .gitignore. Kami mencipta sijil dalam bahasa tempatan sahaja untuk menguji sambungan, jika anda mahu.
  4. Kini apabila anda pergi ke CockroachDB → Papan Pemuka → Menu Kiri → Pangkalan data, anda sepatutnya dapat melihat DB yang anda buat.

Penghijrahan

Dari contoh DB tempatan anda, jalankan:

brew install postgresql@17
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
  1. Pergi ke CockroachDB → Menu Kiri → Migrasi → Tambah Skema → Seret fail SQL yang baru anda dapat. Semua langkah akan dijalankan, kecuali sisipan data jadual. Ia juga akan menunjukkan kepada anda senarai langkah yang telah dilaksanakan.
  2. Pada masa menulis artikel ini, contoh PostgreSQL dalam CockroachDB tidak menyokong pernyataan seperti IMPORT INTO. Jadi saya terpaksa mencipta pernyataan INSERT dalam fail SQL tempatan untuk 270 baris (yang boleh kita perolehi daripada output pg_dump yang baru kita dapat).
  3. Log masuk ke contoh jauh, dan jalankan fail SQL.

Melog masuk ke contoh jauh:

# create the DB
createdb materiafusiondb
# step into the DB to perform SQL commands
psql materiafusiondb
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

4. Sediakan contoh Google Cloud Run

  1. Buat fail Docker seperti ini.
  2. Pergi ke Google Cloud Run dan buat projek baharu untuk API.
  3. Buat Perkhidmatan → Terapkan secara berterusan daripada repoPERSEDIAAN DENGAN CLOUD BUILDPenyedia Repositori = Github → Pilih repo anda → Jenis Binaan = Fail Docker → Simpan.
  4. Pengesahan = Benarkan seruan yang tidak dimaklumkan.
  5. Kebanyakan lalai sepatutnya baik seperti sedia ada.
  6. Tatal ke bawah ke Kontena → Pelabuhan Kontena = 4444.
  7. Pilih tab Pembolehubah dan Rahsia dan tambahkan pembolehubah persekitaran yang sama seperti yang kita ada dalam fail .env tempatan kami.

Nilai:

  1. HTTP_PORT = 4444
  2. DB_DSN = ?sslmode=verify-full&sslrootcert=/app/certs/root.crt
  3. API_TIMEOUT_SECONDS = 5
  4. PANGGILAN_API_DIBENARKAN_SEKALI = 1

Menggunakan Pengurus Rahsia Google untuk sijil

Bahagian terakhir teka-teki.

  1. Cari Pengurus Rahsia → Cipta Rahsia → Nama = ‘DB_CERT’ → Muat naik sijil .crt CockroachDB.
  2. Dalam Cloud Run → (perkhidmatan anda) → Klik Edit Penerapan Berterusan → Tatal ke bawah ke Konfigurasi → Buka Editor.
  3. Tambah ini sebagai langkah pertama:
-- create an SQL user to be used by the Go server
CREATE USER go_client WITH PASSWORD 'xxxxxxxx';

-- The Go server doesn't ever need to add data to the DB. 
-- So let's give it just read permission.
CREATE ROLE readonly_role;
GRANT USAGE ON SCHEMA public TO readonly_role;

-- This command gives SELECT access to all future created tables. 
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_role;

-- If you want to be more strict and give access only to tables that already exist, use this:
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role;

GRANT readonly_role TO go_client;
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Ini akan menjadikan Cloud Build mencipta fail certs/root.crt dalam projek kami sebelum binaan bermula, supaya Dockerfile akan mempunyai akses kepadanya walaupun kami tidak pernah menolaknya ke repositori Github kami.


Dan itu sahaja. Cuba tolak komit dan semak sama ada binaan dicetuskan. Papan pemuka Cloud Run akan menunjukkan URL pelayan Go yang dihoskan anda.


Untuk soalan yang berkaitan dengan "Mengapa anda melakukan X dan bukan Y?" baca ini.

Untuk apa-apa lagi yang anda ingin ketahui atau bincangkan, pergi ke sini atau komen di bawah.

Atas ialah kandungan terperinci Membina API dengan Go, PostgreSQL, Google Cloud dan CockroachDB. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!