React Native dan Golang adalah kombinasi yang cukup menarik untuk membangun aplikasi mobile modern. React Native membantu tim membuat aplikasi Android dan iOS dengan satu basis kode, sedangkan Golang dapat digunakan untuk membangun backend yang ringan, cepat, dan mudah dijalankan di server.

Kombinasi ini bukan berarti semua aplikasi harus memakai React Native dan Go. Pemilihan teknologi tetap perlu disesuaikan dengan kebutuhan produk, kemampuan tim, integrasi, serta biaya operasional. Namun, untuk aplikasi yang membutuhkan mobile client dan API yang terstruktur, keduanya dapat bekerja dengan baik.

Artikel ini membahas dasar arsitektur, alur request, struktur project, desain REST API, autentikasi, database, validasi, error handling, testing, keamanan, serta deployment sederhana.

Gambaran arsitektur

Arsitektur paling sederhana terdiri dari tiga bagian:

React Native App
       |
       | HTTPS / JSON
       v
Golang REST API
       |
       v
Database dan layanan pendukung

React Native bertugas menampilkan antarmuka, mengelola input pengguna, menyimpan state di sisi client, dan mengirim request ke API.

Backend Golang bertugas memvalidasi request, menjalankan aturan bisnis, mengelola autentikasi, membaca atau menulis database, dan mengembalikan response yang konsisten.

Database menyimpan data aplikasi. Layanan tambahan dapat mencakup object storage, email provider, push notification, payment gateway, queue, cache, atau sistem monitoring.

Mengapa React Native?

React Native menggunakan React untuk membangun antarmuka aplikasi mobile. Developer dapat berbagi banyak kode antara Android dan iOS, terutama pada komponen tampilan, state management, validasi form, dan komunikasi API.

Beberapa kelebihan React Native:

  • Satu basis kode untuk dua platform.
  • Ekosistem JavaScript dan TypeScript yang besar.
  • Pengembangan UI relatif cepat.
  • Dukungan library untuk navigasi, form, storage, dan networking.
  • Hot reload atau fast refresh saat development.

Tetap ada bagian yang perlu ditangani secara khusus untuk Android dan iOS. Integrasi kamera, notifikasi, background task, pembayaran, Bluetooth, dan permission tertentu dapat membutuhkan konfigurasi native.

Mengapa Golang?

Golang dirancang dengan sintaks yang relatif sederhana, compiler cepat, dan dukungan concurrency bawaan melalui goroutine dan channel.

Go cocok untuk backend API karena:

  • Binary hasil build dapat dijalankan tanpa runtime besar.
  • Performa cukup baik untuk banyak jenis API.
  • Standard library menyediakan HTTP server dan tooling yang kuat.
  • Struktur project dapat dibuat sederhana.
  • Deployment ke server Linux relatif praktis.
  • Goroutine memudahkan pekerjaan concurrent yang sesuai.

Go bukan solusi otomatis untuk semua masalah performa. Query database yang buruk, desain API yang tidak efisien, penggunaan memory yang salah, atau konfigurasi server yang lemah tetap dapat membuat aplikasi lambat.

Kontrak API harus ditentukan lebih dulu

Sebelum menulis UI dan handler, tentukan kontrak antara aplikasi mobile dan backend.

Kontrak API biasanya mencakup:

  • URL endpoint.
  • HTTP method.
  • Header yang dibutuhkan.
  • Format request body.
  • Format response sukses.
  • Format response error.
  • Status code.
  • Aturan autentikasi.
  • Pagination dan filtering.
  • Versi API.

Contoh endpoint:

POST /api/v1/auth/login
GET  /api/v1/me
GET  /api/v1/products
POST /api/v1/orders
GET  /api/v1/orders/{id}

Versi /api/v1 membantu perubahan API dilakukan secara terencana. Tidak semua perubahan membutuhkan versi baru, tetapi perubahan yang mematahkan client lama sebaiknya tidak dilakukan secara diam-diam.

Contoh format response

Response sukses dapat dibuat konsisten:

{
  "data": {
    "id": "ord_123",
    "status": "pending"
  },
  "meta": {}
}

Response error:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Data yang dikirim belum valid",
    "fields": {
      "email": "Format email tidak valid"
    }
  }
}

