Skip to content

API Integration Examples

This page provides practical examples of integrating with the Laravel Nusa API from various platforms and programming languages.

JavaScript/Frontend Integration

Vanilla JavaScript

js
class NusaAPI {
  constructor(baseUrl = '/nusa') {
    this.baseUrl = baseUrl;
  }
  
  async request(endpoint, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    const response = await fetch(url, {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        ...options.headers
      },
      ...options
    });
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    return response.json();
  }
  
  // Province methods
  async getProvinces(params = {}) {
    const query = new URLSearchParams(params);
    return this.request(`/provinces?${query}`);
  }
  
  async getProvince(code) {
    return this.request(`/provinces/${code}`);
  }
  
  // Regency methods
  async getRegencies(params = {}) {
    const query = new URLSearchParams(params);
    return this.request(`/regencies?${query}`);
  }
  
  async getRegenciesByProvince(provinceCode, params = {}) {
    const query = new URLSearchParams(params);
    return this.request(`/provinces/${provinceCode}/regencies?${query}`);
  }
  
  // District methods
  async getDistrictsByRegency(regencyCode, params = {}) {
    const query = new URLSearchParams(params);
    return this.request(`/regencies/${regencyCode}/districts?${query}`);
  }
  
  // Village methods
  async getVillagesByDistrict(districtCode, params = {}) {
    const query = new URLSearchParams(params);
    return this.request(`/districts/${districtCode}/villages?${query}`);
  }
  
  // Search methods
  async searchProvinces(query) {
    return this.getProvinces({ search: query });
  }
  
  async searchRegencies(query) {
    return this.getRegencies({ search: query });
  }
}

// Usage
const nusa = new NusaAPI();

// Get all provinces
const provinces = await nusa.getProvinces();
console.log(provinces.data);

// Search for Java provinces
const javaProvinces = await nusa.searchProvinces('jawa');
console.log(javaProvinces.data);

// Get regencies in Central Java
const regencies = await nusa.getRegenciesByProvince('33');
console.log(regencies.data);

React Hook

jsx
import { useState, useEffect } from 'react';

// Custom hook for Nusa API
function useNusaAPI() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const request = async (endpoint, options = {}) => {
    setLoading(true);
    setError(null);
    
    try {
      const response = await fetch(`/nusa${endpoint}`, {
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          ...options.headers
        },
        ...options
      });
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      const data = await response.json();
      return data;
    } catch (err) {
      setError(err.message);
      throw err;
    } finally {
      setLoading(false);
    }
  };
  
  return { request, loading, error };
}

