Files
duck-prod-manager/notes/api_services.md
bartool 93778065ce refaktoryzacja service api.
wydzielenie mockow.
dodanie parsowania wyszukiwania
2026-05-02 05:49:24 +02:00

11 KiB

API Services I Mocki

Data: 2026-04-29

Cel dokumentu

Ten dokument opisuje warstwe API we frontendzie.

Warstwa API znajduje sie w:

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.:

fetchProducts(params)

a service powinien zajac sie reszta.

Aktualne pliki services

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:

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:

komponent Vue
  -> store Pinia
  -> service API
  -> mock JSON albo Axios
  -> backend Directus

Przyklad:

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:

api.get('/mayo-api/products', { params })

Wtedy store zna:

  • Axios,
  • endpoint,
  • strukture odpowiedzi HTTP.

To miesza odpowiedzialnosci.

Po wydzieleniu services store robi:

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:

export const fetchProducts = USE_MOCK_API ? fetchProductsFromMock : fetchProductsFromHttp

Dzieki temu productsStore zawsze importuje to samo:

import { fetchProducts } from 'src/services/productsApi'

ale realna implementacja moze byc mockowa albo HTTP.

apiMode.js

Plik:

frontend/src/services/apiMode.js

Zawiera przelacznik miedzy mock API i prawdziwym API:

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:

VITE_USE_MOCK_API=false npm run dev

Uruchomienie z mockami:

npm run dev

Mocki maja sztuczne opoznienie:

waitForMockApi()

Dzieki temu podczas pracy nad UI widac stany ladowania podobne do prawdziwego API.

Pliki mockow

Mocki sa zwyklymi plikami JSON:

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:

frontend/src/services/productsApi.js
frontend/src/services/productsHttpApi.js

Eksportuje:

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:

GET /mayo-api/products

Zwraca:

{
  items: [],
  pageInfo: {
    limit: 30,
    offset: 0,
    hasMore: true,
    total: 184,
  },
}

W trybie mock implementacja jest w:

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:

GET /mayo-api/products

z parametrami:

{
  limit,
  offset,
  search,
  orderSearch,
  model,
  client,
  finish,
  productionList,
  year,
}

orderSearch powstaje w productsStore przez parser:

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:

frontend/src/services/productTimelineApi.js
frontend/src/services/productTimelineHttpApi.js

Eksportuje:

fetchProductTimeline(productId)
createProductTimelineEvent(productId, payload)

fetchProductTimeline

Pobiera pelny timeline produktu.

W trybie mock implementacja jest w:

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:

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:

POST /mayo-api/products/:id/timeline/events

Zwracany format:

{
  event: {},
  timelinePreview: {
    body: [],
    neck: [],
  },
}

To pozwala store od razu zaktualizowac:

  • pelny timeline,
  • preview na glownej karcie produktu.

productSpecificationApi.js i productSpecificationHttpApi.js

Plik:

frontend/src/services/productSpecificationApi.js
frontend/src/services/productSpecificationHttpApi.js

Eksportuje:

fetchProductSpecification(productId)
refreshProductSpecification(productId)

fetchProductSpecification

Pobiera specyfikacje produktu.

W trybie mock implementacja jest w:

frontend/src/mocks/api/productSpecificationMockApi.js

W trybie mock:

  • czyta frontend/src/mocks/specifications.json.

W trybie prawdziwego API implementacja jest w productSpecificationHttpApi.js:

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:

POST /mayo-api/products/:id/specification/refresh

Normalizacja odpowiedzi

Ten plik zawiera funkcje:

normalizeSpecificationResponse(data)

Jej zadaniem jest zamiana odpowiedzi backendu na prosty format dla store:

{
  productId,
  orderId,
  sourceUrl,
  lastFetchedAt,
  sections,
  diff,
}

Service akceptuje dwa ksztalty:

data.specification.sections

albo:

data.sections

Dzieki temu store nie musi znac szczegolow odpowiedzi HTTP.

dictionariesApi.js i dictionariesHttpApi.js

Plik:

frontend/src/services/dictionariesApi.js
frontend/src/services/dictionariesHttpApi.js

Eksportuje:

fetchDictionaries()

Odpowiedzialnosc:

  • pobranie slownikow do filtrow i formularzy,
  • normalizacja pustych pol do pustych tablic.

W trybie mock implementacja jest w:

frontend/src/mocks/api/dictionariesMockApi.js

W trybie mock:

  • czyta frontend/src/mocks/dictionaries.json.

W trybie prawdziwego API implementacja jest w dictionariesHttpApi.js:

GET /mayo-api/dictionaries

Zwraca:

{
  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:

frontend/src/services/productPhotosApi.js
frontend/src/services/productPhotosHttpApi.js
frontend/src/mocks/api/productPhotosMockApi.js

Fasada:

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:

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:

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:

{
  items: [],
  pageInfo: {}
}

to mock tez powinien zwrocic:

{
  items: [],
  pageInfo: {}
}

Nie nalezy robic osobnych struktur danych tylko dla widoku, jesli nie beda zgodne z przyszlym API.

Co zostalo juz zrobione

Utworzono:

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:

frontend/src/mocks/products.json
frontend/src/mocks/timelines.json
frontend/src/mocks/specifications.json
frontend/src/mocks/dictionaries.json

Utworzono implementacje mock API:

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:

api

ani:

src/mocks/*.json

Komponent powinien uzywac store albo dostawac dane przez props.

Store powinien uzywac services.

Services powinny uzywac Axiosa albo mockow.