Jangan mengirim stack trace, query database, password hash, token internal, atau detail infrastruktur ke aplikasi mobile.

Struktur project backend Golang

Struktur dapat disesuaikan dengan ukuran aplikasi. Untuk project API yang mulai berkembang, struktur berikut cukup mudah dipahami:

backend/
├── cmd/
│   └── api/
│       └── main.go
├── internal/
│   ├── auth/
│   ├── config/
│   ├── handler/
│   ├── middleware/
│   ├── model/
│   ├── repository/
│   └── service/
├── migrations/
├── pkg/
├── go.mod
└── go.sum

Pembagian tanggung jawab:

  • handler menerima HTTP request dan membentuk response.
  • service menyimpan aturan bisnis.
  • repository berkomunikasi dengan database.
  • model berisi struktur data.
  • middleware menangani autentikasi, logging, recovery, dan request ID.
  • config membaca environment variable dan konfigurasi.
  • migrations menyimpan perubahan struktur database.

Tidak perlu membuat abstraksi berlebihan sejak awal. Tambahkan lapisan ketika memang ada tanggung jawab yang perlu dipisahkan.

Membuat HTTP server sederhana di Go

Contoh minimal menggunakan standard library:

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type HealthResponse struct {
    Status string `json:"status"`
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(HealthResponse{Status: "ok"})
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("GET /health", healthHandler)

    server := &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }

    log.Println("API listening on :8080")
    log.Fatal(server.ListenAndServe())
}

Untuk aplikasi yang lebih besar, router tambahan dapat membantu routing, parameter path, middleware, dan grouping. Pilih library yang aktif dipelihara dan tetap pahami perilaku HTTP di bawahnya.

Handler, service, dan repository

Handler sebaiknya tidak memuat seluruh logika bisnis. Contoh alurnya:

HTTP request
    ↓
Handler: parse dan validasi inputService: aturan bisnisRepository: query databaseService: hasil bisnisHandler: response JSON

Contoh interface service:

type UserService interface {
    GetProfile(ctx context.Context, userID string) (User, error)
}

Contoh repository:

type UserRepository interface {
    FindByID(ctx context.Context, userID string) (User, error)
}

Interface tidak harus dibuat untuk setiap struct. Gunakan ketika membantu testing, penggantian implementasi, atau pemisahan dependency.

Database untuk backend Go

PostgreSQL sering dipilih untuk aplikasi yang membutuhkan transaksi, relasi data, constraint, dan query yang cukup kompleks.

Contoh tabel user:

CREATE TABLE users (
    id UUID PRIMARY KEY,
    email TEXT NOT NULL UNIQUE,
    password_hash TEXT NOT NULL,
    name TEXT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

Beberapa prinsip penting:

  • Gunakan migration yang dapat dilacak.
  • Tambahkan unique constraint untuk data yang memang unik.
  • Gunakan index berdasarkan query yang sering dipakai.
  • Hindari menyimpan password asli.
  • Gunakan transaksi untuk operasi yang harus atomik.
  • Jangan membuat query dengan string concatenation dari input pengguna.
  • Atur connection pool.

Contoh query parameterized:

row := db.QueryRowContext(
    ctx,
    `SELECT id, email, name FROM users WHERE email = $1`,
    email,
)

Parameter $1 membantu mencegah query dibentuk dari input mentah pengguna.

Autentikasi dan authorization

Autentikasi menjawab pertanyaan "siapa pengguna ini?" Authorization menjawab "apa yang boleh dilakukan pengguna tersebut?"

Alur login sederhana:

Mobile mengirim email dan password
           ↓
Go memvalidasi input
           ↓
Go mengambil password hash
           ↓
Go membandingkan password
           ↓
Go menerbitkan session atau token
           ↓
Mobile menyimpan credential secara aman

Password harus disimpan menggunakan algoritma hashing yang sesuai seperti Argon2id atau bcrypt. Jangan memakai hash cepat seperti SHA-256 secara langsung untuk password.

JWT

JWT sering digunakan untuk API stateless. Token berisi claim yang ditandatangani server.

Hal yang perlu diperhatikan:

  • Gunakan secret atau private key yang kuat.
  • Tetapkan expiry time.
  • Validasi issuer, audience, dan algoritma.
  • Jangan menyimpan data sensitif dalam payload.
  • Siapkan mekanisme refresh atau re-authentication.
  • Cabut akses jika akun dinonaktifkan atau credential bocor.

JWT bukan mekanisme enkripsi. Payload dapat dibaca oleh pihak yang memiliki token.

Session token

Session token yang disimpan di server dapat memberi kontrol revocation lebih baik. Server dapat menghapus atau menonaktifkan session tertentu.

Untuk aplikasi mobile, pilihan token atau session bergantung pada arsitektur, kebutuhan revocation, skala, dan integrasi layanan.

Penyimpanan credential di React Native

Jangan menyimpan access token di plain text storage jika token memberi akses ke data sensitif. Gunakan secure storage yang memanfaatkan mekanisme keamanan platform seperti Keychain pada iOS atau Keystore pada Android melalui library yang sesuai.

Contoh request dari React Native

Dengan fetch:

const response = await fetch(`${API_URL}/api/v1/products`, {
  method: "GET",
  headers: {
    Accept: "application/json",
    Authorization: `Bearer ${accessToken}`,
  },
});

if (!response.ok) {
  throw new Error("Gagal mengambil data produk");
}

const payload = await response.json();

Untuk aplikasi yang lebih besar, buat satu API client agar base URL, timeout, header, parsing error, dan refresh token tidak tersebar di seluruh komponen.

Contoh sederhana:

export async function apiFetch<T>(
  path: string,
  options: RequestInit = {},
): Promise<T> {
  const response = await fetch(`${API_URL}${path}`, {
    ...options,
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
      ...options.headers,
    },
  });

  const body = await response.json().catch(() => null);

  if (!response.ok) {
    throw new ApiError(
      body?.error?.code ?? "UNKNOWN_ERROR",
      body?.error?.message ?? "Request gagal",
      response.status,
    );
  }

  return body as T;
}

State management di React Native

State aplikasi biasanya terbagi menjadi:

  • UI state: modal terbuka atau tertutup.
  • Form state: isi dan error validasi form.
  • Server state: data yang berasal dari API.
  • Session state: user aktif dan token.
  • Device state: permission, koneksi, dan konfigurasi lokal.

Pisahkan server state dari UI state. Library seperti TanStack Query dapat membantu caching, refetching, retry, dan invalidasi data server. Untuk state global sederhana, context atau state management library dapat digunakan sesuai kebutuhan.

Jangan menganggap data di client selalu benar. Backend tetap harus memvalidasi permission, harga, status order, dan aturan bisnis penting.

Validasi di dua sisi

Validasi di React Native berguna untuk memberikan feedback cepat. Validasi di Go tetap wajib karena request dapat dikirim tanpa aplikasi mobile.

Contoh validasi yang perlu dilakukan backend:

  • Field wajib.
  • Panjang string.
  • Format email.
  • Nilai numerik.
  • Ownership resource.
  • Status transaksi.
  • Batas upload.
  • Permission pengguna.

Client validation meningkatkan pengalaman. Server validation menjaga integritas sistem.

Error handling

Error API sebaiknya memiliki kategori yang jelas:

  • 400 untuk request tidak valid.
  • 401 untuk belum terautentikasi.
  • 403 untuk tidak memiliki izin.
  • 404 untuk resource tidak ditemukan.
  • 409 untuk konflik data.
  • 422 untuk validasi bisnis tertentu.
  • 429 untuk rate limit.
  • 500 untuk error internal.
  • 503 ketika layanan sementara tidak tersedia.

Jangan mengubah semua error menjadi 500. Status code membantu client dan monitoring memahami masalah.

Di React Native, tampilkan pesan yang ramah pengguna, tetapi simpan detail teknis di log internal. Jangan menampilkan SQL error atau stack trace kepada pengguna.

Pagination dan pencarian

Endpoint list sebaiknya tidak mengirim seluruh data sekaligus.

Contoh response:

{
  "data": [],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 245,
    "total_pages": 13
  }
}

Untuk data yang terus berubah dan jumlahnya besar, cursor pagination dapat lebih stabil daripada offset pagination.

