Blog · Car Brain / Expo / Appwrite / Architektura
Twój klucz API jest prywatny. Dopóki nie wyślesz go z aplikacją.
Działający odczyt VIN umieścił klucz dostawcy po niewłaściwej stronie granicy klient–serwer. Funkcja Appwrite, jej koszty i usunięcie starej ścieżki.
Odczyt VIN działał. To była łatwiejsza część.
W Car Brain numer VIN pozwala uzupełnić dane pojazdu bez ręcznego wpisywania każdego pola. Pierwsza implementacja odpytywała auto.dev bezpośrednio z aplikacji mobilnej. Integrację napisał agent; za jej przegląd odpowiadałem ja.
Problem krył się w miejscu, z którego pobierała klucz dostawcy: EXPO_PUBLIC_AUTO_DEV_API_KEY.
W Expo używane w kodzie zmienne EXPO_PUBLIC_ trafiają do paczki JavaScript aplikacji. Usunięcie wartości z Gita nie usuwa jej z aplikacji. Poprawna odpowiedź API potwierdza, że integracja działa. Nie potwierdza, że klucz znajduje się po właściwej stronie.
Trzeba przyznać: nazwa EXPO_PUBLIC_ niczego nie ukrywa.
Z konfiguracji zrobiła się decyzja architektoniczna
Zmienne środowiskowe odpowiadają na pytanie „skąd pochodzi ta wartość?”. Nie rozstrzygają automatycznie, kto będzie mógł ją odczytać.
Publiczny adres API może być częścią konfiguracji klienta. Sekretny klucz, który pozwala wykonywać żądania na moim koncie u dostawcy, już nie. Podczas przeglądu oba wyglądają jak zwykłe ciągi znaków. Konsekwencje są zupełnie inne.
Dokumentacja Expo wprost ostrzega przed umieszczaniem sekretów w zmiennych z publicznym prefiksem. Sama zmiana nazwy też nie wystarczy: jeśli aplikacja nadal potrzebuje klucza do wywołania dostawcy, problem architektoniczny zostaje.
Tego samego dnia przeniosłem wywołanie do funkcji Appwrite. Klient wysyła VIN; funkcja pobiera klucz ze swojego środowiska po stronie serwera i odpytuje auto.dev.

Najważniejsza zmiana dotyczy granicy dostępu do klucza. Car Brain już korzystał z Appwrite, więc funkcja pozwoliła przenieść tę operację na serwer bez dokładania osobnego serwisu API.
Funkcja ma więcej do zrobienia niż schowanie klucza
Serwis mobilny wywołuje teraz API wykonywania funkcji Appwrite i interpretuje wynik. Dostępem do zewnętrznego dostawcy zajmuje się funkcja.
W kodzie sprawdzonym do tego artykułu ścieżka dekodowania najpierw sprawdza obecność tożsamości wywołującego przekazanej przez Appwrite. Waliduje też format VIN, ogranicza czas żądania do dostawcy i rozróżnia błędy, między innymi niepoprawne dane, brak danych pojazdu oraz błąd dostawcy.
Te obowiązki są ze sobą związane: kto może zlecić operację, jakie dane przyjmujemy i co robimy, kiedy zewnętrzne API zawiedzie. Klient nadal odpowiada za stan ładowania i czytelny komunikat. Przeniesienie wykonania nie przenosi całego doświadczenia użytkownika.
Ważne jest jeszcze jedno rozróżnienie: ukrycie klucza i kontrolowanie jego użycia to dwa osobne problemy. Klucz na serwerze nadal może finansować niechciane żądania, jeśli dostęp do operacji jest zbyt szeroki. Uwierzytelnianie to jedna warstwa; limity użycia, częstotliwości i wydatków wymagają osobnych decyzji. Samo przeniesienie wywołania nie dowodzi, że wszystkie te mechanizmy działają.
Funkcja wyznacza granicę. Nie jest miniaturowym działem bezpieczeństwa.
Co daje dodatkowy krok i ile kosztuje
| Decyzja | Zysk | Koszt |
|---|---|---|
| Wywołanie dostawcy w funkcji | Mobilna ścieżka VIN nie potrzebuje klucza dostawcy | Dodatkowy krok wykonania i wdrożenie funkcji |
| Walidacja i obsługa błędów na serwerze | Jedno miejsce kontroli tej integracji | Odpowiedzi błędów stają się kontraktem dla klienta |
| Wykorzystanie istniejącego Appwrite | Bez osobnego serwisu API dla tej operacji | Zależność od modelu wykonania i ograniczeń platformy |
Zimny start funkcji i opóźnienia dostawcy to możliwe koszty, a nie zmierzone tutaj pogorszenie wydajności. Nie mam benchmarku, który wykazywałby przewagę szybkości lub ceny nad innym backendem.
Wróciłbym do tej decyzji, gdyby integracje wymagały na tyle dużo wspólnej logiki lub koordynacji, że uzasadniałoby to osobny serwis. Dla pojedynczego, ograniczonego zadania oznaczałby on dziś dodatkowy obowiązek operacyjny.
Analiza danych pojazdu z Gemini również korzysta z osobnej funkcji Appwrite. To kolejne zastosowanie tej granicy, a nie powód, żeby zmienić tekst w przegląd całej architektury AI. Cache i koszty generowania odpowiedzi zasługują na własny artykuł.
Usunąć skrót, zachować lekcję
Pierwotny dekoder pozostał w kliencie jako nieużywany kod, mimo że aktywne wywołanie przeniesiono wcześniej. Został usunięty 12 października. Obecny serwis VIN w aplikacji mobilnej korzysta ze ścieżki przez funkcję.
To istotna różnica. „Dziś nikt tego nie wywołuje” daje mniej pewności niż usunięcie starej drogi, którą kolejna zmiana mogłaby przywrócić. Uzasadnienie decyzji można zachować w ADR bez zachowywania wykonywalnego skrótu.
Przy kolejnej integracji chcę sprawdzić:
- Czy klucz służy publicznej identyfikacji, czy daje uprzywilejowany dostęp do konta u dostawcy?
- Dokąd trafia: do paczki klienta, środowiska serwera czy obu?
- Kto może uruchomić operację po stronie serwera?
- Co się dzieje, gdy dostawca nie odpowie, odrzuci żądanie lub wyczerpie limit?
To pytania do przeglądu, nie deklaracja, że każdą odpowiedź wymusza automatyczny test.
Wniosek wykracza poza kod generowany przez AI. Za tę granicę nadal odpowiadam ja — niezależnie od tego, czy pierwszą wersję napisał agent, znalazłem ją w tutorialu, czy podpowiedział mi ją mój własny optymizm po północy.
Stan na 12 października 2026: artykuł opisuje kod i zapisy decyzji w repozytorium, nie nowy test działającego Appwrite. Nie stwierdza publicznego wykorzystania klucza, strat finansowych ani wydania aplikacji z przejętymi zabezpieczeniami.
Kontekst produktu znajdziesz w tekście dlaczego zbudowałem Car Brain.