Kuasai panggilan API type-safe di TypeScript untuk aplikasi web yang andal, mudah dipelihara, dan bebas eror. Pelajari praktik terbaik dan teknik canggih.
Panggilan API Type-Safe dengan TypeScript: Panduan Komprehensif
Dalam pengembangan web modern, berinteraksi dengan API adalah tugas mendasar. TypeScript, dengan sistem tipenya yang kuat, menawarkan keuntungan signifikan dalam memastikan keandalan dan keterpeliharaan aplikasi Anda dengan mengaktifkan panggilan API yang type-safe. Panduan ini akan membahas cara memanfaatkan fitur-fitur TypeScript untuk membangun interaksi API yang kuat dan bebas eror, mencakup praktik terbaik, teknik canggih, dan contoh dunia nyata.
Mengapa Keamanan Tipe (Type Safety) Penting untuk Panggilan API
Saat bekerja dengan API, pada dasarnya Anda berurusan dengan data yang berasal dari sumber eksternal. Data ini mungkin tidak selalu dalam format yang Anda harapkan, yang dapat menyebabkan eror saat runtime dan perilaku yang tidak terduga. Keamanan tipe menyediakan lapisan perlindungan penting dengan memverifikasi bahwa data yang Anda terima sesuai dengan struktur yang telah ditentukan, sehingga dapat menangkap potensi masalah di awal proses pengembangan.
- Mengurangi Eror Runtime: Pemeriksaan tipe pada waktu kompilasi (compile time) membantu mengidentifikasi dan memperbaiki eror terkait tipe sebelum mencapai produksi.
- Peningkatan Keterpeliharaan Kode: Definisi tipe yang jelas membuat kode Anda lebih mudah dipahami dan dimodifikasi, mengurangi risiko munculnya bug saat refactoring.
- Peningkatan Keterbacaan Kode: Anotasi tipe menyediakan dokumentasi berharga, memudahkan pengembang lain untuk memahami struktur data yang diharapkan.
- Pengalaman Pengembang yang Lebih Baik: Dukungan IDE untuk pemeriksaan tipe dan pelengkapan otomatis (autocompletion) secara signifikan meningkatkan pengalaman pengembang dan mengurangi kemungkinan terjadinya eror.
Menyiapkan Proyek TypeScript Anda
Sebelum mendalami panggilan API, pastikan Anda telah menyiapkan proyek TypeScript. Jika Anda memulai dari awal, Anda dapat menginisialisasi proyek baru menggunakan:
npm init -y
npm install typescript --save-dev
tsc --init
Ini akan membuat file `tsconfig.json` dengan opsi kompiler TypeScript default. Anda dapat menyesuaikan opsi ini agar sesuai dengan kebutuhan proyek Anda. Misalnya, Anda mungkin ingin mengaktifkan mode ketat (strict mode) untuk pemeriksaan tipe yang lebih ketat:
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
Mendefinisikan Tipe untuk Respons API
Langkah pertama dalam mencapai panggilan API yang type-safe adalah mendefinisikan tipe TypeScript yang mewakili struktur data yang Anda harapkan akan diterima dari API. Ini biasanya dilakukan menggunakan deklarasi `interface` atau `type`.
Menggunakan Interface
Interface adalah cara yang ampuh untuk mendefinisikan bentuk sebuah objek. Misalnya, jika Anda mengambil daftar pengguna dari API, Anda mungkin mendefinisikan interface seperti ini:
interface User {
id: number;
name: string;
email: string;
address?: string; // Properti opsional
phone?: string; // Properti opsional
website?: string; // Properti opsional
company?: {
name: string;
catchPhrase: string;
bs: string;
};
}
Tanda `?` setelah nama properti menunjukkan bahwa properti tersebut bersifat opsional. Ini berguna untuk menangani respons API di mana beberapa bidang mungkin tidak ada.
Menggunakan Type
Type mirip dengan interface tetapi menawarkan lebih banyak fleksibilitas, termasuk kemampuan untuk mendefinisikan union type dan intersection type. Anda dapat mencapai hasil yang sama seperti interface di atas menggunakan type:
type User = {
id: number;
name: string;
email: string;
address?: string; // Properti opsional
phone?: string; // Properti opsional
website?: string; // Properti opsional
company?: {
name: string;
catchPhrase: string;
bs: string;
};
};
Untuk struktur objek sederhana, interface dan type seringkali dapat digunakan secara bergantian. Namun, type menjadi lebih kuat ketika berhadapan dengan skenario yang lebih kompleks.
Melakukan Panggilan API dengan Axios
Axios adalah klien HTTP populer untuk membuat permintaan API di JavaScript dan TypeScript. Ia menyediakan API yang bersih dan intuitif, membuatnya mudah untuk menangani berbagai metode HTTP, header permintaan, dan data respons.
Menginstal Axios
npm install axios
Melakukan Panggilan API dengan Tipe
Untuk membuat panggilan API yang type-safe dengan Axios, Anda dapat menggunakan metode `axios.get` dan menentukan tipe respons yang diharapkan menggunakan generic:
import axios from 'axios';
async function fetchUsers(): Promise {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
return response.data;
} catch (error) {
console.error('Eror saat mengambil data pengguna:', error);
throw error;
}
}
fetchUsers().then(users => {
users.forEach(user => {
console.log(user.name);
});
});
Dalam contoh ini, `axios.get
Menangani Metode HTTP yang Berbeda
Axios mendukung berbagai metode HTTP, termasuk `GET`, `POST`, `PUT`, `DELETE`, dan `PATCH`. Anda dapat menggunakan metode yang sesuai untuk membuat berbagai jenis permintaan API. Misalnya, untuk membuat pengguna baru, Anda bisa menggunakan metode `axios.post`:
async function createUser(user: Omit): Promise {
try {
const response = await axios.post('https://jsonplaceholder.typicode.com/users', user);
return response.data;
} catch (error) {
console.error('Eror saat membuat pengguna:', error);
throw error;
}
}
const newUser = {
name: 'John Doe',
email: 'john.doe@example.com',
address: '123 Main St',
phone: '555-1234',
website: 'example.com',
company: {
name: 'Example Corp',
catchPhrase: 'Leading the way',
bs: 'Innovative solutions'
}
};
createUser(newUser).then(user => {
console.log('Pengguna yang dibuat:', user);
});
Dalam contoh ini, `Omit
Menggunakan Fetch API
Fetch API adalah API bawaan JavaScript untuk membuat permintaan HTTP. Meskipun lebih dasar daripada Axios, ia juga dapat digunakan dengan TypeScript untuk mencapai panggilan API yang type-safe. Anda mungkin lebih memilihnya untuk menghindari penambahan dependensi jika sesuai dengan kebutuhan Anda.
Melakukan Panggilan API dengan Tipe menggunakan Fetch
Untuk membuat panggilan API yang type-safe dengan Fetch, Anda dapat menggunakan fungsi `fetch` dan kemudian mem-parsing respons sebagai JSON, dengan menentukan tipe respons yang diharapkan:
async function fetchUsers(): Promise {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error(`Eror HTTP! status: ${response.status}`);
}
const data: User[] = await response.json();
return data;
} catch (error) {
console.error('Eror saat mengambil data pengguna:', error);
throw error;
}
}
fetchUsers().then(users => {
users.forEach(user => {
console.log(user.name);
});
});
Dalam contoh ini, `const data: User[] = await response.json();` memberitahu TypeScript bahwa data respons harus diperlakukan sebagai sebuah array (larik) dari objek `User`. Ini memungkinkan TypeScript untuk melakukan pemeriksaan tipe dan pelengkapan otomatis.
Menangani Metode HTTP yang Berbeda dengan Fetch
Untuk membuat berbagai jenis permintaan API dengan Fetch, Anda dapat menggunakan fungsi `fetch` dengan opsi yang berbeda, seperti opsi `method` dan `body`. Misalnya, untuk membuat pengguna baru, Anda dapat menggunakan kode berikut:
async function createUser(user: Omit): Promise {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(user)
});
if (!response.ok) {
throw new Error(`Eror HTTP! status: ${response.status}`);
}
const data: User = await response.json();
return data;
} catch (error) {
console.error('Eror saat membuat pengguna:', error);
throw error;
}
}
const newUser = {
name: 'John Doe',
email: 'john.doe@example.com',
address: '123 Main St',
phone: '555-1234',
website: 'example.com',
company: {
name: 'Example Corp',
catchPhrase: 'Leading the way',
bs: 'Innovative solutions'
}
};
createUser(newUser).then(user => {
console.log('Pengguna yang dibuat:', user);
});
Menangani Eror API
Penanganan eror adalah aspek penting dari panggilan API. API dapat gagal karena berbagai alasan, termasuk masalah konektivitas jaringan, eror server, dan permintaan yang tidak valid. Penting untuk menangani eror ini dengan baik untuk mencegah aplikasi Anda mogok atau menampilkan perilaku yang tidak terduga.
Menggunakan Blok Try-Catch
Cara paling umum untuk menangani eror dalam kode asinkron adalah dengan menggunakan blok try-catch. Ini memungkinkan Anda untuk menangkap setiap pengecualian (exception) yang dilemparkan selama panggilan API dan menanganinya dengan tepat.
async function fetchUsers(): Promise {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
return response.data;
} catch (error) {
console.error('Eror saat mengambil data pengguna:', error);
// Tangani eror, misalnya, tampilkan pesan eror kepada pengguna
throw error; // Lempar kembali eror agar kode pemanggil juga dapat menanganinya
}
}
Menangani Kode Eror Spesifik
API sering kali mengembalikan kode eror spesifik untuk menunjukkan jenis eror yang terjadi. Anda dapat menggunakan kode eror ini untuk menyediakan penanganan eror yang lebih spesifik. Misalnya, Anda mungkin ingin menampilkan pesan eror yang berbeda untuk eror 404 Not Found daripada untuk eror 500 Internal Server Error.
async function fetchUser(id: number): Promise {
try {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
return response.data;
} catch (error: any) {
if (error.response?.status === 404) {
console.log(`Pengguna dengan ID ${id} tidak ditemukan.`);
return null; // Atau lemparkan eror kustom
} else {
console.error('Eror saat mengambil data pengguna:', error);
throw error;
}
}
}
fetchUser(123).then(user => {
if (user) {
console.log('Pengguna:', user);
} else {
console.log('Pengguna tidak ditemukan.');
}
});
Membuat Tipe Eror Kustom
Untuk skenario penanganan eror yang lebih kompleks, Anda dapat membuat tipe eror kustom untuk mewakili berbagai jenis eror API. Ini memungkinkan Anda memberikan informasi eror yang lebih terstruktur dan menangani eror dengan lebih efektif.
class ApiError extends Error {
constructor(public statusCode: number, message: string) {
super(message);
this.name = 'ApiError';
}
}
async function fetchUser(id: number): Promise {
try {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
return response.data;
} catch (error: any) {
if (error.response?.status === 404) {
throw new ApiError(404, `Pengguna dengan ID ${id} tidak ditemukan.`);
} else {
console.error('Eror saat mengambil data pengguna:', error);
throw new ApiError(500, 'Internal Server Error'); //Atau kode status lain yang sesuai
}
}
}
fetchUser(123).catch(error => {
if (error instanceof ApiError) {
console.error(`Eror API: ${error.statusCode} - ${error.message}`);
} else {
console.error('Terjadi eror yang tidak terduga:', error);
}
});
Validasi Data
Bahkan dengan sistem tipe TypeScript, sangat penting untuk memvalidasi data yang Anda terima dari API saat runtime. API dapat mengubah struktur respons mereka tanpa pemberitahuan, dan tipe TypeScript Anda mungkin tidak selalu sinkron sempurna dengan respons aktual API.
Menggunakan Zod untuk Validasi Runtime
Zod adalah pustaka TypeScript yang populer untuk validasi data saat runtime. Ini memungkinkan Anda untuk mendefinisikan skema yang menggambarkan struktur data yang diharapkan dan kemudian memvalidasi data terhadap skema tersebut saat runtime.
Menginstal Zod
npm install zod
Memvalidasi Respons API dengan Zod
Untuk memvalidasi respons API dengan Zod, Anda dapat mendefinisikan skema Zod yang sesuai dengan tipe TypeScript Anda dan kemudian menggunakan metode `parse` untuk memvalidasi data.
import { z } from 'zod';
const userSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
address: z.string().optional(),
phone: z.string().optional(),
website: z.string().optional(),
company: z.object({
name: z.string(),
catchPhrase: z.string(),
bs: z.string(),
}).optional(),
});
type User = z.infer;
async function fetchUsers(): Promise {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
const data = z.array(userSchema).parse(response.data);
return data;
} catch (error) {
console.error('Eror saat mengambil data pengguna:', error);
throw error;
}
}
Dalam contoh ini, `z.array(userSchema).parse(response.data)` memvalidasi bahwa data respons adalah sebuah array (larik) dari objek yang sesuai dengan `userSchema`. Jika data tidak sesuai dengan skema, Zod akan melemparkan eror, yang kemudian dapat Anda tangani dengan tepat.
Teknik Lanjutan
Menggunakan Generic untuk Fungsi API yang Dapat Digunakan Kembali
Generic memungkinkan Anda menulis fungsi API yang dapat digunakan kembali yang dapat menangani berbagai jenis data. Misalnya, Anda dapat membuat fungsi `fetchData` generik yang dapat mengambil data dari endpoint API mana pun dan mengembalikannya dengan tipe yang benar.
async function fetchData(url: string): Promise {
try {
const response = await axios.get(url);
return response.data;
} catch (error) {
console.error(`Eror saat mengambil data dari ${url}:`, error);
throw error;
}
}
// Penggunaan
fetchData('https://jsonplaceholder.typicode.com/users').then(users => {
console.log('Pengguna:', users);
});
fetchData<{ title: string; body: string }>('https://jsonplaceholder.typicode.com/todos/1').then(todo => {
console.log('Todo', todo)
});
Menggunakan Interceptor untuk Penanganan Eror Global
Axios menyediakan interceptor yang memungkinkan Anda untuk mencegat permintaan dan respons sebelum ditangani oleh kode Anda. Anda dapat menggunakan interceptor untuk mengimplementasikan penanganan eror global, seperti mencatat eror atau menampilkan pesan eror kepada pengguna.
axios.interceptors.response.use(
(response) => response,
(error) => {
console.error('Penangan eror global:', error);
// Tampilkan pesan eror kepada pengguna
return Promise.reject(error);
}
);
Menggunakan Variabel Lingkungan untuk URL API
Untuk menghindari penulisan URL API secara langsung (hardcoding) di kode Anda, Anda dapat menggunakan variabel lingkungan untuk menyimpan URL tersebut. Ini memudahkan konfigurasi aplikasi Anda untuk lingkungan yang berbeda, seperti pengembangan, staging, dan produksi.
Contoh menggunakan file `.env` dan paket `dotenv`.
// .env
API_URL=https://api.example.com
// Instal dotenv
npm install dotenv
// Impor dan konfigurasikan dotenv
import * as dotenv from 'dotenv'
dotenv.config()
const apiUrl = process.env.API_URL || 'http://localhost:3000'; // berikan nilai default
async function fetchData(endpoint: string): Promise {
try {
const response = await axios.get(`${apiUrl}/${endpoint}`);
return response.data;
} catch (error) {
console.error(`Eror saat mengambil data dari ${apiUrl}/${endpoint}:`, error);
throw error;
}
}
Kesimpulan
Panggilan API yang type-safe sangat penting untuk membangun aplikasi web yang kuat, mudah dipelihara, dan bebas eror. TypeScript menyediakan fitur-fitur canggih yang memungkinkan Anda untuk mendefinisikan tipe untuk respons API, memvalidasi data saat runtime, dan menangani eror dengan baik. Dengan mengikuti praktik terbaik dan teknik yang diuraikan dalam panduan ini, Anda dapat secara signifikan meningkatkan kualitas dan keandalan interaksi API Anda.
Dengan menggunakan TypeScript dan pustaka seperti Axios dan Zod, Anda dapat memastikan bahwa panggilan API Anda aman secara tipe, data Anda divalidasi, dan eror Anda ditangani dengan baik. Ini akan menghasilkan aplikasi yang lebih kuat dan mudah dipelihara.
Ingatlah untuk selalu memvalidasi data Anda saat runtime, bahkan dengan sistem tipe TypeScript. API dapat berubah, dan tipe Anda mungkin tidak selalu sinkron sempurna dengan respons aktual API. Dengan memvalidasi data Anda saat runtime, Anda dapat menangkap potensi masalah sebelum menyebabkan masalah di aplikasi Anda.
Selamat membuat kode!