UNPKG

@cranberry-money/shared-services

Version:

Platform-agnostic API services with pure functions and dependency injection. Includes auth, portfolios, instruments, countries, sectors, and more.

266 lines (191 loc) โ€ข 7.77 kB
# @cranberry-money/shared-services **Platform-agnostic service functions** that work with any axios-compatible API client. [![npm version](https://badge.fury.io/js/%40cranberry-money%2Fshared-services.svg)](https://badge.fury.io/js/%40cranberry-money%2Fshared-services) [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/) ## ๐Ÿ“ฆ Package Overview This package provides reusable service functions for the Blueberry platform ecosystem. Services are pure functions that accept an API client, allowing the same business logic to work across web browsers, mobile apps, and testing environments. ### Core Philosophy - **One service implementation, multiple platforms**: Write once, use everywhere - **Platform owns authentication**: Each platform handles auth its own way (cookies, tokens, etc.) - **Simple and functional**: No complex patterns, just functions that call APIs - **Fully typed**: Complete TypeScript support with types from `@cranberry-money/shared-types` ## ๐Ÿš€ Quick Start ### Installation ```bash npm install @cranberry-money/shared-services ``` ### Basic Usage #### Web Application (with cookies) ```typescript // src/services/webApiClient.ts import axios from 'axios'; export const webApiClient = axios.create({ baseURL: import.meta.env.VITE_API_URL, withCredentials: true, // Use cookies for auth }); ``` ```typescript // src/services/portfolios.ts import { webApiClient } from './webApiClient'; import { createPortfolioService } from '@cranberry-money/shared-services'; // Create your platform-specific service export const portfolioService = createPortfolioService(webApiClient); // Use in your application const portfolios = await portfolioService.getAll(); const portfolio = await portfolioService.create({ name: 'My Portfolio' }); ``` #### Mobile Application (with tokens) ```typescript // src/services/mobileApiClient.ts import axios from 'axios'; import * as SecureStore from 'expo-secure-store'; export const mobileApiClient = axios.create({ baseURL: process.env.API_URL, }); // Add auth token to requests mobileApiClient.interceptors.request.use(async config => { const token = await SecureStore.getItemAsync('authToken'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); ``` ```typescript // src/services/portfolios.ts import { mobileApiClient } from './mobileApiClient'; import { createPortfolioService } from '@cranberry-money/shared-services'; export const portfolioService = createPortfolioService(mobileApiClient); ``` ## ๐Ÿ“š Service Patterns ### Pattern 1: Service Factory (Recommended) Group related operations together: ```typescript import { createPortfolioService } from '@cranberry-money/shared-services'; import { webApiClient } from './webApiClient'; // or mobileApiClient for mobile const portfolioService = createPortfolioService(webApiClient); // All portfolio operations in one object await portfolioService.getAll({ page: 1 }); await portfolioService.getByUuid('123'); await portfolioService.create({ name: 'Tech Portfolio' }); await portfolioService.update('123', { name: 'Updated Portfolio' }); await portfolioService.delete('123'); ``` ### Pattern 2: Individual Functions For better tree-shaking and selective imports: ```typescript import { getPortfolios, createPortfolio } from '@cranberry-money/shared-services'; import { webApiClient } from './webApiClient'; // or mobileApiClient for mobile // Create platform-specific functions const getMyPortfolios = getPortfolios(webApiClient); const createMyPortfolio = createPortfolio(webApiClient); // Use them await getMyPortfolios({ page: 1 }); await createMyPortfolio({ name: 'New Portfolio' }); ``` ## ๐Ÿงช Testing Services are easy to test with mock API clients: ```typescript import { createPortfolioService } from '@cranberry-money/shared-services'; describe('Portfolio Service', () => { const mockApiClient = { get: jest.fn(), post: jest.fn(), patch: jest.fn(), delete: jest.fn(), }; const portfolioService = createPortfolioService(mockApiClient); it('should fetch portfolios', async () => { mockApiClient.get.mockResolvedValue({ data: { results: [], count: 0 }, }); const result = await portfolioService.getAll(); expect(mockApiClient.get).toHaveBeenCalledWith('/api/portfolios/', { params: undefined, }); }); }); ``` ## ๐Ÿ”„ React Query Integration Works seamlessly with React Query: ```typescript import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { portfolioService } from '@services/portfolios'; export const usePortfolios = (params?) => { return useQuery({ queryKey: ['portfolios', params], queryFn: () => portfolioService.getAll(params), staleTime: 5 * 60 * 1000, }); }; export const useCreatePortfolio = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: portfolioService.create, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['portfolios'] }); }, }); }; ``` ## ๐ŸŒ Available Services ### Financial Services - `createPortfolioService` - Portfolio management - `createAccountService` - Account operations - `createTradeService` - Trading operations - `createInstrumentService` - Financial instruments - `createWithdrawalService` - Withdrawal requests ### Banking Services - `createBankService` - Bank management - `createCashAccountService` - Cash accounts - `createTransactionService` - Transactions ### User & Compliance - `createAuthService` - Authentication - `createUserService` - User profiles - `createDocumentService` - Documents - `createTaxResidencyService` - Tax information - `createInvestmentPreferenceService` - Investment preferences ### Reference Data - `createCountryService` - Country data - `createIndustryService` - Industries - `createSectorService` - Sectors - `createStockExchangeService` - Stock exchanges ## ๐Ÿ“ฆ API Client Requirements Your API client must implement this interface: ```typescript interface ApiClient { get<T>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>; post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>; put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>; patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>; delete<T>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>; } ``` Any axios instance automatically satisfies this interface. ## ๐Ÿ”— Related Packages - `@cranberry-money/shared-types` - TypeScript type definitions - `@cranberry-money/shared-constants` - API endpoints and business constants - `@cranberry-money/shared-utils` - Utility functions for formatting and validation ## ๐Ÿ’ก Migration from Local Services If you're currently using local service implementations in your app: ### Before (Local Implementation) ```typescript // src/services/portfolios.ts import webApiClient from './webApiClient'; export const getPortfolios = (params?) => { return webApiClient.get('/api/portfolios/', { params }); }; ``` ### After (Shared Implementation) ```typescript // src/services/portfolios.ts import webApiClient from './webApiClient'; import { createPortfolioService } from '@cranberry-money/shared-services'; export const portfolioService = createPortfolioService(webApiClient); // or export const { getAll: getPortfolios } = createPortfolioService(webApiClient); ``` ## ๐Ÿ“„ License MIT License - see the [LICENSE](LICENSE) file for details. --- For complete documentation and architecture details, see the [Cranberry Development Guide](../../docs/CRANBERRY-DEVELOPMENT-GUIDE.md).