Overview
The T1 Component Library provides a TypeScript-based API client for tracking component interactions, managing authentication, and fetching usage statistics. The client is built on top of the native Fetch API and integrates seamlessly with TanStack React Query for data fetching and caching.
Installation
The client dependencies are already included in the project:
{
"dependencies" : {
"@tanstack/react-query" : "^5.90.11" ,
"next" : "16.0.4" ,
"react" : "19.2.0"
}
}
Configuration
Environment Variables
Configure the API base URL using the NEXT_PUBLIC_API_URL environment variable:
NEXT_PUBLIC_API_URL = http://localhost:3001/api
The NEXT_PUBLIC_ prefix is required for Next.js to expose the variable to the browser.
If not set, the client defaults to http://localhost:3001/api:
const API_URL = process . env . NEXT_PUBLIC_API_URL || 'http://localhost:3001/api' ;
API Client Structure
The API client is defined in client/app/lib/api.ts and exports a single api object with methods for all API operations:
export const api = {
trackInteraction : async ( payload : TrackingPayload ) : Promise < TrackingResponse > => { },
getStats : async () : Promise < StatsResponse > => { },
getExportView : async ( params : ExportViewParams ) : Promise < ExportViewResponse > => { },
getExportData : async ( token : string ) : Promise < ExportResponse > => { },
getHealthCheck : async () : Promise < HealthCheckResponse > => { }
};
Making Requests
Public Endpoints
Public endpoints don’t require authentication:
import { api } from '@/lib/api' ;
// Track a component interaction
const response = await api . trackInteraction ({
nombre: 'Button' ,
accion: 'click' ,
tipo_usuario: 'anonymous'
});
// Get usage statistics
const stats = await api . getStats ();
// Check server health
const health = await api . getHealthCheck ();
Authenticated Requests
Authenticated endpoints require a JWT token in the Authorization header:
// Get paginated export data
const exportView = await api . getExportView ({
page: 1 ,
limit: 10 ,
token: userToken
});
// Get all export data
const exportData = await api . getExportData ( userToken );
The token is automatically included in the Authorization header:
const response = await fetch ( ` ${ API_URL } /components/export/view? ${ queryParams } ` , {
headers: {
'Authorization' : `Bearer ${ token } ` ,
},
});
Using TanStack React Query
The recommended way to interact with the API is through TanStack React Query, which provides caching, refetching, and state management.
Setup Query Client
Wrap your application with QueryClientProvider:
import { QueryClient , QueryClientProvider } from '@tanstack/react-query' ;
const queryClient = new QueryClient ();
export default function RootLayout ({ children }) {
return (
< QueryClientProvider client = { queryClient } >
{ children }
</ QueryClientProvider >
);
}
Query Example
Use useQuery to fetch data with automatic caching and refetching:
context/InteractionContext.tsx:41-54
const {
data : serverStats ,
isLoading : isLoadingServerStats ,
isRefetching : isRefetchingServerStats ,
} = useQuery ({
queryKey: [ 'stats' ],
queryFn : async () => {
const response = await api . getStats ();
return response . data ;
},
refetchOnWindowFocus: true ,
staleTime: 3000 ,
});
Query Options
queryKey : Unique identifier for caching (e.g., ['stats'], ['export', page])
queryFn : Async function that returns data
refetchOnWindowFocus : Refetch when window regains focus
staleTime : Time in ms before data is considered stale (default: 0)
refetchInterval : Poll data at regular intervals (optional)
Mutation Example
Use useMutation for operations that modify data:
context/InteractionContext.tsx:56-71
const trackMutation = useMutation ({
mutationFn : ( params : { nombre : string ; accion : string }) => {
return api . trackInteraction ({
nombre: params . nombre ,
accion: params . accion ,
tipo_usuario: isAuthenticated ? 'registered' : 'anonymous' ,
usuario: isAuthenticated ? user ?. id : undefined ,
});
},
onSuccess : () => {
queryClient . invalidateQueries ({ queryKey: [ 'stats' ] });
},
onError : ( error ) => {
console . error ( 'Error al enviar interacción:' , error );
},
});
Then call the mutation:
trackMutation . mutate ({ nombre: 'Button' , accion: 'click' });
Mutation Options
mutationFn : Async function that performs the mutation
onSuccess : Callback after successful mutation (e.g., invalidate queries)
onError : Callback on error (e.g., show error message)
onSettled : Callback that runs regardless of success or failure
Client-Side Error Handling
Basic Error Handling
All API methods throw errors when the response is not OK:
if ( ! response . ok ) {
throw new Error ( 'Error al registrar interacción' );
}
Handle errors using try-catch:
try {
const stats = await api . getStats ();
console . log ( stats . data );
} catch ( error ) {
console . error ( 'Failed to fetch stats:' , error );
}
Error Handling with React Query
React Query provides built-in error handling:
const { data , error , isError } = useQuery ({
queryKey: [ 'stats' ],
queryFn : () => api . getStats ()
});
if ( isError ) {
return < div > Error : {error. message } </ div > ;
}
For mutations:
const mutation = useMutation ({
mutationFn: api . trackInteraction ,
onError : ( error ) => {
// Show toast notification
toast . error ( `Failed to track interaction: ${ error . message } ` );
}
});
Handling HTTP Status Codes
The API returns standard HTTP status codes:
200 : Success
201 : Resource created
400 : Validation error
401 : Unauthorized (invalid/missing token)
404 : Resource not found
500 : Internal server error
503 : Service unavailable (database disconnected)
You can check the response status before parsing:
const response = await fetch ( ` ${ API_URL } /components/track` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ( payload ),
});
if ( response . status === 401 ) {
// Redirect to login
window . location . href = '/login' ;
} else if ( response . status === 400 ) {
const error = await response . json ();
// Show validation error
console . error ( 'Validation error:' , error . message );
} else if ( ! response . ok ) {
throw new Error ( 'Unexpected error' );
}
TypeScript Types
The client provides full TypeScript support with exported interfaces:
export interface TrackingPayload {
nombre : string ;
accion : string ;
tipo_usuario : 'anonymous' | 'registered' ;
usuario ?: string ;
}
export interface StatsResponse {
success : boolean ;
data : {
totalInteracciones : number ;
porComponente : Array <{ _id : string ; count : number }>;
porAccion : Array <{ _id : { componente : string ; accion : string }; count : number }>;
porTipoUsuario : Array <{ _id : string ; count : number }>;
};
}
export interface ExportViewParams {
page ?: number ;
limit ?: number ;
token : string ;
}
See lib/api.ts for all available types.
Best Practices
1. Use React Query for All API Calls
// Good - Automatic caching and refetching
const { data } = useQuery ({
queryKey: [ 'stats' ],
queryFn: api . getStats
});
// Avoid - Manual state management
const [ data , setData ] = useState ();
useEffect (() => {
api . getStats (). then ( setData );
}, []);
2. Invalidate Queries After Mutations
const mutation = useMutation ({
mutationFn: api . trackInteraction ,
onSuccess : () => {
// Refetch stats after tracking interaction
queryClient . invalidateQueries ({ queryKey: [ 'stats' ] });
}
});
3. Handle Loading and Error States
const { data , isLoading , isError , error } = useQuery ({
queryKey: [ 'stats' ],
queryFn: api . getStats
});
if ( isLoading ) return < Spinner />;
if ( isError ) return < ErrorMessage error ={ error } />;
return < StatsDisplay data ={ data } />;
4. Use Optimistic Updates
const mutation = useMutation ({
mutationFn: api . trackInteraction ,
onMutate : async ( newInteraction ) => {
// Cancel outgoing refetches
await queryClient . cancelQueries ({ queryKey: [ 'stats' ] });
// Snapshot previous value
const previous = queryClient . getQueryData ([ 'stats' ]);
// Optimistically update
queryClient . setQueryData ([ 'stats' ], ( old ) => ({
... old ,
totalInteracciones: old . totalInteracciones + 1
}));
return { previous };
},
onError : ( err , variables , context ) => {
// Rollback on error
queryClient . setQueryData ([ 'stats' ], context . previous );
}
});
Error Handling Learn about standard error responses and HTTP status codes
Authentication Configure JWT authentication for protected endpoints