// Address form component
function AddressForm() {
  const { request, loading, error } = useNusaAPI();
  const [provinces, setProvinces] = useState([]);
  const [regencies, setRegencies] = useState([]);
  const [districts, setDistricts] = useState([]);
  const [villages, setVillages] = useState([]);
  
  const [selectedProvince, setSelectedProvince] = useState('');
  const [selectedRegency, setSelectedRegency] = useState('');
  const [selectedDistrict, setSelectedDistrict] = useState('');
  const [selectedVillage, setSelectedVillage] = useState('');
  
  // Load provinces on mount
  useEffect(() => {
    const loadProvinces = async () => {
      try {
        const response = await request('/provinces');
        setProvinces(response.data);
      } catch (err) {
        console.error('Failed to load provinces:', err);
      }
    };
    
    loadProvinces();
  }, []);
  
  // Load regencies when province changes
  useEffect(() => {
    if (selectedProvince) {
      const loadRegencies = async () => {
        try {
          const response = await request(`/provinces/${selectedProvince}/regencies`);
          setRegencies(response.data);
          setDistricts([]);
          setVillages([]);
          setSelectedRegency('');
          setSelectedDistrict('');
          setSelectedVillage('');
        } catch (err) {
          console.error('Failed to load regencies:', err);
        }
      };
      
      loadRegencies();
    }
  }, [selectedProvince]);
  
  // Load districts when regency changes
  useEffect(() => {
    if (selectedRegency) {
      const loadDistricts = async () => {
        try {
          const response = await request(`/regencies/${selectedRegency}/districts`);
          setDistricts(response.data);
          setVillages([]);
          setSelectedDistrict('');
          setSelectedVillage('');
        } catch (err) {
          console.error('Failed to load districts:', err);
        }
      };
      
      loadDistricts();
    }
  }, [selectedRegency]);
  
  // Load villages when district changes
  useEffect(() => {
    if (selectedDistrict) {
      const loadVillages = async () => {
        try {
          const response = await request(`/districts/${selectedDistrict}/villages`);
          setVillages(response.data);
          setSelectedVillage('');
        } catch (err) {
          console.error('Failed to load villages:', err);
        }
      };
      
      loadVillages();
    }
  }, [selectedDistrict]);
  
  return (
    <form>
      {error && <div className="error">Error: {error}</div>}
      
      <div>
        <label>Province:</label>
        <select 
          value={selectedProvince} 
          onChange={(e) => setSelectedProvince(e.target.value)}
          disabled={loading}
        >
          <option value="">Select Province</option>
          {provinces.map(province => (
            <option key={province.code} value={province.code}>
              {province.name}
            </option>
          ))}
        </select>
      </div>
      
      <div>
        <label>Regency:</label>
        <select 
          value={selectedRegency} 
          onChange={(e) => setSelectedRegency(e.target.value)}
          disabled={loading || !selectedProvince}
        >
          <option value="">Select Regency</option>
          {regencies.map(regency => (
            <option key={regency.code} value={regency.code}>
              {regency.name}
            </option>
          ))}
        </select>
      </div>
      
      <div>
        <label>District:</label>
        <select 
          value={selectedDistrict} 
          onChange={(e) => setSelectedDistrict(e.target.value)}
          disabled={loading || !selectedRegency}
        >
          <option value="">Select District</option>
          {districts.map(district => (
            <option key={district.code} value={district.code}>
              {district.name}
            </option>
          ))}
        </select>
      </div>
      
      <div>
        <label>Village:</label>
        <select 
          value={selectedVillage} 
          onChange={(e) => setSelectedVillage(e.target.value)}
          disabled={loading || !selectedDistrict}
        >
          <option value="">Select Village</option>
          {villages.map(village => (
            <option key={village.code} value={village.code}>
              {village.name} {village.postal_code && `(${village.postal_code})`}
            </option>
          ))}
        </select>
      </div>
      
      {loading && <div>Loading...</div>}
    </form>
  );
}

export default AddressForm;

PHP Integration

Laravel HTTP Client

php
namespace App\Services;

use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;

class NusaAPIService
{
    private string $baseUrl;
    private int $timeout;
    private int $cacheTimeout;
    
    public function __construct()
    {
        $this->baseUrl = config('app.url') . '/nusa';
        $this->timeout = 30;
        $this->cacheTimeout = 3600; // 1 hour
    }
    
    private function request(string $endpoint, array $params = []): array
    {
        $response = Http::timeout($this->timeout)
            ->get($this->baseUrl . $endpoint, $params);
            
        if ($response->failed()) {
            throw new \Exception("API request failed: {$response->status()}");
        }
        
        return $response->json();
    }
    
    public function getProvinces(array $params = []): array
    {
        $cacheKey = 'nusa.provinces.' . md5(serialize($params));
        
        return Cache::remember($cacheKey, $this->cacheTimeout, function () use ($params) {
            return $this->request('/provinces', $params);
        });
    }
    
    public function getProvince(string $code): array
    {
        $cacheKey = "nusa.province.{$code}";
        
        return Cache::remember($cacheKey, $this->cacheTimeout, function () use ($code) {
            return $this->request("/provinces/{$code}");
        });
    }
    
