frontend/lib: reorganizing api calls before introducing query cache

Which means:

- Splitting types into another file
- Organizing internal code between what's local and what's not
- Adding a barrel file as well

Might evaluate to split even further, into different groups of files,
and combine it later, so for example have all mood-related calls in a
single place, users, auth, tokens, etc
Pedro Lucas Porcellis porcellis@eletrotupi.com 1 month ago d706da942de64ad47412e67cb183d34e5e1fd05e
Parents: 17dd47a
3 file(s) changed
  • frontend/lib/api/client.ts +43 -51
  • frontend/lib/api/index.ts +12 -0
  • frontend/lib/api/types.ts +73 -0
frontend/lib/api/client.ts
@@ -1,27 +1,21 @@
1 1 import * as SecureStore from 'expo-secure-store';
2 + import {
3 + User,
4 + AuthResponse,
5 + VerifyTokenResponse,
6 + SignUpPayload,
7 + ResetPasswordRequestResponse,
8 + ResetPasswordResponse,
9 + UserUpdatePayload,
10 + UserUpdateResponse,
11 + MoodComponentPayload,
12 + MoodEntryPayload,
13 + PaginatedResponse
14 + } from '@/lib/api/types';
2 15
3 16 const API_BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL;
4 17 const TOKEN_KEY = process.env.EXPO_PUBLIC_TOKEN_KEY; // Gary B.B Coleman fucking slaps
5 18
6 - export interface User {
7 - id: number;
8 - email: string;
9 - firstName: string;
10 - lastName?: string;
11 - updatedAt: Date;
12 - avatarKey?: string;
13 - avatarURL?: string;
14 - }
15 -
16 - export interface AuthResponse {
17 - token: string;
18 - user: User;
19 - }
20 -
21 - export interface UserUpdateResponse {
22 - user: User
23 - }
24 -
25 19 class ApiClient {
26 20 private async getStoredToken(): Promise<string | null> {
27 21 try {
@@ -49,6 +43,11 @@ console.error('Failed to remove token:', error);
49 43 }
50 44 }
51 45
46 + async getStoredAuthData(): Promise<{ token: string | null }> {
47 + const token = await this.getStoredToken();
48 + return { token };
49 + }
50 +
52 51 private async request<T>(
53 52 endpoint: string,
54 53 options: RequestInit = {}
@@ -79,22 +78,7 @@
79 78 return response.json();
80 79 }
81 80
82 - async avatar(formData: FormData): Promise<void> {
83 - return await this.request(`/users/me/avatar`, {
84 - method: 'POST',
85 - body: formData,
86 - headers: {
87 - 'Content-Type': 'multipart/form-data',
88 - } as any,
89 - })
90 - }
91 -
92 - async removeAvatar(): Promise<void> {
93 - return await this.request(`/users/me/avatar`, {
94 - method: 'DELETE'
95 - });
96 - }
97 -
81 + // Auth
98 82 async login(email: string, password: string): Promise<AuthResponse> {
99 83 const response = await this.request<AuthResponse>('/auth/login', {
100 84 method: 'POST',
@@ -105,36 +89,31 @@ await this.storeToken(response.token);
105 89 return response;
106 90 }
107 91
108 - async signup(userData: {
109 - first_name: string;
110 - last_name?: string;
111 - email: string;
112 - password: string;
113 - }): Promise<AuthResponse> {
92 + async signup(data: SignUpPayload): Promise<AuthResponse> {
114 93 const response = await this.request<AuthResponse>('/users', {
115 94 method: 'POST',
116 - body: JSON.stringify(userData),
95 + body: JSON.stringify(data),
117 96 });
118 97
119 98 await this.storeToken(response.token);
120 99 return response;
121 100 }
122 101
123 - async forgotPassword(email: string): Promise<{ message: string; token?: string }> {
102 + async forgotPassword(email: string): Promise<ResetPasswordRequestResponse> {
124 103 return this.request('/auth/forgot-password', {
125 104 method: 'POST',
126 105 body: JSON.stringify({ email }),
127 106 });
128 107 }
129 108
130 - async resetPassword(token: string, password: string): Promise<{ message: string }> {
109 + async resetPassword(token: string, password: string): Promise<ResetPasswordResponse> {
131 110 return this.request('/auth/reset-password', {
132 111 method: 'POST',
133 112 body: JSON.stringify({ token, newPassword: password }),
134 113 });
135 114 }
136 115
137 - async verifyToken(): Promise<{ valid: boolean; userId?: number; email?: string }> {
116 + async verifyToken(): Promise<VerifyTokenResponse> {
138 117 try {
139 118 return await this.request('/auth/verify');
140 119 } catch (error) {
@@ -148,18 +127,31 @@ async logout(): Promise<void> {
148 127 await this.removeToken();
149 128 }
150 129
151 - async getStoredAuthData(): Promise<{ token: string | null }> {
152 - const token = await this.getStoredToken();
153 - return { token };
154 - }
155 -
156 - async updateUser(user): Promise<void> {
130 + // User-related
131 + async updateUser(user: UserUpdatePayload): Promise<User> {
157 132 return await this.request(`/users/${user.id}`, {
158 133 method: 'PUT',
159 134 body: JSON.stringify({ user })
160 135 })
161 136 }
162 137
138 + async avatar(formData: FormData): Promise<void> {
139 + return await this.request(`/users/me/avatar`, {
140 + method: 'POST',
141 + body: formData,
142 + headers: {
143 + 'Content-Type': 'multipart/form-data',
144 + } as any,
145 + })
146 + }
147 +
148 + async removeAvatar(): Promise<void> {
149 + return await this.request(`/users/me/avatar`, {
150 + method: 'DELETE'
151 + });
152 + }
153 +
154 + // Mood-related
163 155 async createMoodEntry(moodEntry): Promise<void> {
164 156 return await this.request(`/moods`, {
165 157 method: 'POST',
frontend/lib/api/index.ts
@@ -0,0 +1,12 @@
1 + export {
2 + apiClient
3 + } from '@/lib/api/client';
4 +
5 + export type {
6 + User,
7 + AuthResponse,
8 + MoodEntryPayload,
9 + MoodComponentPayload,
10 + PaginatedResponse,
11 + UserUpdateResponse
12 + } from '@/lib/api/types';
frontend/lib/api/types.ts
@@ -0,0 +1,73 @@
1 + export interface User {
2 + id: number;
3 + email: string;
4 + firstName: string;
5 + lastName?: string;
6 + updatedAt: Date;
7 + avatarKey?: string;
8 + avatarURL?: string;
9 + };
10 +
11 + // Auth
12 + export interface AuthResponse {
13 + token: string;
14 + user: User;
15 + };
16 +
17 + export interface VerifyTokenResponse {
18 + valid: boolean;
19 + userId?: number;
20 + email?: string;
21 + user: Pick<User, 'firstName' | 'lastName'>;
22 + };
23 +
24 + export interface SignUpPayload {
25 + firstName: string;
26 + lastName?: string;
27 + email: string;
28 + password: string;
29 + };
30 +
31 + export interface ResetPasswordRequestResponse {
32 + message: string;
33 + token?: string;
34 + };
35 +
36 + export interface ResetPasswordResponse {
37 + message: string;
38 + };
39 +
40 + // User
41 + export interface UserUpdatePayload extends Partial<User> {
42 + id: number;
43 + }
44 +
45 + export interface UserUpdateResponse {
46 + user: User
47 + };
48 +
49 + // Mood entries
50 + export interface MoodComponentPayload {
51 + component: string;
52 + intensity: string;
53 + };
54 +
55 +
56 + export interface MoodEntryPayload {
57 + annotation: string;
58 + moment: Date;
59 + selectedMood: string;
60 + anxietyLevel: number;
61 + energyLevel: number;
62 + stressLevel: number;
63 + moodComponents: MoodComponentPayload[]
64 + };
65 +
66 +
67 + // Generic stuff
68 + export interface PaginatedResponse<T> {
69 + entries: T[];
70 + total: number;
71 + page: number;
72 + nextPage: number | null;
73 + };