Pastikan sorting ditentukan secara eksplisit. Tanpa sorting yang konsisten, pengguna dapat melihat data berulang atau melewatkan data ketika melakukan scroll.

Upload file

Upload dari React Native ke Go perlu memperhatikan:

  • Ukuran maksimal file.
  • MIME type.
  • Ekstensi dan content signature.
  • Nama file yang aman.
  • Penyimpanan di luar web root jika perlu.
  • Virus scanning untuk kebutuhan berisiko.
  • Akses private melalui signed URL.
  • Batas timeout dan bandwidth.

Jangan mempercayai MIME type dari client tanpa validasi tambahan. Untuk file besar, object storage dengan multipart upload sering lebih cocok daripada melewatkan seluruh file melalui aplikasi API.

Push notification

Backend Go dapat memicu push notification melalui provider. Jangan menyimpan device token tanpa mengelola lifecycle-nya.

Perhatikan:

  • Satu pengguna dapat memiliki banyak perangkat.
  • Token dapat berubah atau tidak valid.
  • Notification tidak selalu sampai.
  • Payload jangan berisi data sensitif.
  • Deep link perlu divalidasi di aplikasi.
  • Event penting jangan hanya bergantung pada notification.

Database sebaiknya tetap menjadi sumber data utama. Push notification hanya memberi sinyal kepada pengguna untuk membuka aplikasi.

Keamanan API

HTTPS

Gunakan HTTPS untuk komunikasi mobile dan API. Jangan mengirim credential melalui HTTP biasa.

CORS

CORS lebih relevan untuk browser, tetapi konfigurasi API tetap perlu dirapikan jika backend juga diakses web frontend.

Rate limit

Batasi endpoint login, OTP, reset password, upload, dan endpoint mahal. Rate limit dapat diterapkan di reverse proxy, API gateway, atau aplikasi.

Request ID

Berikan request ID pada setiap request. ID yang sama dapat dicatat oleh API, database layer, dan sistem monitoring.

Secret management

Simpan secret di environment variable atau secret manager. Jangan memasukkan JWT secret, database password, API key, atau private key ke repository.

Dependency update

Perbarui dependency React Native, library native, dan module Go secara berkala. Uji perubahan di staging sebelum production.

Logging dan monitoring

Backend minimal perlu mencatat:

  • Timestamp.
  • HTTP method dan path.
  • Status code.
  • Durasi request.
  • Request ID.
  • User ID jika tersedia.
  • Error code.
  • Ukuran response.

Jangan mencatat password, access token, nomor kartu, atau data personal secara utuh.

Metrik yang berguna:

  • Request per detik.
  • Error rate.
  • Latency p50, p95, dan p99.
  • Jumlah koneksi database.
  • Penggunaan CPU dan memory.
  • Jumlah goroutine.
  • Cache hit ratio.
  • Queue depth jika menggunakan queue.

React Native juga perlu error monitoring untuk crash, native exception, dan masalah jaringan. Data yang dikirim ke monitoring harus disaring dari informasi sensitif.

Testing

Backend

Testing backend dapat mencakup:

  • Unit test untuk service.
  • Repository test dengan database test.
  • Handler test menggunakan httptest.
  • Integration test untuk alur utama.
  • Contract test untuk format API.
  • Load test untuk endpoint kritis.

Contoh handler test sederhana:

func TestHealthHandler(t *testing.T) {
    req := httptest.NewRequest(http.MethodGet, "/health", nil)
    rec := httptest.NewRecorder()

    healthHandler(rec, req)

    if rec.Code != http.StatusOK {
        t.Fatalf("expected 200, got %d", rec.Code)
    }
}

React Native

Di sisi mobile, test dapat mencakup:

  • Komponen UI.
  • Form validation.
  • API client.
  • Navigation flow.
  • Login dan logout.
  • Offline state.
  • Permission.
  • Deep link.
  • End-to-end flow.

Test bukan hanya untuk mengejar angka coverage. Prioritaskan alur yang berdampak pada pengguna dan transaksi.

Offline dan koneksi tidak stabil

Aplikasi mobile dapat kehilangan jaringan kapan saja. UI perlu membedakan:

  • Loading.
  • Offline.
  • Timeout.
  • Server error.
  • Unauthorized.
  • Data kosong.