    public function getRegenciesByProvince(string $provinceCode, array $params = []): array
    {
        $cacheKey = "nusa.regencies.{$provinceCode}." . md5(serialize($params));
        
        return Cache::remember($cacheKey, $this->cacheTimeout, function () use ($provinceCode, $params) {
            return $this->request("/provinces/{$provinceCode}/regencies", $params);
        });
    }
    
    public function getDistrictsByRegency(string $regencyCode, array $params = []): array
    {
        $cacheKey = "nusa.districts.{$regencyCode}." . md5(serialize($params));
        
        return Cache::remember($cacheKey, $this->cacheTimeout, function () use ($regencyCode, $params) {
            return $this->request("/regencies/{$regencyCode}/districts", $params);
        });
    }
    
    public function getVillagesByDistrict(string $districtCode, array $params = []): array
    {
        $cacheKey = "nusa.villages.{$districtCode}." . md5(serialize($params));
        
        return Cache::remember($cacheKey, $this->cacheTimeout, function () use ($districtCode, $params) {
            return $this->request("/districts/{$districtCode}/villages", $params);
        });
    }
    
    public function searchProvinces(string $query): array
    {
        return $this->getProvinces(['search' => $query]);
    }
    
    public function searchRegencies(string $query): array
    {
        return $this->request('/regencies', ['search' => $query]);
    }
    
    public function getFullAddressHierarchy(string $villageCode): array
    {
        $village = $this->request("/villages/{$villageCode}");
        $villageData = $village['data'];
        
        $district = $this->request("/districts/{$villageData['district_code']}");
        $regency = $this->request("/regencies/{$villageData['regency_code']}");
        $province = $this->request("/provinces/{$villageData['province_code']}");
        
        return [
            'village' => $villageData,
            'district' => $district['data'],
            'regency' => $regency['data'],
            'province' => $province['data'],
            'full_address' => implode(', ', [
                $villageData['name'],
                $district['data']['name'],
                $regency['data']['name'],
                $province['data']['name'],
                $villageData['postal_code'] ?? ''
            ])
        ];
    }
}

// Usage in controller
class AddressController extends Controller
{
    private NusaAPIService $nusaAPI;
    
    public function __construct(NusaAPIService $nusaAPI)
    {
        $this->nusaAPI = $nusaAPI;
    }
    
    public function getProvinces()
    {
        try {
            $provinces = $this->nusaAPI->getProvinces();
            return response()->json($provinces['data']);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }
    
    public function getRegencies(Request $request)
    {
        $request->validate([
            'province_code' => 'required|string|size:2'
        ]);
        
        try {
            $regencies = $this->nusaAPI->getRegenciesByProvince(
                $request->province_code
            );
            return response()->json($regencies['data']);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }
}

Python Integration

Using Requests Library

python
import requests
from typing import Dict, List, Optional
import time

class NusaAPI:
    def __init__(self, base_url: str = "https://your-app.com/nusa", timeout: int = 30):
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout
        self.session = requests.Session()
        self.session.headers.update({
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        })
        
    def _request(self, endpoint: str, params: Optional[Dict] = None) -> Dict:
        """Make a request to the API"""
        url = f"{self.base_url}{endpoint}"
        
        try:
            response = self.session.get(url, params=params, timeout=self.timeout)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f"API request failed: {e}")
    
    def get_provinces(self, params: Optional[Dict] = None) -> Dict:
        """Get all provinces"""
        return self._request('/provinces', params)
    
    def get_province(self, code: str) -> Dict:
        """Get specific province by code"""
        return self._request(f'/provinces/{code}')
    
    def get_regencies_by_province(self, province_code: str, params: Optional[Dict] = None) -> Dict:
        """Get regencies in a province"""
        return self._request(f'/provinces/{province_code}/regencies', params)
    
