Skip to content

Commit aac2ed5

Browse files
Merge pull request #332 from anumukul/feat/integrate-react-query
feat: integrate TanStack React Query for auth mutations
2 parents 931a109 + d1018c3 commit aac2ed5

File tree

9 files changed

+236
-9
lines changed

9 files changed

+236
-9
lines changed

frontend/app/layout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Metadata } from "next";
22
import { Geist, Geist_Mono } from "next/font/google";
33
import "./globals.css";
4+
import { Providers } from "./providers";
45

56
const geistSans = Geist({
67
variable: "--font-geist-sans",
@@ -27,8 +28,8 @@ export default function RootLayout({
2728
<body
2829
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
2930
>
30-
{children}
31+
<Providers>{children}</Providers>
3132
</body>
3233
</html>
3334
);
34-
}
35+
}

frontend/app/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ export default function Home() {
44
<h1>HomePage</h1>
55
</section>
66
);
7-
}
7+
}

frontend/app/providers.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use client';
2+
3+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
4+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
5+
import { useState, ReactNode } from 'react';
6+
7+
interface ProvidersProps {
8+
children: ReactNode;
9+
}
10+
11+
/**
12+
* Providers component that wraps the app with React Query context
13+
* Includes dev tools for debugging in development mode
14+
*/
15+
export function Providers({ children }: ProvidersProps) {
16+
const [queryClient] = useState(
17+
() =>
18+
new QueryClient({
19+
defaultOptions: {
20+
queries: {
21+
staleTime: 60 * 1000,
22+
refetchOnWindowFocus: false,
23+
},
24+
mutations: {
25+
onError: (error) => {
26+
console.error('Mutation error:', error);
27+
},
28+
},
29+
},
30+
})
31+
);
32+
33+
return (
34+
<QueryClientProvider client={queryClient}>
35+
{children}
36+
{process.env.NODE_ENV === 'development' && (
37+
<ReactQueryDevtools initialIsOpen={false} />
38+
)}
39+
</QueryClientProvider>
40+
);
41+
}

frontend/lib/api/client.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { RegisterInput, LoginInput, AuthResponse } from '../query/types';
2+
3+
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api';
4+
5+
/**
6+
* API Client for making HTTP requests
7+
* Handles authentication endpoints with proper error handling
8+
*/
9+
class ApiClient {
10+
private async request<T>(
11+
endpoint: string,
12+
options?: RequestInit
13+
): Promise<T> {
14+
const url = `${API_BASE_URL}${endpoint}`;
15+
16+
const response = await fetch(url, {
17+
...options,
18+
headers: {
19+
'Content-Type': 'application/json',
20+
...options?.headers,
21+
},
22+
});
23+
24+
if (!response.ok) {
25+
const error = await response.json().catch(() => ({
26+
message: 'An error occurred',
27+
statusCode: response.status,
28+
}));
29+
throw error;
30+
}
31+
32+
return response.json();
33+
}
34+
35+
async register(data: RegisterInput): Promise<AuthResponse> {
36+
return this.request<AuthResponse>('/auth/register', {
37+
method: 'POST',
38+
body: JSON.stringify(data),
39+
});
40+
}
41+
42+
async login(data: LoginInput): Promise<AuthResponse> {
43+
return this.request<AuthResponse>('/auth/login', {
44+
method: 'POST',
45+
body: JSON.stringify(data),
46+
});
47+
}
48+
}
49+
50+
export const apiClient = new ApiClient();

frontend/lib/query/keys.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Centralized query and mutation keys for React Query
3+
* This ensures consistent cache management across the application
4+
*/
5+
export const queryKeys = {
6+
auth: {
7+
register: ['auth', 'register'] as const,
8+
login: ['auth', 'login'] as const,
9+
},
10+
} as const;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
2+
import { apiClient } from '@/lib/api/client';
3+
import { queryKeys } from '../keys';
4+
import {
5+
RegisterInput,
6+
LoginInput,
7+
AuthResponse,
8+
ApiError,
9+
} from '../types';
10+
11+
/**
12+
* Mutation hook for user registration
13+
* @param options - Optional mutation options for callbacks and config
14+
* @returns Mutation object with mutate, isPending, isError, etc.
15+
*/
16+
export function useRegisterMutation(
17+
options?: UseMutationOptions<AuthResponse, ApiError, RegisterInput>
18+
) {
19+
return useMutation<AuthResponse, ApiError, RegisterInput>({
20+
mutationKey: queryKeys.auth.register,
21+
mutationFn: (data: RegisterInput) => apiClient.register(data),
22+
...options,
23+
});
24+
}
25+
26+
/**
27+
* Mutation hook for user login
28+
* @param options - Optional mutation options for callbacks and config
29+
* @returns Mutation object with mutate, isPending, isError, etc.
30+
*/
31+
export function useLoginMutation(
32+
options?: UseMutationOptions<AuthResponse, ApiError, LoginInput>
33+
) {
34+
return useMutation<AuthResponse, ApiError, LoginInput>({
35+
mutationKey: queryKeys.auth.login,
36+
mutationFn: (data: LoginInput) => apiClient.login(data),
37+
...options,
38+
});
39+
}

frontend/lib/query/types.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* TypeScript types for authentication mutations
3+
*/
4+
5+
export interface RegisterInput {
6+
email: string;
7+
password: string;
8+
name: string;
9+
}
10+
11+
export interface LoginInput {
12+
email: string;
13+
password: string;
14+
}
15+
16+
export interface AuthResponse {
17+
token: string;
18+
user: {
19+
id: string;
20+
email: string;
21+
name: string;
22+
};
23+
}
24+
25+
export interface ApiError {
26+
message: string;
27+
statusCode: number;
28+
errors?: Record<string, string[]>;
29+
}

frontend/package-lock.json

Lines changed: 55 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,21 @@
99
"lint": "eslint"
1010
},
1111
"dependencies": {
12+
"@tanstack/react-query": "^5.90.2",
13+
"@tanstack/react-query-devtools": "^5.90.2",
14+
"next": "15.5.4",
1215
"react": "19.1.0",
13-
"react-dom": "19.1.0",
14-
"next": "15.5.4"
16+
"react-dom": "19.1.0"
1517
},
1618
"devDependencies": {
17-
"typescript": "^5",
19+
"@eslint/eslintrc": "^3",
20+
"@tailwindcss/postcss": "^4",
1821
"@types/node": "^20",
1922
"@types/react": "^19",
2023
"@types/react-dom": "^19",
21-
"@tailwindcss/postcss": "^4",
22-
"tailwindcss": "^4",
2324
"eslint": "^9",
2425
"eslint-config-next": "15.5.4",
25-
"@eslint/eslintrc": "^3"
26+
"tailwindcss": "^4",
27+
"typescript": "^5"
2628
}
2729
}

0 commit comments

Comments
 (0)