Untuk beberapa jenis aplikasi, data tertentu dapat disimpan lokal dan disinkronkan kemudian. Sinkronisasi perlu memiliki aturan konflik yang jelas. Jangan menganggap request dapat diulang tanpa risiko duplikasi.

Untuk operasi yang dapat diulang, gunakan idempotency key. Contoh pentingnya adalah pembuatan order atau pembayaran.

Deployment backend Go

Alur deployment yang umum:

Commit code
   ↓
Test dan build
   ↓
Buat artifact atau container image
   ↓
Deploy ke staging
   ↓
Smoke test
   ↓
Approval
   ↓
Deploy production
   ↓
Monitoring dan rollback

Contoh multi-stage Dockerfile:

FROM golang:1.24 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /out/api ./cmd/api

FROM gcr.io/distroless/static-debian12
COPY --from=builder /out/api /api
USER nonroot:nonroot
ENTRYPOINT ["/api"]

Versi image harus disesuaikan dengan versi Go yang digunakan project. Jangan menjalankan container sebagai root tanpa alasan.

Deployment React Native

Deployment mobile memiliki proses berbeda untuk Android dan iOS.

Hal yang perlu dikelola:

  • Environment API URL.
  • Signing key Android.
  • Provisioning dan certificate iOS.
  • Version code dan build number.
  • Permission.
  • Crash monitoring.
  • Store metadata.
  • Release notes.
  • Rollback atau hotfix strategy.

Jangan memasukkan secret backend ke aplikasi mobile. Nilai yang tertanam di aplikasi dapat diekstrak. Aplikasi hanya boleh memiliki credential publik atau token yang memang dirancang untuk client.

Environment dan konfigurasi

Gunakan environment berbeda:

development
staging
production

Contoh konfigurasi backend:

APP_ENV=production
HTTP_ADDR=:8080
DATABASE_URL=...
JWT_ISSUER=...
LOG_LEVEL=info

Nilai rahasia tidak boleh ditulis langsung dalam artikel, repository, screenshot, atau log deployment.

Aplikasi React Native juga perlu menggunakan konfigurasi build yang berbeda untuk development dan production. Pastikan aplikasi production tidak mengarah ke database atau API staging.

Reverse proxy dan server

Go API dapat dijalankan di belakang Nginx, Caddy, load balancer, atau ingress Kubernetes.

Reverse proxy menangani sebagian kebutuhan seperti:

  • TLS termination.
  • Routing domain.
  • Compression.
  • Request size limit.
  • Access log.
  • Health check.
  • Header forwarding.

Backend harus memahami header proxy yang dipercaya. Jangan mempercayai X-Forwarded-For dari sembarang sumber tanpa konfigurasi jaringan yang benar.

Praktik yang sebaiknya dipakai

  • Definisikan kontrak API sebelum implementasi UI terlalu jauh.
  • Gunakan HTTPS sejak development yang melibatkan perangkat nyata.
  • Pisahkan handler, service, dan repository sesuai kebutuhan.
  • Buat response error yang konsisten.
  • Validasi di client dan backend.
  • Simpan password dengan password hashing yang tepat.
  • Gunakan secure storage untuk token di perangkat.
  • Gunakan migration untuk database.
  • Buat request ID dan structured logging.
  • Siapkan backup dan prosedur rollback.
  • Uji skenario offline dan timeout.
  • Pantau API dan crash aplikasi.

Kesimpulan

React Native dan Golang dapat menjadi fondasi yang baik untuk aplikasi mobile dengan backend API. React Native membantu membangun client Android dan iOS dengan basis kode yang dapat dibagi, sedangkan Go menyediakan runtime backend yang ringan dan tooling yang kuat.

Kualitas hasil akhir tidak hanya ditentukan oleh bahasa pemrograman. Desain kontrak API, keamanan autentikasi, validasi, struktur data, error handling, testing, observability, dan deployment memiliki pengaruh yang sama pentingnya.

Mulailah dari satu alur utama, misalnya login dan membaca profil. Setelah kontraknya stabil, tambahkan fitur lain secara bertahap. Dengan cara ini, masalah pada mobile, API, dan database lebih mudah dipisahkan dan diperbaiki.