601 lines
11 KiB
Markdown
601 lines
11 KiB
Markdown
# API Services I Mocki
|
|
|
|
Data: 2026-04-29
|
|
|
|
## Cel dokumentu
|
|
|
|
Ten dokument opisuje warstwe API we frontendzie.
|
|
|
|
Warstwa API znajduje sie w:
|
|
|
|
```txt
|
|
frontend/src/services/
|
|
```
|
|
|
|
Jej zadaniem jest odseparowanie store od szczegolow komunikacji HTTP.
|
|
|
|
Store nie powinien wiedziec:
|
|
|
|
- jaki dokladnie jest URL endpointu,
|
|
- czy komunikacja idzie przez Axios,
|
|
- czy dane pochodza z prawdziwego backendu,
|
|
- czy dane pochodza z mockow JSON.
|
|
|
|
Store powinien wywolac funkcje domenowa, np.:
|
|
|
|
```js
|
|
fetchProducts(params)
|
|
```
|
|
|
|
a service powinien zajac sie reszta.
|
|
|
|
## Aktualne pliki services
|
|
|
|
```txt
|
|
frontend/src/services/apiMode.js
|
|
frontend/src/services/productsApi.js
|
|
frontend/src/services/productsHttpApi.js
|
|
frontend/src/services/productTimelineApi.js
|
|
frontend/src/services/productTimelineHttpApi.js
|
|
frontend/src/services/productSpecificationApi.js
|
|
frontend/src/services/productSpecificationHttpApi.js
|
|
frontend/src/services/dictionariesApi.js
|
|
frontend/src/services/dictionariesHttpApi.js
|
|
```
|
|
|
|
Pliki `*Api.js` sa fasadami. Wybieraja implementacje na podstawie `USE_MOCK_API`.
|
|
|
|
Pliki `*HttpApi.js` sa implementacja prawdziwego API przez Axios.
|
|
|
|
Implementacje mockow sa poza katalogiem `services`:
|
|
|
|
```txt
|
|
frontend/src/mocks/api/productsMockApi.js
|
|
frontend/src/mocks/api/productTimelineMockApi.js
|
|
frontend/src/mocks/api/productSpecificationMockApi.js
|
|
frontend/src/mocks/api/dictionariesMockApi.js
|
|
```
|
|
|
|
## Przeplyw danych
|
|
|
|
Docelowy przeplyw wyglada tak:
|
|
|
|
```txt
|
|
komponent Vue
|
|
-> store Pinia
|
|
-> service API
|
|
-> mock JSON albo Axios
|
|
-> backend Directus
|
|
```
|
|
|
|
Przyklad:
|
|
|
|
```txt
|
|
IndexPage.vue
|
|
-> productsStore.fetchFirstPage()
|
|
-> fetchProducts(params)
|
|
-> productsMockApi albo productsHttpApi
|
|
-> products.json albo GET /mayo-api/products
|
|
```
|
|
|
|
## Dlaczego services sa osobna warstwa
|
|
|
|
Bez services store musialby robic cos takiego:
|
|
|
|
```js
|
|
api.get('/mayo-api/products', { params })
|
|
```
|
|
|
|
Wtedy store zna:
|
|
|
|
- Axios,
|
|
- endpoint,
|
|
- strukture odpowiedzi HTTP.
|
|
|
|
To miesza odpowiedzialnosci.
|
|
|
|
Po wydzieleniu services store robi:
|
|
|
|
```js
|
|
const { items, pageInfo } = await fetchProducts(params)
|
|
```
|
|
|
|
To jest lepsze, bo:
|
|
|
|
- store skupia sie na stanie aplikacji,
|
|
- services skupiaja sie na komunikacji,
|
|
- endpoint mozna zmienic w jednym miejscu,
|
|
- latwiej dodac mocki,
|
|
- latwiej testowac logike store.
|
|
|
|
Po ostatnim porzadkowaniu services sa fasadami. Przyklad:
|
|
|
|
```js
|
|
export const fetchProducts = USE_MOCK_API ? fetchProductsFromMock : fetchProductsFromHttp
|
|
```
|
|
|
|
Dzieki temu `productsStore` zawsze importuje to samo:
|
|
|
|
```js
|
|
import { fetchProducts } from 'src/services/productsApi'
|
|
```
|
|
|
|
ale realna implementacja moze byc mockowa albo HTTP.
|
|
|
|
## `apiMode.js`
|
|
|
|
Plik:
|
|
|
|
```txt
|
|
frontend/src/services/apiMode.js
|
|
```
|
|
|
|
Zawiera przelacznik miedzy mock API i prawdziwym API:
|
|
|
|
```js
|
|
export const USE_MOCK_API = import.meta.env.VITE_USE_MOCK_API !== 'false'
|
|
```
|
|
|
|
To oznacza:
|
|
|
|
- domyslnie mocki sa wlaczone,
|
|
- jesli ustawisz `VITE_USE_MOCK_API=false`, aplikacja uzyje prawdziwego backendu.
|
|
|
|
Uruchomienie z prawdziwym backendem:
|
|
|
|
```bash
|
|
VITE_USE_MOCK_API=false npm run dev
|
|
```
|
|
|
|
Uruchomienie z mockami:
|
|
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
Mocki maja sztuczne opoznienie:
|
|
|
|
```js
|
|
waitForMockApi()
|
|
```
|
|
|
|
Dzieki temu podczas pracy nad UI widac stany ladowania podobne do prawdziwego API.
|
|
|
|
## Pliki mockow
|
|
|
|
Mocki sa zwyklymi plikami JSON:
|
|
|
|
```txt
|
|
frontend/src/mocks/products.json
|
|
frontend/src/mocks/timelines.json
|
|
frontend/src/mocks/specifications.json
|
|
frontend/src/mocks/dictionaries.json
|
|
```
|
|
|
|
Sa uzywane tylko przez implementacje mock API w `frontend/src/mocks/api/`.
|
|
|
|
Komponenty, store i fasady `services/*Api.js` nie importuja plikow JSON bezposrednio.
|
|
|
|
To jest wazne, bo komponent nie powinien wiedziec, skad przyszly dane. Dla komponentu dane z mocka i z backendu powinny wygladac identycznie.
|
|
|
|
## `productsApi.js` i `productsHttpApi.js`
|
|
|
|
Plik:
|
|
|
|
```txt
|
|
frontend/src/services/productsApi.js
|
|
frontend/src/services/productsHttpApi.js
|
|
```
|
|
|
|
Eksportuje:
|
|
|
|
```js
|
|
fetchProducts(params)
|
|
```
|
|
|
|
Odpowiedzialnosc:
|
|
|
|
- pobranie listy produktow,
|
|
- obsluga filtrow,
|
|
- obsluga `limit` i `offset`,
|
|
- zwrocenie danych w formacie oczekiwanym przez `productsStore`.
|
|
|
|
`productsApi.js` jest fasada.
|
|
|
|
`productsHttpApi.js` zna Axios i endpoint:
|
|
|
|
```http
|
|
GET /mayo-api/products
|
|
```
|
|
|
|
Zwraca:
|
|
|
|
```js
|
|
{
|
|
items: [],
|
|
pageInfo: {
|
|
limit: 30,
|
|
offset: 0,
|
|
hasMore: true,
|
|
total: 184,
|
|
},
|
|
}
|
|
```
|
|
|
|
W trybie mock implementacja jest w:
|
|
|
|
```txt
|
|
frontend/src/mocks/api/productsMockApi.js
|
|
```
|
|
|
|
W trybie mock:
|
|
|
|
- czyta `frontend/src/mocks/products.json`,
|
|
- filtruje po `orderSearch`, `finish`, `year`, `productionList`,
|
|
- ucina wynik wedlug `limit` i `offset`,
|
|
- zwraca `items` i `pageInfo`.
|
|
|
|
W trybie prawdziwego API implementacja jest w `productsHttpApi.js`:
|
|
|
|
```http
|
|
GET /mayo-api/products
|
|
```
|
|
|
|
z parametrami:
|
|
|
|
```js
|
|
{
|
|
limit,
|
|
offset,
|
|
search,
|
|
orderSearch,
|
|
model,
|
|
client,
|
|
finish,
|
|
productionList,
|
|
year,
|
|
}
|
|
```
|
|
|
|
`orderSearch` powstaje w `productsStore` przez parser:
|
|
|
|
```txt
|
|
frontend/src/utils/orderSearchParser.js
|
|
```
|
|
|
|
Do HTTP `orderSearch` jest serializowane jako JSON string, bo endpoint listy jest typu `GET`.
|
|
|
|
## `productTimelineApi.js` i `productTimelineHttpApi.js`
|
|
|
|
Plik:
|
|
|
|
```txt
|
|
frontend/src/services/productTimelineApi.js
|
|
frontend/src/services/productTimelineHttpApi.js
|
|
```
|
|
|
|
Eksportuje:
|
|
|
|
```js
|
|
fetchProductTimeline(productId)
|
|
createProductTimelineEvent(productId, payload)
|
|
```
|
|
|
|
### `fetchProductTimeline`
|
|
|
|
Pobiera pelny timeline produktu.
|
|
|
|
W trybie mock implementacja jest w:
|
|
|
|
```txt
|
|
frontend/src/mocks/api/productTimelineMockApi.js
|
|
```
|
|
|
|
W trybie mock:
|
|
|
|
- czyta `frontend/src/mocks/timelines.json`,
|
|
- zwraca timeline dla `productId`,
|
|
- jesli brak danych, zwraca pusty timeline.
|
|
|
|
W trybie prawdziwego API implementacja jest w `productTimelineHttpApi.js`:
|
|
|
|
```http
|
|
GET /mayo-api/products/:id/timeline
|
|
```
|
|
|
|
### `createProductTimelineEvent`
|
|
|
|
Dodaje event do timeline.
|
|
|
|
W trybie mock:
|
|
|
|
- tworzy sztuczny event,
|
|
- dodaje go do pamieci mockow,
|
|
- przelicza `timelinePreview`,
|
|
- zwraca nowy event i nowy preview.
|
|
|
|
W trybie prawdziwego API:
|
|
|
|
```http
|
|
POST /mayo-api/products/:id/timeline/events
|
|
```
|
|
|
|
Zwracany format:
|
|
|
|
```js
|
|
{
|
|
event: {},
|
|
timelinePreview: {
|
|
body: [],
|
|
neck: [],
|
|
},
|
|
}
|
|
```
|
|
|
|
To pozwala store od razu zaktualizowac:
|
|
|
|
- pelny timeline,
|
|
- preview na glownej karcie produktu.
|
|
|
|
## `productSpecificationApi.js` i `productSpecificationHttpApi.js`
|
|
|
|
Plik:
|
|
|
|
```txt
|
|
frontend/src/services/productSpecificationApi.js
|
|
frontend/src/services/productSpecificationHttpApi.js
|
|
```
|
|
|
|
Eksportuje:
|
|
|
|
```js
|
|
fetchProductSpecification(productId)
|
|
refreshProductSpecification(productId)
|
|
```
|
|
|
|
### `fetchProductSpecification`
|
|
|
|
Pobiera specyfikacje produktu.
|
|
|
|
W trybie mock implementacja jest w:
|
|
|
|
```txt
|
|
frontend/src/mocks/api/productSpecificationMockApi.js
|
|
```
|
|
|
|
W trybie mock:
|
|
|
|
- czyta `frontend/src/mocks/specifications.json`.
|
|
|
|
W trybie prawdziwego API implementacja jest w `productSpecificationHttpApi.js`:
|
|
|
|
```http
|
|
GET /mayo-api/products/:id/specification
|
|
```
|
|
|
|
### `refreshProductSpecification`
|
|
|
|
Odswieza specyfikacje produktu.
|
|
|
|
W trybie mock:
|
|
|
|
- zwraca dane z mocka,
|
|
- aktualizuje `lastFetchedAt` na aktualny czas.
|
|
|
|
W trybie prawdziwego API:
|
|
|
|
```http
|
|
POST /mayo-api/products/:id/specification/refresh
|
|
```
|
|
|
|
### Normalizacja odpowiedzi
|
|
|
|
Ten plik zawiera funkcje:
|
|
|
|
```js
|
|
normalizeSpecificationResponse(data)
|
|
```
|
|
|
|
Jej zadaniem jest zamiana odpowiedzi backendu na prosty format dla store:
|
|
|
|
```js
|
|
{
|
|
productId,
|
|
orderId,
|
|
sourceUrl,
|
|
lastFetchedAt,
|
|
sections,
|
|
diff,
|
|
}
|
|
```
|
|
|
|
Service akceptuje dwa ksztalty:
|
|
|
|
```js
|
|
data.specification.sections
|
|
```
|
|
|
|
albo:
|
|
|
|
```js
|
|
data.sections
|
|
```
|
|
|
|
Dzieki temu store nie musi znac szczegolow odpowiedzi HTTP.
|
|
|
|
## `dictionariesApi.js` i `dictionariesHttpApi.js`
|
|
|
|
Plik:
|
|
|
|
```txt
|
|
frontend/src/services/dictionariesApi.js
|
|
frontend/src/services/dictionariesHttpApi.js
|
|
```
|
|
|
|
Eksportuje:
|
|
|
|
```js
|
|
fetchDictionaries()
|
|
```
|
|
|
|
Odpowiedzialnosc:
|
|
|
|
- pobranie slownikow do filtrow i formularzy,
|
|
- normalizacja pustych pol do pustych tablic.
|
|
|
|
W trybie mock implementacja jest w:
|
|
|
|
```txt
|
|
frontend/src/mocks/api/dictionariesMockApi.js
|
|
```
|
|
|
|
W trybie mock:
|
|
|
|
- czyta `frontend/src/mocks/dictionaries.json`.
|
|
|
|
W trybie prawdziwego API implementacja jest w `dictionariesHttpApi.js`:
|
|
|
|
```http
|
|
GET /mayo-api/dictionaries
|
|
```
|
|
|
|
Zwraca:
|
|
|
|
```js
|
|
{
|
|
models: [],
|
|
clients: [],
|
|
finishes: [],
|
|
productionLists: [],
|
|
operations: [],
|
|
colors: [],
|
|
}
|
|
```
|
|
|
|
## Jak dodac nowy endpoint
|
|
|
|
Przyklad: chcesz dodac API do zdjec produktu.
|
|
|
|
Nie dodawaj Axiosa bezposrednio w komponencie ani w store.
|
|
|
|
Zrob nowy service:
|
|
|
|
```txt
|
|
frontend/src/services/productPhotosApi.js
|
|
frontend/src/services/productPhotosHttpApi.js
|
|
frontend/src/mocks/api/productPhotosMockApi.js
|
|
```
|
|
|
|
Fasada:
|
|
|
|
```js
|
|
import { fetchProductPhotos as fetchProductPhotosFromHttp } from 'src/services/productPhotosHttpApi'
|
|
import { fetchProductPhotos as fetchProductPhotosFromMock } from 'src/mocks/api/productPhotosMockApi'
|
|
import { USE_MOCK_API } from 'src/services/apiMode'
|
|
|
|
export const fetchProductPhotos = USE_MOCK_API
|
|
? fetchProductPhotosFromMock
|
|
: fetchProductPhotosFromHttp
|
|
```
|
|
|
|
Implementacja HTTP:
|
|
|
|
```js
|
|
import { api } from 'src/boot/axios'
|
|
|
|
export async function fetchProductPhotos(productId) {
|
|
const response = await api.get(`/mayo-api/products/${productId}/photos`)
|
|
return response.data
|
|
}
|
|
```
|
|
|
|
Dopiero potem store importuje:
|
|
|
|
```js
|
|
import { fetchProductPhotos } from 'src/services/productPhotosApi'
|
|
```
|
|
|
|
## Zasady pracy z mockami
|
|
|
|
Mocki powinny miec taki sam ksztalt jak prawdziwe API.
|
|
|
|
To jest najwazniejsza zasada.
|
|
|
|
Jesli prawdziwy endpoint ma zwrocic:
|
|
|
|
```js
|
|
{
|
|
items: [],
|
|
pageInfo: {}
|
|
}
|
|
```
|
|
|
|
to mock tez powinien zwrocic:
|
|
|
|
```js
|
|
{
|
|
items: [],
|
|
pageInfo: {}
|
|
}
|
|
```
|
|
|
|
Nie nalezy robic osobnych struktur danych tylko dla widoku, jesli nie beda zgodne z przyszlym API.
|
|
|
|
## Co zostalo juz zrobione
|
|
|
|
Utworzono:
|
|
|
|
```txt
|
|
frontend/src/services/apiMode.js
|
|
frontend/src/services/productsApi.js
|
|
frontend/src/services/productsHttpApi.js
|
|
frontend/src/services/productTimelineApi.js
|
|
frontend/src/services/productTimelineHttpApi.js
|
|
frontend/src/services/productSpecificationApi.js
|
|
frontend/src/services/productSpecificationHttpApi.js
|
|
frontend/src/services/dictionariesApi.js
|
|
frontend/src/services/dictionariesHttpApi.js
|
|
```
|
|
|
|
Utworzono dane mockow:
|
|
|
|
```txt
|
|
frontend/src/mocks/products.json
|
|
frontend/src/mocks/timelines.json
|
|
frontend/src/mocks/specifications.json
|
|
frontend/src/mocks/dictionaries.json
|
|
```
|
|
|
|
Utworzono implementacje mock API:
|
|
|
|
```txt
|
|
frontend/src/mocks/api/productsMockApi.js
|
|
frontend/src/mocks/api/productTimelineMockApi.js
|
|
frontend/src/mocks/api/productSpecificationMockApi.js
|
|
frontend/src/mocks/api/dictionariesMockApi.js
|
|
```
|
|
|
|
Zaktualizowano store tak, aby uzywaly services zamiast Axiosa bezposrednio.
|
|
|
|
Zaktualizowano widok glownej strony tak, aby pobieral dane ze store zamiast trzymac lokalne mocki w komponencie.
|
|
|
|
## Najwazniejsza regula
|
|
|
|
Komponenty nie powinny importowac:
|
|
|
|
```js
|
|
api
|
|
```
|
|
|
|
ani:
|
|
|
|
```js
|
|
src/mocks/*.json
|
|
```
|
|
|
|
Komponent powinien uzywac store albo dostawac dane przez props.
|
|
|
|
Store powinien uzywac services.
|
|
|
|
Services powinny uzywac Axiosa albo mockow.
|