| name | api-integration |
| description | Work with REST and GraphQL APIs, authentication, API configuration, and data fetching. Use when implementing API calls, debugging network requests, setting up Apollo Client, or handling authentication. |
API Integration
Overview
This skill helps you work with API integrations in the admin_side application. The project uses both REST API and GraphQL with Apollo Client, secured by Keycloak authentication.
Key Files
API Configuration
src/config/api.config.ts- REST API configuration and authenticated fetchsrc/infrastructure/graphQL/graphql-client.ts- Apollo Client setupsrc/infrastructure/utilities.auth.ts- Authentication utilitiessrc/infrastructure/utilities.ts- General utility functions
Authentication
src/auth/AuthContext.tsx- Auth context providersrc/auth/ProtectedRoute.tsx- Route protectionsrc/auth/keycloak.ts- Keycloak configuration
Data Access
src/infrastructure/das/categories.das.ts- Category data access servicesrc/infrastructure/graphQL/queries/categories/- GraphQL queries
REST API Setup
Configuration
// src/config/api.config.ts
export const getApiUrl = (path: string) => {
const baseUrl = import.meta.env.PUBLIC_REST_API_URL;
return `${baseUrl}${path}`;
};
Authenticated Requests
export const authenticatedFetch = async (
url: string,
token: string | null,
options: RequestInit = {}
): Promise<Response> => {
const headers = {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
...options.headers,
};
return fetch(url, {
...options,
headers,
});
};
Environment Variables
REST API URL is configured via environment variable:
PUBLIC_REST_API_URL=http://localhost:4000/api
GraphQL Integration
Apollo Client Setup
// src/infrastructure/graphQL/graphql-client.ts
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
const httpLink = createHttpLink({
uri: import.meta.env.PUBLIC_GRAPHQL_API_URL,
});
const authLink = setContext((_, { headers }) => {
const token = getAuthToken(); // Get from storage
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
export const graphqlClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
Writing GraphQL Queries
// src/infrastructure/graphQL/queries/categories/get-categories.ts
import { gql } from '@apollo/client';
export const GET_CATEGORIES = gql`
query GetCategories {
categories {
id
name
slug
description
posts {
id
title
}
}
}
`;
Using GraphQL Queries
import { graphqlClient } from '@/infrastructure/graphQL/graphql-client';
import { GET_CATEGORIES } from '@/infrastructure/graphQL/queries/categories/get-categories';
const { data, loading, error } = await graphqlClient.query({
query: GET_CATEGORIES,
fetchPolicy: 'network-only', // or 'cache-first', 'cache-only'
});
Authentication
Keycloak Integration
The app uses Keycloak for authentication:
// src/auth/keycloak.ts
import Keycloak from 'keycloak-js';
const keycloak = new Keycloak({
url: import.meta.env.PUBLIC_KEYCLOAK_URL,
realm: import.meta.env.PUBLIC_KEYCLOAK_REALM,
clientId: import.meta.env.PUBLIC_KEYCLOAK_CLIENT_ID,
});
export default keycloak;
Auth Context
// Usage in components
import { useAuth } from '@/auth/AuthContext';
function MyComponent() {
const { token, isAuthenticated, login, logout } = useAuth();
// Use token for API calls
const response = await authenticatedFetch(url, token);
}
Protected Routes
import ProtectedRoute from '@/auth/ProtectedRoute';
<ProtectedRoute>
<AdminDashboard />
</ProtectedRoute>
API Endpoints Reference
Blog Posts
GET /posts- List all postsGET /posts/:id- Get single postPOST /posts- Create new postPUT /posts/:id- Update postDELETE /posts/:id- Delete post
Categories
GET /categories- List all categoriesGET /categories/:id- Get single categoryPOST /categories- Create new categoryPUT /categories/:id- Update categoryDELETE /categories/:id- Delete category
Common Patterns
Fetching Data
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const { token } = useAuth();
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await authenticatedFetch(
getApiUrl('/posts'),
token
);
if (response.ok) {
const result = await response.json();
setData(result.data);
} else {
throw new Error('Failed to fetch');
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [token]);
Creating Data
const handleCreate = async (formData) => {
try {
const response = await authenticatedFetch(
getApiUrl('/posts'),
token,
{
method: 'POST',
body: JSON.stringify(formData),
}
);
if (response.ok) {
const result = await response.json();
navigate('/admin/posts');
} else {
const error = await response.json();
console.error('Create failed:', error);
}
} catch (err) {
console.error('Network error:', err);
}
};
Updating Data
const handleUpdate = async (id, formData) => {
try {
const response = await authenticatedFetch(
getApiUrl(`/posts/${id}`),
token,
{
method: 'PUT',
body: JSON.stringify({
...formData,
rowVersion, // Include for optimistic locking
}),
}
);
if (response.ok) {
navigate('/admin/posts');
}
} catch (err) {
console.error('Update error:', err);
}
};
Error Handling
REST API Errors
const response = await authenticatedFetch(url, token);
if (!response.ok) {
if (response.status === 401) {
// Unauthorized - redirect to login
logout();
} else if (response.status === 404) {
// Not found
console.error('Resource not found');
} else {
// Other errors
const error = await response.json();
console.error('API error:', error);
}
}
GraphQL Errors
const { data, error } = await graphqlClient.query({
query: GET_CATEGORIES,
});
if (error) {
console.error('GraphQL error:', error.message);
// Handle network errors, GraphQL errors, etc.
}
Best Practices
- Always use authenticatedFetch for REST API calls
- Include Bearer token in all authenticated requests
- Handle loading states appropriately
- Catch and display errors to users
- Use environment variables for API URLs
- Implement retry logic for failed requests when appropriate
- Cache GraphQL queries strategically
- Validate responses before using data
- Handle 401 errors by redirecting to login
- Include rowVersion in update operations
Debugging API Issues
- Check Network Tab: Verify request/response in browser DevTools
- Validate Token: Ensure auth token is valid and not expired
- Check CORS: Verify server allows requests from your origin
- Review Headers: Confirm Authorization header is set correctly
- Test Endpoints: Use Postman/curl to test API directly
- Check Environment Variables: Verify API URLs are correct
- Review Apollo Cache: Clear cache if seeing stale data
- Monitor Console: Check for error messages and warnings
Environment Variables
Required environment variables:
# REST API
PUBLIC_REST_API_URL=http://localhost:4000/api
# GraphQL API
PUBLIC_GRAPHQL_API_URL=http://localhost:4000/graphql
# Keycloak
PUBLIC_KEYCLOAK_URL=http://localhost:8080
PUBLIC_KEYCLOAK_REALM=your-realm
PUBLIC_KEYCLOAK_CLIENT_ID=your-client-id
Example Workflow
When implementing a new API integration:
- Review existing API configuration
- Check authentication setup
- Define TypeScript types for request/response
- Implement API call with error handling
- Add loading states to UI
- Test with valid and invalid data
- Handle edge cases (network errors, timeouts)
- Add appropriate user feedback