# API Integration ## RTK Query Setup ```typescript // src/services/api.ts import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import Config from 'react-native-config'; import { RootState } from '@redux/store'; export const api = createApi({ reducerPath: 'api', baseQuery: fetchBaseQuery({ baseUrl: Config.API_BASE_URL, prepareHeaders: (headers, { getState }) => { const token = (getState() as RootState).auth.token; if (token) { headers.set('authorization', `Bearer ${token}`); } headers.set('Content-Type', 'application/json'); return headers; }, }), tagTypes: ['User', 'NGO', 'Donation', 'Request'], endpoints: () => ({}), }); ``` ## Auth API ```typescript // src/services/authAPI.ts import { api } from './api'; interface LoginRequest { email: string; password: string; } interface LoginResponse { user: { id: string; email: string; firstName: string; lastName: string; }; token: string; refreshToken: string; } interface RegisterRequest { firstName: string; lastName: string; email: string; password: string; phone: string; } export const authAPI = api.injectEndpoints({ endpoints: (builder) => ({ login: builder.mutation({ query: (credentials) => ({ url: '/auth/login', method: 'POST', body: credentials, }), }), register: builder.mutation({ query: (userData) => ({ url: '/auth/register', method: 'POST', body: userData, }), }), refreshToken: builder.mutation<{ token: string }, { refreshToken: string }>( { query: ({ refreshToken }) => ({ url: '/auth/refresh', method: 'POST', body: { refreshToken }, }), }, ), logout: builder.mutation({ query: () => ({ url: '/auth/logout', method: 'POST', }), }), forgotPassword: builder.mutation({ query: ({ email }) => ({ url: '/auth/forgot-password', method: 'POST', body: { email }, }), }), verifyOTP: builder.mutation({ query: ({ email, otp }) => ({ url: '/auth/verify-otp', method: 'POST', body: { email, otp }, }), }), }), }); export const { useLoginMutation, useRegisterMutation, useRefreshTokenMutation, useLogoutMutation, useForgotPasswordMutation, useVerifyOTPMutation, } = authAPI; ``` ## User API ```typescript // src/services/userAPI.ts import { api } from './api'; interface User { id: string; email: string; firstName: string; lastName: string; phone?: string; avatar?: string; isNGO: boolean; ngoDetails?: NGODetails; } interface NGODetails { ngoName: string; registrationNumber: string; address: string; description: string; causes: string[]; } interface UpdateProfileRequest { firstName?: string; lastName?: string; phone?: string; avatar?: string; } export const userAPI = api.injectEndpoints({ endpoints: (builder) => ({ getProfile: builder.query({ query: () => '/user/profile', providesTags: ['User'], }), updateProfile: builder.mutation({ query: (updates) => ({ url: '/user/profile', method: 'PUT', body: updates, }), invalidatesTags: ['User'], }), uploadAvatar: builder.mutation<{ avatarUrl: string }, FormData>({ query: (formData) => ({ url: '/user/avatar', method: 'POST', body: formData, }), invalidatesTags: ['User'], }), deleteAccount: builder.mutation({ query: () => ({ url: '/user/account', method: 'DELETE', }), }), }), }); export const { useGetProfileQuery, useUpdateProfileMutation, useUploadAvatarMutation, useDeleteAccountMutation, } = userAPI; ``` ## NGO API ```typescript // src/services/ngoAPI.ts import { api } from './api'; interface NGO { id: string; ngoName: string; description: string; registrationNumber: string; address: string; causes: string[]; coverImage?: string; isVerified: boolean; totalDonations: number; activeRequests: number; } interface CreateNGORequest { ngoName: string; description: string; registrationNumber: string; address: string; causes: string[]; documents: string[]; } export const ngoAPI = api.injectEndpoints({ endpoints: (builder) => ({ getNGOList: builder.query< NGO[], { page?: number; limit?: number; search?: string } >({ query: ({ page = 1, limit = 10, search }) => ({ url: '/ngo', params: { page, limit, search }, }), providesTags: ['NGO'], }), getNGOById: builder.query({ query: (id) => `/ngo/${id}`, providesTags: ['NGO'], }), createNGO: builder.mutation({ query: (ngoData) => ({ url: '/ngo', method: 'POST', body: ngoData, }), invalidatesTags: ['NGO', 'User'], }), updateNGO: builder.mutation< NGO, { id: string; updates: Partial } >({ query: ({ id, updates }) => ({ url: `/ngo/${id}`, method: 'PUT', body: updates, }), invalidatesTags: ['NGO'], }), getFavoriteNGOs: builder.query({ query: () => '/ngo/favorites', providesTags: ['NGO'], }), toggleFavoriteNGO: builder.mutation({ query: (ngoId) => ({ url: `/ngo/${ngoId}/favorite`, method: 'POST', }), invalidatesTags: ['NGO'], }), }), }); export const { useGetNGOListQuery, useGetNGOByIdQuery, useCreateNGOMutation, useUpdateNGOMutation, useGetFavoriteNGOsQuery, useToggleFavoriteNGOMutation, } = ngoAPI; ``` ## Request API ```typescript // src/services/requestAPI.ts import { api } from './api'; interface FundraisingRequest { id: string; title: string; description: string; category: string; targetAmount: number; currentAmount: number; images: string[]; location: { latitude: number; longitude: number; address: string; }; status: 'active' | 'completed' | 'cancelled'; createdBy: string; ngoId?: string; isUrgent: boolean; endDate: string; } interface CreateRequestData { title: string; description: string; category: string; targetAmount: number; images: string[]; location: { latitude: number; longitude: number; address: string; }; isUrgent: boolean; endDate: string; } export const requestAPI = api.injectEndpoints({ endpoints: (builder) => ({ getRequests: builder.query< FundraisingRequest[], { page?: number; limit?: number; category?: string; location?: string; urgent?: boolean; } >({ query: (params) => ({ url: '/requests', params, }), providesTags: ['Request'], }), getRequestById: builder.query({ query: (id) => `/requests/${id}`, providesTags: ['Request'], }), createRequest: builder.mutation({ query: (requestData) => ({ url: '/requests', method: 'POST', body: requestData, }), invalidatesTags: ['Request'], }), updateRequest: builder.mutation< FundraisingRequest, { id: string; updates: Partial; } >({ query: ({ id, updates }) => ({ url: `/requests/${id}`, method: 'PUT', body: updates, }), invalidatesTags: ['Request'], }), deleteRequest: builder.mutation({ query: (id) => ({ url: `/requests/${id}`, method: 'DELETE', }), invalidatesTags: ['Request'], }), getMyRequests: builder.query({ query: () => '/requests/my-requests', providesTags: ['Request'], }), }), }); export const { useGetRequestsQuery, useGetRequestByIdQuery, useCreateRequestMutation, useUpdateRequestMutation, useDeleteRequestMutation, useGetMyRequestsQuery, } = requestAPI; ``` ## Error Handling ```typescript // src/utils/errorHandler.ts import { SerializedError } from '@reduxjs/toolkit'; import { FetchBaseQueryError } from '@reduxjs/toolkit/query'; export interface APIError { message: string; status?: number; code?: string; } export const handleAPIError = ( error: FetchBaseQueryError | SerializedError, ): APIError => { if ('status' in error) { // FetchBaseQueryError const status = error.status; const data = error.data as any; switch (status) { case 400: return { message: data?.message || 'Bad request', status: 400, code: 'BAD_REQUEST', }; case 401: return { message: 'Authentication required', status: 401, code: 'UNAUTHORIZED', }; case 403: return { message: 'Access denied', status: 403, code: 'FORBIDDEN', }; case 404: return { message: 'Resource not found', status: 404, code: 'NOT_FOUND', }; case 500: return { message: 'Server error. Please try again later.', status: 500, code: 'SERVER_ERROR', }; default: return { message: data?.message || 'An unexpected error occurred', status: typeof status === 'number' ? status : undefined, code: 'UNKNOWN_ERROR', }; } } else { // SerializedError return { message: error.message || 'Network error', code: 'NETWORK_ERROR', }; } }; ``` ## API Hooks ```typescript // src/hooks/useAPI.ts import { useState, useCallback } from 'react'; import { handleAPIError, APIError } from '@utils/errorHandler'; interface UseAPIState { data: T | null; loading: boolean; error: APIError | null; } export const useAPI = () => { const [state, setState] = useState>({ data: null, loading: false, error: null, }); const execute = useCallback(async (apiCall: () => Promise) => { setState((prev) => ({ ...prev, loading: true, error: null })); try { const data = await apiCall(); setState({ data, loading: false, error: null }); return data; } catch (error: any) { const apiError = handleAPIError(error); setState((prev) => ({ ...prev, loading: false, error: apiError })); throw apiError; } }, []); const reset = useCallback(() => { setState({ data: null, loading: false, error: null }); }, []); return { ...state, execute, reset, }; }; ``` ## Store Integration ```typescript // src/redux/store/index.ts (updated) import { configureStore } from '@reduxjs/toolkit'; import { persistStore, persistReducer } from 'redux-persist'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers } from '@reduxjs/toolkit'; import { api } from '@services/api'; import authSlice from '../slices/authSlice'; const persistConfig = { key: 'root', storage: AsyncStorage, whitelist: ['auth'], }; const rootReducer = combineReducers({ auth: authSlice, [api.reducerPath]: api.reducer, }); const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'], }, }).concat(api.middleware), }); export const persistor = persistStore(store); export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; ```