    def get_districts_by_regency(self, regency_code: str, params: Optional[Dict] = None) -> Dict:
        """Get districts in a regency"""
        return self._request(f'/regencies/{regency_code}/districts', params)
    
    def get_villages_by_district(self, district_code: str, params: Optional[Dict] = None) -> Dict:
        """Get villages in a district"""
        return self._request(f'/districts/{district_code}/villages', params)
    
    def search_provinces(self, query: str) -> Dict:
        """Search provinces by name or code"""
        return self.get_provinces({'search': query})
    
    def search_regencies(self, query: str) -> Dict:
        """Search regencies by name or code"""
        return self._request('/regencies', {'search': query})
    
    def get_full_address_hierarchy(self, village_code: str) -> Dict:
        """Get complete address hierarchy for a village"""
        village = self._request(f'/villages/{village_code}')
        village_data = village['data']
        
        district = self._request(f"/districts/{village_data['district_code']}")
        regency = self._request(f"/regencies/{village_data['regency_code']}")
        province = self._request(f"/provinces/{village_data['province_code']}")
        
        return {
            'village': village_data,
            'district': district['data'],
            'regency': regency['data'],
            'province': province['data'],
            'full_address': ', '.join(filter(None, [
                village_data['name'],
                district['data']['name'],
                regency['data']['name'],
                province['data']['name'],
                village_data.get('postal_code', '')
            ]))
        }

# Usage example
def main():
    nusa = NusaAPI()
    
    # Get all provinces
    provinces = nusa.get_provinces()
    print(f"Found {len(provinces['data'])} provinces")
    
    # Search for Java provinces
    java_provinces = nusa.search_provinces("jawa")
    for province in java_provinces['data']:
        print(f"- {province['name']} ({province['code']})")
    
    # Get regencies in Central Java
    regencies = nusa.get_regencies_by_province("33")
    print(f"Central Java has {len(regencies['data'])} regencies")
    
    # Get full address hierarchy
    address = nusa.get_full_address_hierarchy("3375011002")
    print(f"Full address: {address['full_address']}")

if __name__ == "__main__":
    main()

Django Integration

python
# services.py
from django.conf import settings
from django.core.cache import cache
import requests

class NusaAPIService:
    def __init__(self):
        self.base_url = getattr(settings, 'NUSA_API_URL', 'https://your-app.com/nusa')
        self.timeout = getattr(settings, 'NUSA_API_TIMEOUT', 30)
        self.cache_timeout = getattr(settings, 'NUSA_CACHE_TIMEOUT', 3600)
    
    def _get_cached_or_fetch(self, cache_key: str, url: str, params=None):
        """Get from cache or fetch from API"""
        cached_data = cache.get(cache_key)
        if cached_data:
            return cached_data
        
        response = requests.get(url, params=params, timeout=self.timeout)
        response.raise_for_status()
        data = response.json()
        
        cache.set(cache_key, data, self.cache_timeout)
        return data
    
    def get_provinces(self):
        cache_key = 'nusa_provinces'
        url = f'{self.base_url}/provinces'
        return self._get_cached_or_fetch(cache_key, url)
    
    def get_regencies_by_province(self, province_code: str):
        cache_key = f'nusa_regencies_{province_code}'
        url = f'{self.base_url}/provinces/{province_code}/regencies'
        return self._get_cached_or_fetch(cache_key, url)

# views.py
from django.http import JsonResponse
from django.views import View
from .services import NusaAPIService

class ProvinceListView(View):
    def get(self, request):
        try:
            nusa_service = NusaAPIService()
            provinces = nusa_service.get_provinces()
            return JsonResponse(provinces['data'], safe=False)
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=500)

class RegencyListView(View):
    def get(self, request, province_code):
        try:
            nusa_service = NusaAPIService()
            regencies = nusa_service.get_regencies_by_province(province_code)
            return JsonResponse(regencies['data'], safe=False)
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=500)

Mobile App Integration

Flutter/Dart

