| 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. Use when this capability is needed. |
| metadata | {"author":"doitsu2014"} |
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 fetch
src/infrastructure/graphQL/graphql-client.ts - Apollo Client setup
src/infrastructure/utilities.auth.ts - Authentication utilities
src/infrastructure/utilities.ts - General utility functions
Authentication
src/auth/AuthContext.tsx - Auth context provider
src/auth/ProtectedRoute.tsx - Route protection
src/auth/keycloak.ts - Keycloak configuration
Data Access
src/infrastructure/das/categories.das.ts - Category data access service
src/infrastructure/graphQL/queries/categories/ - GraphQL queries
REST API Setup
Configuration
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
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();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
export const graphqlClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
Writing GraphQL Queries
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',
});
Authentication
Keycloak Integration
The app uses Keycloak for authentication:
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
import { useAuth } from '@/auth/AuthContext';
function MyComponent() {
const { token, isAuthenticated, login, logout } = useAuth();
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 posts
GET /posts/:id - Get single post
POST /posts - Create new post
PUT /posts/:id - Update post
DELETE /posts/:id - Delete post
Categories
GET /categories - List all categories
GET /categories/:id - Get single category
POST /categories - Create new category
PUT /categories/:id - Update category
DELETE /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,
}),
}
);
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) {
logout();
} else if (response.status === 404) {
console.error('Resource not found');
} else {
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);
}
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
Converted and distributed by TomeVault — claim your Tome and manage your conversions.