Respons Kosong
Metode Delete
standar harus menampilkan google.protobuf.Empty
, kecuali jika
melakukan penghapusan "lunak", dalam hal ini metode harus menampilkan
resource dengan statusnya diperbarui untuk menunjukkan bahwa penghapusan sedang berlangsung.
Untuk metode kustom, metode tersebut harus memiliki pesan XxxResponse
sendiri meskipun kosong, karena kemungkinan besar fungsionalitasnya akan berkembang seiring waktu dan perlu menampilkan data tambahan.
Merepresentasikan Rentang
Kolom yang mewakili rentang harus menggunakan interval setengah terbuka dengan konvensi
penamaan [start_xxx, end_xxx)
, seperti [start_key, end_key)
atau
[start_time, end_time)
.
Semantik interval setengah terbuka biasanya digunakan oleh library STL C++ dan library standar Java. API harus menghindari penggunaan cara lain untuk merepresentasikan
rentang, seperti (index, count)
, atau [first, last]
.
Label Resource
Dalam API berorientasi resource, skema resource ditetapkan oleh API. Agar klien dapat melampirkan sejumlah kecil metadata sederhana ke resource (misalnya, memberi tag pada resource virtual machine sebagai server database), API harus menambahkan kolom map<string, string> labels
ke definisi resource:
message Book {
string name = 1;
map<string, string> labels = 2;
}
Operasi yang Berjalan Lama
Jika metode API biasanya membutuhkan waktu lama untuk diselesaikan, metode tersebut dapat dirancang untuk menampilkan resource Operasi yang Berjalan Lama ke klien, yang dapat digunakan klien untuk melacak progres dan menerima hasilnya. Operasi menentukan antarmuka standar untuk bekerja dengan operasi yang berjalan lama. Setiap API tidak boleh menentukan antarmukanya sendiri untuk operasi yang berjalan lama guna menghindari inkonsistensi.
Resource operasi harus ditampilkan langsung sebagai pesan respons dan setiap konsekuensi langsung dari operasi harus ditampilkan dalam API. Misalnya, saat membuat resource,
resource tersebut harus muncul dalam metode LIST dan GET meskipun resource
harus menunjukkan bahwa resource tersebut belum siap digunakan. Setelah operasi selesai, kolom Operation.response
harus berisi pesan yang akan ditampilkan secara langsung, jika metode tidak berjalan lama.
Operasi dapat memberikan informasi tentang progresnya menggunakan
kolom Operation.metadata
. API harus menentukan pesan untuk metadata ini meskipun penerapan awal tidak mengisi kolom metadata
.
Mencantumkan Penomoran Halaman
Koleksi yang dapat dicantumkan harus mendukung penomoran halaman, meskipun hasilnya biasanya kecil.
Rasionale: Jika API tidak mendukung penomoran halaman dari awal, mendukungnya nanti akan sulit karena menambahkan penomoran halaman akan merusak perilaku API. Klien yang tidak menyadari bahwa API sekarang menggunakan penomoran halaman dapat salah mengasumsikan bahwa mereka menerima hasil lengkap, padahal sebenarnya mereka hanya menerima halaman pertama.
Untuk mendukung penomoran halaman (menampilkan hasil daftar di halaman) dalam metode List
, API harus:
- tentukan kolom
string
page_token
dalam pesan permintaan metodeList
. Klien menggunakan kolom ini untuk meminta halaman tertentu dari hasil daftar. - tentukan kolom
int32
page_size
dalam pesan permintaan metodeList
. Klien menggunakan kolom ini untuk menentukan jumlah hasil maksimum yang akan ditampilkan oleh server. Server dapat membatasi lebih lanjut jumlah maksimum hasil yang ditampilkan dalam satu halaman. Jikapage_size
adalah0
, server akan memutuskan jumlah hasil yang akan ditampilkan. - menentukan kolom
string
next_page_token
dalam pesan respons metodeList
. Kolom ini menunjukkan token penomoran halaman untuk mengambil halaman hasil berikutnya. Jika nilainya""
, artinya tidak ada hasil lebih lanjut untuk permintaan tersebut.
Untuk mengambil halaman hasil berikutnya, klien harus meneruskan nilai next_page_token
respons dalam panggilan metode List
berikutnya (di kolom page_token
pesan permintaan):
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse);
message ListBooksRequest {
string parent = 1;
int32 page_size = 2;
string page_token = 3;
}
message ListBooksResponse {
repeated Book books = 1;
string next_page_token = 2;
}
Saat klien meneruskan parameter kueri selain token halaman, layanan harus menggagalkan permintaan jika parameter kueri tidak konsisten dengan token halaman.
Konten token halaman harus berupa buffering protokol berenkode base64 yang aman untuk URL. Dengan begitu, konten dapat berkembang tanpa masalah kompatibilitas. Jika token halaman berisi informasi yang berpotensi sensitif, informasi tersebut harus dienkripsi. Layanan harus mencegah modifikasi token halaman agar tidak mengekspos data yang tidak diinginkan melalui salah satu metode berikut:
- memerlukan parameter kueri untuk ditentukan ulang pada permintaan tindak lanjut.
- hanya mereferensikan status sesi sisi server di token halaman.
- mengenkripsi dan menandatangani parameter kueri di token halaman, serta memvalidasi ulang dan memberikan otorisasi ulang parameter ini pada setiap panggilan.
Implementasi penomoran halaman dapat juga memberikan jumlah total
item dalam kolom int32
bernama total_size
.
Membuat Daftar Sub-Koleksi
Terkadang, API perlu mengizinkan List/Search
klien di seluruh subkoleksi. Misalnya, Library API memiliki koleksi rak,
dan setiap rak memiliki koleksi buku, dan klien ingin menelusuri
buku di semua rak. Dalam kasus tersebut, sebaiknya gunakan List
standar di subkoleksi dan tentukan ID koleksi karakter pengganti "-"
untuk koleksi induk. Untuk contoh Library API, kita dapat menggunakan permintaan REST API berikut:
GET https://library.googleapis.com/v1/shelves/-/books?filter=xxx
Mendapatkan Referensi Unik dari Sub-Koleksi
Terkadang, resource dalam subkoleksi memiliki ID yang unik dalam koleksi induknya. Dalam hal ini, sebaiknya izinkan Get
mengambil resource tersebut tanpa mengetahui koleksi induk mana yang berisi resource tersebut. Dalam kasus tersebut, sebaiknya gunakan
Get
standar pada resource dan tentukan ID koleksi karakter pengganti
"-"
untuk semua koleksi induk yang memiliki resource tersebut unik.
Misalnya, di Library API, kita dapat menggunakan permintaan REST API
berikut, jika buku tersebut unik di antara semua buku di semua rak:
GET https://library.googleapis.com/v1/shelves/-/books/{id}
Nama resource dalam respons terhadap panggilan ini harus menggunakan nama kanonis
resource, dengan ID koleksi induk yang sebenarnya, bukan "-"
,
untuk setiap koleksi induk. Misalnya, permintaan di atas harus menampilkan
resource dengan nama seperti shelves/shelf713/books/book8141
, bukan
shelves/-/books/book8141
.
Urutan Pengurutan
Jika metode API memungkinkan klien menentukan urutan pengurutan untuk hasil daftar, pesan permintaan seharusnya berisi kolom:
string order_by = ...;
Nilai string harus mengikuti sintaksis SQL: daftar kolom yang dipisahkan koma. Contoh: "foo,bar"
. Urutan penyortiran {i>default<i}-nya adalah
menaik. Untuk menentukan urutan menurun untuk suatu kolom, akhiran " desc"
harus ditambahkan ke nama kolom. Contoh: "foo desc,bar"
.
Karakter spasi yang redundan dalam sintaksis tidak signifikan.
"foo,bar desc"
dan " foo , bar desc "
setara.
Minta Validasi
Jika metode API memiliki efek samping dan perlu memvalidasi permintaan tanpa menyebabkan efek samping tersebut, pesan permintaan harus berisi kolom:
bool validate_only = ...;
Jika kolom ini disetel ke true
, server tidak boleh menjalankan efek
samping apa pun dan hanya melakukan validasi spesifik per penerapan secara konsisten
dengan permintaan penuh.
Jika validasi berhasil, google.rpc.Code.OK
harus ditampilkan dan
permintaan lengkap apa pun yang menggunakan pesan permintaan yang sama tidak boleh menampilkan
google.rpc.Code.INVALID_ARGUMENT
. Perhatikan bahwa permintaan mungkin masih gagal karena error lain seperti google.rpc.Code.ALREADY_EXISTS
atau karena kondisi race.
Minta Duplikasi
Untuk API jaringan, metode API idempoten sangat disarankan karena dapat dicoba lagi dengan aman setelah terjadi kegagalan jaringan. Namun, beberapa metode API tidak dapat dengan mudah bersifat idempoten, seperti membuat resource, dan terdapat kebutuhan untuk menghindari duplikasi yang tidak diperlukan. Untuk kasus penggunaan semacam itu, pesan permintaan harus berisi ID unik, seperti UUID, yang akan digunakan server untuk mendeteksi duplikasi dan memastikan permintaan hanya diproses satu kali.
// A unique request ID for server to detect duplicated requests.
// This field **should** be named as `request_id`.
string request_id = ...;
Jika permintaan duplikat terdeteksi, server harus menampilkan respons untuk permintaan yang sebelumnya berhasil, karena klien kemungkinan besar tidak menerima respons sebelumnya.
Nilai Default Enum
Setiap definisi enum harus dimulai dengan entri bernilai 0
, yang akan digunakan saat nilai enum tidak ditentukan secara eksplisit. API harus mendokumentasikan cara
penanganan nilai 0
.
Nilai enum 0
harus diberi nama ENUM_TYPE_UNSPECIFIED
. Jika ada
perilaku default umum, perilaku tersebut akan digunakan saat nilai enum tidak
ditentukan secara eksplisit. Jika tidak ada perilaku default umum, nilai 0
harus ditolak dengan error INVALID_ARGUMENT
saat digunakan.
enum Isolation {
// Not specified.
ISOLATION_UNSPECIFIED = 0;
// Reads from a snapshot. Collisions occur if all reads and writes cannot be
// logically serialized with concurrent transactions.
SERIALIZABLE = 1;
// Reads from a snapshot. Collisions occur if concurrent transactions write
// to the same rows.
SNAPSHOT = 2;
...
}
// When unspecified, the server will use an isolation level of SNAPSHOT or
// better.
Isolation level = 1;
Nama idiomatis dapat digunakan untuk nilai 0
. Misalnya, google.rpc.Code.OK
adalah cara idiomatis untuk menentukan
tidak adanya kode error. Dalam hal ini, OK
secara semantik
setara dengan UNSPECIFIED
dalam konteks jenis enum.
Jika terdapat default yang masuk akal dan aman secara intrinsik, nilai tersebut
dapat digunakan untuk nilai '0'. Misalnya, BASIC
adalah nilai '0' dalam
enum Resource View.
Sintaksis Tata Bahasa
Dalam desain API, sering kali perlu mendefinisikan tata bahasa sederhana untuk format data tertentu, seperti input teks yang dapat diterima. Untuk memberikan pengalaman developer yang konsisten di seluruh API dan mengurangi kurva pembelajaran, desainer API harus menggunakan varian sintaksis Extended Backus-Naur Form (EBNF) berikut untuk menentukan tata bahasa tersebut:
Production = name "=" [ Expression ] ";" ;
Expression = Alternative { "|" Alternative } ;
Alternative = Term { Term } ;
Term = name | TOKEN | Group | Option | Repetition ;
Group = "(" Expression ")" ;
Option = "[" Expression "]" ;
Repetition = "{" Expression "}" ;
Jenis Bilangan Bulat
Dalam desain API, jenis integer yang tidak ditandatangani seperti uint32
dan fixed32
tidak boleh digunakan karena beberapa bahasa dan sistem pemrograman penting
tidak mendukungnya dengan baik, seperti Java, JavaScript, dan OpenAPI. Selain itu, jenis error ini cenderung
menyebabkan error tambahan. Masalah lainnya adalah API yang berbeda sangat mungkin menggunakan jenis bertanda tangan dan tidak bertanda tangan yang tidak cocok untuk hal yang sama.
Jika jenis bilangan bulat bertanda digunakan untuk hal-hal yang nilai negatifnya tidak
bermakna, seperti ukuran atau waktu tunggu, nilai -1
(dan hanya -1
) dapat
digunakan untuk menunjukkan makna khusus, seperti akhir file (EOF), waktu tunggu
tak terbatas, batas kuota tidak terbatas, atau usia yang tidak diketahui. Penggunaan tersebut harus didokumentasikan dengan jelas
untuk menghindari kebingungan. Produsen API juga harus mendokumentasikan perilaku
nilai default implisit 0
jika tidak terlalu jelas.
Respons Sebagian
Terkadang klien API hanya memerlukan subset data tertentu dalam pesan respons. Untuk mendukung kasus penggunaan tersebut, beberapa platform API menyediakan dukungan native untuk respons parsial. Google API Platform mendukungnya melalui mask kolom respons.
Untuk setiap panggilan REST API, terdapat parameter kueri sistem implisit $fields
,
yang merupakan representasi JSON dari nilai google.protobuf.FieldMask
. Pesan respons akan difilter oleh $fields
sebelum dikirim kembali ke klien. Logika ini ditangani secara otomatis untuk semua metode API oleh Platform
API.
GET https://library.googleapis.com/v1/shelves?$fields=shelves.name
GET https://library.googleapis.com/v1/shelves/123?$fields=name
Tampilan Referensi
Untuk mengurangi traffic jaringan, terkadang ada baiknya mengizinkan klien membatasi bagian resource mana yang harus ditampilkan oleh server dalam responsnya, yang menampilkan tampilan resource, bukan representasi resource lengkap. Dukungan tampilan resource di API diimplementasikan dengan menambahkan parameter ke permintaan metode yang memungkinkan klien menentukan tampilan resource yang ingin diterima dalam respons.
Parameter:
- harus berjenis
enum
- harus diberi nama
view
Setiap nilai enumerasi menentukan bagian resource (kolom mana) yang akan ditampilkan dalam respons server. Apa yang ditampilkan untuk setiap nilai view
ditentukan oleh implementasi dan harus ditentukan dalam dokumentasi API.
package google.example.library.v1;
service Library {
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {
option (google.api.http) = {
get: "/v1/{name=shelves/*}/books"
}
};
}
enum BookView {
// Not specified, equivalent to BASIC.
BOOK_VIEW_UNSPECIFIED = 0;
// Server responses only include author, title, ISBN and unique book ID.
// The default value.
BASIC = 1;
// Full representation of the book is returned in server responses,
// including contents of the book.
FULL = 2;
}
message ListBooksRequest {
string name = 1;
// Specifies which parts of the book resource should be returned
// in the response.
BookView view = 2;
}
Konstruksi ini akan dipetakan ke URL seperti:
GET https://library.googleapis.com/v1/shelves/shelf1/books?view=BASIC
Anda dapat mengetahui lebih lanjut cara menentukan metode, permintaan, dan respons di bab Metode Standar dalam Panduan Desain ini.
ETags
ETag adalah pengenal buram yang memungkinkan klien membuat permintaan bersyarat.
Untuk mendukung ETag, API harus menyertakan kolom string etag
dalam definisi resource, dan semantiknya harus sesuai dengan penggunaan umum ETag.
Biasanya, etag
berisi sidik jari resource yang dihitung oleh
server. Lihat Wikipedia dan
RFC 7232 untuk detail selengkapnya.
ETag dapat divalidasi dengan kuat atau lemah. ETag yang divalidasi dengan lemah akan diawali dengan W/
. Dalam konteks ini, validasi kuat berarti dua
resource yang memiliki ETag yang sama memiliki konten identik byte-untuk-byte dan
kolom tambahan yang identik (yaitu, Content-Type). Ini berarti ETag yang tervalidasi dengan baik mengizinkan penyimpanan respons parsial dalam cache untuk disusun nanti.
Sebaliknya, resource yang memuat nilai ETag yang tervalidasi dengan lemah berarti bahwa representasi tersebut setara secara semantik, tetapi tidak harus identik byte-untuk-byte, sehingga tidak cocok untuk caching respons permintaan rentang byte.
Contoh:
// This is a strong ETag, including the quotes.
"1a2f3e4d5b6c7c"
// This is a weak ETag, including the prefix and quotes.
W/"1a2b3c4d5ef"
Penting untuk dipahami bahwa tanda kutip benar-benar merupakan bagian dari nilai ETag dan harus ada agar sesuai dengan RFC 7232. Ini berarti representasi JSON dari ETag akan terhapus dari tanda petik. Misalnya, ETag akan ditampilkan dalam isi resource JSON sebagai:
// Strong
{ "etag": "\"1a2f3e4d5b6c7c\"", "name": "...", ... }
// Weak
{ "etag": "W/\"1a2b3c4d5ef\"", "name": "...", ... }
Ringkasan karakter yang diizinkan dalam ETag:
- Khusus ASCII yang dapat dicetak
- Karakter non-ASCII yang diizinkan oleh RFC 7232, tetapi kurang cocok untuk developer
- Tanpa spasi
- Tidak boleh ada tanda kutip ganda selain pada posisi yang ditunjukkan di atas
- Hindari garis miring terbalik seperti yang direkomendasikan oleh RFC 7232 untuk mencegah kebingungan terkait escape
Kolom Output
API mungkin ingin membedakan antara kolom yang disediakan oleh klien sebagai input dan kolom yang hanya ditampilkan oleh server pada output pada resource tertentu. Untuk kolom yang hanya merupakan output, atribut kolom akan diberi anotasi.
Perlu diketahui bahwa jika kolom khusus output ditetapkan dalam permintaan atau disertakan dalam
google.protobuf.FieldMask
, server harus menerima permintaan tanpa
error. Server harus mengabaikan keberadaan kolom hanya output dan indikasi apa pun. Alasan adanya rekomendasi ini karena klien sering
menggunakan kembali resource yang ditampilkan server sebagai input permintaan lain, misalnya
Book
yang diambil akan digunakan kembali dalam metode UPDATE. Jika hanya kolom output yang divalidasi, tindakan ini akan menempatkan tugas tambahan pada klien untuk menghapus kolom khusus output.
import "google/api/field_behavior.proto";
message Book {
string name = 1;
Timestamp create_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
}
Resource Singleton
Resource singleton dapat digunakan jika hanya ada satu instance resource dalam resource induknya (atau dalam API, jika tidak memiliki induk).
Metode Create
dan Delete
standar harus dihilangkan untuk resource
singleton; singleton secara implisit dibuat atau dihapus saat induknya
dibuat atau dihapus (dan secara implisit ada jika tidak memiliki induk). Resource tersebut harus diakses menggunakan metode Get
dan Update
standar, serta metode khusus apa pun yang sesuai untuk kasus penggunaan Anda.
Misalnya, API dengan resource User
dapat mengekspos setelan per pengguna sebagai singleton Settings
.
rpc GetSettings(GetSettingsRequest) returns (Settings) {
option (google.api.http) = {
get: "/v1/{name=users/*/settings}"
};
}
rpc UpdateSettings(UpdateSettingsRequest) returns (Settings) {
option (google.api.http) = {
patch: "/v1/{settings.name=users/*/settings}"
body: "settings"
};
}
[...]
message Settings {
string name = 1;
// Settings fields omitted.
}
message GetSettingsRequest {
string name = 1;
}
message UpdateSettingsRequest {
Settings settings = 1;
// Field mask to support partial updates.
FieldMask update_mask = 2;
}
Streaming Setengah Sebentar
Untuk API dua arah atau streaming klien, server harus mengandalkan half-close yang dimulai oleh klien, seperti yang disediakan oleh sistem RPC, untuk menyelesaikan streaming sisi klien. Anda tidak perlu menentukan pesan penyelesaian eksplisit.
Informasi apa pun yang perlu dikirim klien sebelum penutup setengah harus ditetapkan sebagai bagian dari pesan permintaan.
Nama cakupan domain
Nama cakupan domain adalah nama entity yang diawali dengan nama domain DNS untuk mencegah konflik nama. Ini adalah pola desain yang berguna saat berbagai organisasi menetapkan nama entity mereka secara terdesentralisasi. Sintaksisnya menyerupai URI tanpa skema.
Nama cakupan domain banyak digunakan di antara Google API dan Kubernetes API, seperti:
- Representasi jenis
Any
Protobuf:type.googleapis.com/google.protobuf.Any
- Jenis metrik Stackdriver:
compute.googleapis.com/instance/cpu/utilization
- Kunci label:
cloud.googleapis.com/location
- Versi Kubernetes API:
networking.k8s.io/v1
- Kolom
kind
di ekstensi OpenAPIx-kubernetes-group-version-kind
.
Bool vs. Enum vs. String
Saat mendesain metode API, sebaiknya Anda menyediakan serangkaian pilihan
untuk fitur tertentu, seperti mengaktifkan pelacakan atau menonaktifkan penyimpanan cache. Cara
umum untuk mencapainya adalah dengan memperkenalkan kolom permintaan jenis bool
, enum
,
atau string
. Tidak selalu terlihat jelas jenis apa yang tepat digunakan untuk kasus penggunaan tertentu. Pilihan yang direkomendasikan adalah sebagai berikut:
Menggunakan jenis
bool
jika kita ingin memiliki desain tetap dan sengaja tidak ingin memperluas fungsi. Misalnya,bool enable_tracing
ataubool enable_pretty_print
.Menggunakan jenis
enum
jika kita ingin memiliki desain yang fleksibel, tetapi tidak berharap desainnya akan sering berubah. Prinsipnya adalah definisi enum hanya akan berubah setahun sekali atau lebih jarang. Misalnya,enum TlsVersion
atauenum HttpVersion
.Menggunakan jenis
string
jika kita memiliki desain terbuka atau desain dapat sering diubah oleh standar eksternal. Nilai-nilai yang didukung harus didokumentasikan dengan jelas. Contoh:string region_code
seperti yang ditentukan oleh region Unicode.string language_code
seperti yang ditetapkan oleh lokalitas Unicode.
Retensi Data
Saat mendesain layanan API, retensi data merupakan aspek penting dari keandalan layanan. Data pengguna umumnya dihapus secara keliru oleh bug software atau error manusia. Tanpa retensi data dan fungsi pembatalan penghapusan terkait, kesalahan sederhana dapat menyebabkan dampak bisnis yang fatal.
Secara umum, kami merekomendasikan kebijakan retensi data berikut untuk layanan API:
Untuk metadata pengguna, setelan pengguna, dan informasi penting lainnya, harus ada retensi data selama 30 hari. Misalnya, metrik pemantauan, metadata project, dan definisi layanan.
Untuk konten pengguna bervolume besar, harus ada retensi data selama 7 hari. Misalnya, blob biner dan tabel database.
Untuk status sementara atau penyimpanan yang mahal, harus ada retensi data 1 hari jika memungkinkan. Misalnya, instance memcache dan server Redis.
Selama jendela retensi data, data dapat dibatalkan penghapusannya tanpa kehilangan data. Jika menawarkan retensi data secara gratis itu mahal, layanan dapat menawarkan retensi data sebagai opsi berbayar.
Payload Besar
API jaringan sering kali bergantung pada beberapa lapisan jaringan untuk jalur datanya. Sebagian besar lapisan jaringan memiliki batas yang pasti untuk ukuran permintaan dan respons. 32 MB adalah batas yang umum digunakan di banyak sistem.
Ketika merancang metode API yang menangani payload yang lebih besar dari 10 MB, kita harus berhati-hati dalam memilih strategi yang tepat untuk kegunaan dan pertumbuhan di masa mendatang. Untuk Google API, sebaiknya gunakan streaming atau upload/download media untuk menangani payload yang besar. Dengan streaming, server secara bertahap menangani data besar secara sinkron, seperti Cloud Spanner API. Dengan media, data besar mengalir melalui sistem penyimpanan besar, seperti Google Cloud Storage, dan server dapat menangani data secara asinkron, seperti Google Drive API.
Kolom Primitif Opsional
Protocol Buffer v3 (proto3) mendukung kolom primitif optional
, yang secara semantik setara dengan jenis nullable
dalam banyak bahasa pemrograman.
Parameter ini dapat digunakan untuk membedakan nilai kosong dari nilai yang tidak ditetapkan.
Dalam praktiknya, developer kesulitan menangani kolom opsional dengan benar.
Sebagian besar library klien HTTP JSON, termasuk
Library Klien Google API,
tidak dapat membedakan proto3 int32
, google.protobuf.Int32Value
, dan
optional int32
. Jika desain alternatif sama jelasnya dan tidak
memerlukan primitif opsional, pilih itu. Jika tidak menggunakan opsional akan menambah kerumitan atau ambiguitas, maka gunakan kolom primitif opsional. Jenis wrapper
tidak boleh digunakan ke depannya. Secara umum, desainer API harus menggunakan
jenis primitif biasa, seperti int32
, agar lebih sederhana dan konsisten.