dart
import 'dart:convert';
import 'package:http/http.dart' as http;

class NusaAPI {
  final String baseUrl;
  final Duration timeout;
  
  NusaAPI({
    this.baseUrl = 'https://your-app.com/nusa',
    this.timeout = const Duration(seconds: 30),
  });
  
  Future<Map<String, dynamic>> _request(String endpoint, [Map<String, String>? params]) async {
    final uri = Uri.parse('$baseUrl$endpoint').replace(queryParameters: params);
    
    try {
      final response = await http.get(
        uri,
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
        },
      ).timeout(timeout);
      
      if (response.statusCode == 200) {
        return json.decode(response.body);
      } else {
        throw Exception('HTTP ${response.statusCode}: ${response.body}');
      }
    } catch (e) {
      throw Exception('API request failed: $e');
    }
  }
  
  Future<List<dynamic>> getProvinces({String? search}) async {
    final params = search != null ? {'search': search} : null;
    final response = await _request('/provinces', params);
    return response['data'];
  }
  
  Future<Map<String, dynamic>> getProvince(String code) async {
    final response = await _request('/provinces/$code');
    return response['data'];
  }
  
  Future<List<dynamic>> getRegenciesByProvince(String provinceCode) async {
    final response = await _request('/provinces/$provinceCode/regencies');
    return response['data'];
  }
  
  Future<List<dynamic>> getDistrictsByRegency(String regencyCode) async {
    final response = await _request('/regencies/$regencyCode/districts');
    return response['data'];
  }
  
  Future<List<dynamic>> getVillagesByDistrict(String districtCode) async {
    final response = await _request('/districts/$districtCode/villages');
    return response['data'];
  }
}

// Usage in Flutter widget
class AddressFormWidget extends StatefulWidget {
  @override
  _AddressFormWidgetState createState() => _AddressFormWidgetState();
}

class _AddressFormWidgetState extends State<AddressFormWidget> {
  final NusaAPI _nusaAPI = NusaAPI();
  
  List<dynamic> provinces = [];
  List<dynamic> regencies = [];
  List<dynamic> districts = [];
  List<dynamic> villages = [];
  
  String? selectedProvince;
  String? selectedRegency;
  String? selectedDistrict;
  String? selectedVillage;
  
  bool isLoading = false;
  
  @override
  void initState() {
    super.initState();
    _loadProvinces();
  }
  
  Future<void> _loadProvinces() async {
    setState(() => isLoading = true);
    try {
      final data = await _nusaAPI.getProvinces();
      setState(() => provinces = data);
    } catch (e) {
      print('Error loading provinces: $e');
    } finally {
      setState(() => isLoading = false);
    }
  }
  
  Future<void> _loadRegencies(String provinceCode) async {
    setState(() => isLoading = true);
    try {
      final data = await _nusaAPI.getRegenciesByProvince(provinceCode);
      setState(() {
        regencies = data;
        districts = [];
        villages = [];
        selectedRegency = null;
        selectedDistrict = null;
        selectedVillage = null;
      });
    } catch (e) {
      print('Error loading regencies: $e');
    } finally {
      setState(() => isLoading = false);
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        DropdownButtonFormField<String>(
          value: selectedProvince,
          decoration: InputDecoration(labelText: 'Province'),
          items: provinces.map<DropdownMenuItem<String>>((province) {
            return DropdownMenuItem<String>(
              value: province['code'],
              child: Text(province['name']),
            );
          }).toList(),
          onChanged: (value) {
            setState(() => selectedProvince = value);
            if (value != null) _loadRegencies(value);
          },
        ),
        // Add similar dropdowns for regencies, districts, villages
        if (isLoading) CircularProgressIndicator(),
      ],
    );
  }
}

These examples demonstrate how to integrate with the Laravel Nusa API from various platforms and programming languages, providing a solid foundation for building address forms and location-based features in your applications.

Released under the MIT License.