Grouped transaction history — mobil migration
GET /users/me/transactions/grouped pagination ve entry.id key extractor değişikliği
Grouped transaction history — mobil migration
Breaking change (pagination)
Grouped endpoint artık transaction satırı değil, grouped entry bazında sayfalar. Infinite scroll key extractor entry.id kullanmalıdır.
Amaç
Grouped transaction history endpoint'inde pagination semantiği değişti. Aynı streamId farklı sayfalarda tekrar stream_group olarak dönüyordu; React duplicate key uyarısına yol açıyordu. Mobil istemci grouped listeyi yeni kontrata ve entry.id key'ine göre güncellemelidir.
Etkilenen: iOS / Android (RTK Query veya eşdeğer). Web aynı endpoint'i kullanıyorsa aynı kurallar geçerlidir.
Önkoşullar
- Backend deploy: group-based pagination aktif
- Kullanıcı JWT (
Authorization: Bearer) - Grouped ekran kullanılıyorsa flat endpoint zorunlu değil; flat ekran
/users/me/transactionsile çalışmaya devam eder
Endpoint
| Rol | Method | Path |
|---|---|---|
| Flat (değişmedi) | GET | /api/v1/users/me/transactions |
| Grouped (güncellendi) | GET | /api/v1/users/me/transactions/grouped |
| Admin (mobil kullanmaz) | GET | /api/v1/users/admin/:id/transactions/grouped |
Mobil normal kullanıcı akışı yalnızca GET /api/v1/users/me/transactions/grouped kullanmalıdır.
Query: page, limit, types, statuses, direction, dateFrom, dateTo, sort — API Reference ile doğrulayın.
Request / Response
Başarılı gövde BaseResponse + paginated data:
type UserTransactionHistoryGroupedEntry =
| {
id: string; // stream_group:<streamId>
entryType: 'stream_group';
streamId: string;
streamTitle?: string | null;
thumbnailUrl?: string | null;
transactions: UserTransactionHistoryItem[];
}
| {
id: string; // transaction:<transaction.id>
entryType: 'transaction';
transaction: UserTransactionHistoryItem;
};
type PaginatedUserGroupedTransactions = {
list: UserTransactionHistoryGroupedEntry[];
pagination: {
currentPage: number;
totalPages: number;
totalItems: number; // grouped entry sayısı, transaction satırı değil
itemsPerPage: number;
hasNextPage: boolean;
hasPrevPage: boolean;
};
};pagination.totalItems ve itemsPerPage artık grouped entry sayısını ifade eder. Bir stream_group içinde limit'ten fazla transaction olabilir.
Hata kodları
| HTTP | Durum | Client |
|---|---|---|
401 | Token eksik/geçersiz | Refresh + tek retry (Authentication Flow) |
400 | Query validation | Filtreleri düzelt |
403 | Yetki | — |
Detay: Error Model. Flat endpoint hata davranışı değişmedi.
Client adımları
1. Key extractor
// Eski — duplicate key riski
const keyExtractor = (entry: UserTransactionHistoryGroupedEntry) =>
entry.entryType === 'stream_group'
? `stream_group:${entry.streamId}`
: `transaction:${entry.transaction.id}`;
// Yeni
const keyExtractor = (entry: UserTransactionHistoryGroupedEntry) => entry.id;Nested satırlar için transaction.id kullanılmaya devam eder.
2. RTK Query
getMyGroupedTransactions: builder.query<
PaginatedUserGroupedTransactions,
{ page?: number; limit?: number; /* filtreler */ }
>({
query: ({ page = 1, limit = 20, types, statuses, ...params }) => ({
url: '/api/v1/users/me/transactions/grouped',
method: 'GET',
params: {
page,
limit,
types: types?.join(','),
statuses: statuses?.join(','),
...params,
},
}),
}),Infinite scroll: append sırasında entry.id ile defensive dedupe önerilir.
3. UI
- Header:
streamTitle || streamId pagination.totalItemskullanıcıya "toplam transaction" olarak gösterilmemeli- Filtre değişince appended liste sıfırlanmalı
4. Test checklist
- Flat ekran
/users/me/transactionsile çalışıyor - Grouped ekranda
keyExtractor={(e) => e.id} limit=1ile bir stream grubu tek entry olarak geliyor- Sayfa 2'de page 1'deki
stream_grouptekrar gelmiyor entryType: 'transaction'tekil render- Filtre değişince liste reset