
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@akadenia/api
Advanced tools
@akadenia/apiAn opinionated Axios wrapper that gives every HTTP request a consistent response shape, built-in retry logic, and typed error handling — so you stop writing the same boilerplate across every project.
npm install @akadenia/api
import { AxiosApiClient } from "@akadenia/api";
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer your-token"
}
});
// GET request
const response = await client.get("/users");
if (response.success) {
console.log("Users:", response.data);
} else {
console.error("Error:", response.message);
}
// Fetch a single user
const userResponse = await client.get<User>("/users/1");
if (userResponse.success) {
const user = userResponse.data;
console.log(`${user.firstName} ${user.lastName}`);
}
// Fetch with query parameters
const usersResponse = await client.get<User[]>("/users", {
params: { page: 1, limit: 10 }
});
// Fetch with custom headers
const response = await client.get("/protected-resource", {
headers: { "X-Custom-Header": "value" }
});
// Fetch with response type configuration
const pdfResponse = await client.get("/documents/report.pdf", {
responseType: "blob"
});
// Create a new user
interface CreateUserRequest {
firstName: string;
lastName: string;
email: string;
}
const newUser: CreateUserRequest = {
firstName: "John",
lastName: "Doe",
email: "john@example.com"
};
const response = await client.post<User, CreateUserRequest>("/users", newUser);
if (response.success) {
console.log("User created:", response.data);
} else {
console.error("Creation failed:", response.message);
}
// POST with form data
const formData = new FormData();
formData.append("file", fileInput.files[0]);
formData.append("description", "User avatar");
const uploadResponse = await client.post("/upload", formData, {
headers: { "Content-Type": "multipart/form-data" }
});
// Update an existing user
const updatedUser = {
firstName: "Jane",
lastName: "Smith"
};
const response = await client.put<User>("/users/1", updatedUser);
if (response.success) {
console.log("User updated:", response.data);
}
// Partial update
const partialUpdate = {
firstName: "Jane"
};
const response = await client.patch<User>("/users/1", partialUpdate);
if (response.success) {
console.log("User partially updated:", response.data);
}
// Delete a user
const response = await client.delete("/users/1");
if (response.success) {
console.log("User deleted successfully");
} else {
console.error("Deletion failed:", response.message);
}
// Delete with confirmation
const deleteResponse = await client.delete("/users/1", {
data: { confirm: true, reason: "User requested deletion" }
});
// Initialize with token
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
headers: {
"Authorization": "Bearer your-jwt-token"
}
});
// Update token dynamically
client.setHeader("Authorization", "Bearer new-token");
// Handle token expiration
try {
const response = await client.get("/protected-endpoint");
if (!response.success && response.message === "Unauthorized") {
// Refresh token logic
const newToken = await refreshToken();
client.setHeader("Authorization", `Bearer ${newToken}`);
// Retry the request
return await client.get("/protected-endpoint");
}
return response;
} catch (error) {
console.error("Authentication failed:", error);
}
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
headers: {
"X-API-Key": "your-api-key",
"X-API-Version": "v1"
}
});
// Rotate API keys
client.setHeader("X-API-Key", "new-api-key");
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
headers: {
"Authorization": "Basic " + btoa("username:password")
}
});
// Define your data types
interface User {
id: number;
firstName: string;
lastName: string;
email: string;
createdAt: string;
}
interface CreateUserRequest {
firstName: string;
lastName: string;
email: string;
}
interface UpdateUserRequest {
firstName?: string;
lastName?: string;
email?: string;
}
interface PaginatedResponse<T> {
data: T[];
total: number;
page: number;
limit: number;
}
// Use generics for type safety
const response = await client.post<User, CreateUserRequest>("/users", {
firstName: "John",
lastName: "Doe",
email: "john@example.com"
});
if (response.success) {
// TypeScript knows this is a User
const user: User = response.data;
console.log(user.firstName); // ✅ Type-safe
}
// Paginated responses
const usersResponse = await client.get<PaginatedResponse<User>>("/users", {
params: { page: 1, limit: 10 }
});
if (usersResponse.success) {
const { data: users, total, page } = usersResponse.data;
console.log(`Page ${page}: ${users.length} of ${total} users`);
}
// Set headers during initialization
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer initial-token"
}
});
// Set headers dynamically
client.setHeader("X-API-Key", "your-api-key");
client.setHeader("Authorization", "Bearer new-token");
// Headers are automatically included in all requests
const response = await client.get("/protected-endpoint");
// Override headers for specific requests
const responseWithCustomHeaders = await client.get("/special-endpoint", {
headers: { "X-Custom-Header": "override-value" }
});
try {
const response = await client.get("/users/999");
if (response.success) {
console.log("User found:", response.data);
} else {
// Handle different error types
switch (response.message) {
case "Resource Not Found":
console.log("User doesn't exist");
break;
case "Unauthorized":
console.log("Please log in again");
break;
case "Network Error":
console.log("Check your internet connection");
break;
case "Bad Request":
console.log("Invalid request parameters");
break;
case "Internal Server Error":
console.log("Server error, please try again later");
break;
default:
console.error("Unexpected error:", response.message);
}
}
} catch (error) {
console.error("Request failed:", error);
}
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
retries: 5, // Retry up to 5 times
retryDelay: (retryCount) => {
// Exponential backoff: 1s, 2s, 4s, 8s, 16s
return Math.pow(2, retryCount) * 1000;
},
onRetry: (retryCount, error) => {
console.log(`Retry attempt ${retryCount} for error: ${error.message}`);
// You could log to monitoring service here
}
});
// Linear backoff
const linearClient = new AxiosApiClient({
baseUrl: "https://api.example.com",
retries: 3,
retryDelay: (retryCount) => retryCount * 1000, // 1s, 2s, 3s
});
// Custom retry logic
const customRetryClient = new AxiosApiClient({
baseUrl: "https://api.example.com",
retries: 3,
retryDelay: (retryCount) => {
if (retryCount === 1) return 1000; // 1s
if (retryCount === 2) return 5000; // 5s
return 10000; // 10s
}
});
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
timeout: 10000, // 10 seconds
retries: 2
});
// For long-running operations, you might want a longer timeout
const longRunningClient = new AxiosApiClient({
baseUrl: "https://api.example.com",
timeout: 60000, // 1 minute
retries: 1
});
// For quick operations
const quickClient = new AxiosApiClient({
baseUrl: "https://api.example.com",
timeout: 5000, // 5 seconds
retries: 0
});
// JSON responses
const jsonResponse = await client.get("/users");
// File downloads
const fileResponse = await client.get("/files/document.pdf", {
responseType: "blob"
});
// Form data
const formResponse = await client.post("/upload", formData, {
headers: { "Content-Type": "multipart/form-data" }
});
// Array buffer responses
const bufferResponse = await client.get("/files/image.jpg", {
responseType: "arraybuffer"
});
// Stream responses
const streamResponse = await client.get("/files/large-file.zip", {
responseType: "stream"
});
// Access the underlying axios instance for advanced use cases
const axiosInstance = client.getInstance();
// You can still use axios interceptors
axiosInstance.interceptors.request.use((config) => {
console.log("Request:", config.method?.toUpperCase(), config.url);
return config;
});
axiosInstance.interceptors.response.use((response) => {
console.log("Response:", response.status, response.config.url);
return response;
});
// Or use axios directly for one-off requests
const directResponse = await axiosInstance.get("/special-endpoint");
// Custom axios configuration
const customAxiosInstance = axios.create({
baseURL: "https://api.example.com",
timeout: 15000,
validateStatus: (status) => status < 500 // Don't reject if status is less than 500
});
const client = new AxiosApiClient({
baseUrl: "https://api.example.com"
});
const axiosInstance = client.getInstance();
// Request interceptor for logging
axiosInstance.interceptors.request.use((config) => {
console.log(`[${new Date().toISOString()}] ${config.method?.toUpperCase()} ${config.url}`);
return config;
});
// Response interceptor for data transformation
axiosInstance.interceptors.response.use((response) => {
// Transform response data if needed
if (response.data && response.data.items) {
response.data = response.data.items;
}
return response;
});
const client = new AxiosApiClient({
baseUrl: "https://api.example.com"
});
const axiosInstance = client.getInstance();
// Custom error interceptor
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 429) {
// Rate limiting - wait and retry
return new Promise(resolve => {
setTimeout(() => {
resolve(axiosInstance.request(error.config));
}, 1000);
});
}
return Promise.reject(error);
}
);
All responses follow this consistent structure:
// Success Response
{
success: true,
data: T, // Your actual data
status: number, // HTTP status code
statusText: string, // HTTP status text
headers: object, // Response headers
config: object, // Request configuration
request: object // Request object
}
// Error Response
{
success: false,
message: string, // Human-readable error message
data?: any, // Error response data (if available)
error: any, // Original error object
status?: number, // HTTP status code (if available)
statusText?: string // HTTP status text (if available)
}
The library provides standardized error messages for common HTTP status codes:
400 - Bad Request401 - Unauthorized403 - Forbidden404 - Resource Not Found422 - Unprocessable Entity429 - Too Many Requests500 - Internal Server Error502 - Bad Gateway503 - Service Unavailable504 - Gateway Timeout// Pattern 1: Check success flag
const response = await client.get("/users");
if (response.success) {
return response.data;
} else {
throw new Error(response.message);
}
// Pattern 2: Try-catch with success check
try {
const response = await client.get("/users");
if (response.success) {
return response.data;
}
throw new Error(response.message);
} catch (error) {
console.error("Failed to fetch users:", error.message);
throw error;
}
// Pattern 3: Handle specific error types
const response = await client.get("/users");
if (!response.success) {
switch (response.status) {
case 401:
await refreshToken();
return await client.get("/users");
case 404:
return [];
default:
throw new Error(response.message);
}
}
return response.data;
const client = new AxiosApiClient(
baseUrl, // Required: Base URL for all requests
headers, // Optional: Default headers
timeout, // Optional: Request timeout in milliseconds (default: 30000)
retries, // Optional: Number of retry attempts (default: 3)
retryDelay, // Optional: Function to calculate delay between retries
onRetry // Optional: Callback function called on each retry
);
// Exponential backoff (recommended)
const exponentialClient = new AxiosApiClient(
"https://api.example.com",
{},
30000,
3,
(retryCount) => Math.pow(2, retryCount) * 1000,
(retryCount, error) => console.log(`Retry ${retryCount}: ${error.message}`)
);
// Linear backoff
const linearClient = new AxiosApiClient(
"https://api.example.com",
{},
30000,
3,
(retryCount) => retryCount * 1000
);
// Custom retry logic
const customClient = new AxiosApiClient(
"https://api.example.com",
{},
30000,
3,
(retryCount) => {
if (retryCount === 1) return 1000;
if (retryCount === 2) return 5000;
return 10000;
}
);
interface User {
id: number;
firstName: string;
lastName: string;
email: string;
createdAt: string;
}
interface CreateUserRequest {
firstName: string;
lastName: string;
email: string;
}
interface UpdateUserRequest {
firstName?: string;
lastName?: string;
email?: string;
}
class UserService {
private client: AxiosApiClient;
constructor() {
this.client = new AxiosApiClient({
baseUrl: "https://api.example.com",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer your-token"
},
retries: 3,
timeout: 10000
});
}
async getUsers(page: number = 1, limit: number = 10): Promise<User[]> {
const response = await this.client.get<User[]>("/users", {
params: { page, limit }
});
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
async getUserById(id: number): Promise<User> {
const response = await this.client.get<User>(`/users/${id}`);
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
async createUser(userData: CreateUserRequest): Promise<User> {
const response = await this.client.post<User>("/users", userData);
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
async updateUser(id: number, userData: UpdateUserRequest): Promise<User> {
const response = await this.client.put<User>(`/users/${id}`, userData);
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
async deleteUser(id: number): Promise<void> {
const response = await this.client.delete(`/users/${id}`);
if (!response.success) {
throw new Error(response.message);
}
}
async searchUsers(query: string): Promise<User[]> {
const response = await this.client.get<User[]>("/users/search", {
params: { q: query }
});
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
}
// Usage
const userService = new UserService();
try {
const users = await userService.getUsers(1, 20);
console.log(`Found ${users.length} users`);
const newUser = await userService.createUser({
firstName: "John",
lastName: "Doe",
email: "john@example.com"
});
console.log("Created user:", newUser);
const updatedUser = await userService.updateUser(newUser.id, {
firstName: "Jane"
});
console.log("Updated user:", updatedUser);
} catch (error) {
console.error("User service error:", error.message);
}
interface Product {
id: number;
name: string;
price: number;
description: string;
category: string;
stock: number;
}
interface Order {
id: number;
userId: number;
products: Array<{ productId: number; quantity: number }>;
total: number;
status: 'pending' | 'confirmed' | 'shipped' | 'delivered';
createdAt: string;
}
class EcommerceAPI {
private client: AxiosApiClient;
constructor(apiKey: string) {
this.client = new AxiosApiClient({
baseUrl: "https://api.ecommerce.com",
headers: {
"X-API-Key": apiKey,
"Content-Type": "application/json"
},
retries: 3,
timeout: 15000
});
}
async getProducts(category?: string, page: number = 1): Promise<Product[]> {
const params: any = { page };
if (category) params.category = category;
const response = await this.client.get<Product[]>("/products", { params });
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
async getProduct(id: number): Promise<Product> {
const response = await this.client.get<Product>(`/products/${id}`);
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
async createOrder(orderData: Omit<Order, 'id' | 'createdAt'>): Promise<Order> {
const response = await this.client.post<Order>("/orders", orderData);
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
async updateOrderStatus(orderId: number, status: Order['status']): Promise<Order> {
const response = await this.client.patch<Order>(`/orders/${orderId}`, { status });
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
}
interface UploadResponse {
id: string;
filename: string;
size: number;
url: string;
uploadedAt: string;
}
class FileUploadService {
private client: AxiosApiClient;
constructor() {
this.client = new AxiosApiClient({
baseUrl: "https://api.files.com",
headers: {
"Authorization": "Bearer your-token"
},
timeout: 60000, // Longer timeout for file uploads
retries: 2
});
}
async uploadFile(file: File, description?: string): Promise<UploadResponse> {
const formData = new FormData();
formData.append("file", file);
if (description) {
formData.append("description", description);
}
const response = await this.client.post<UploadResponse>("/upload", formData, {
headers: { "Content-Type": "multipart/form-data" }
});
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
async uploadMultipleFiles(files: File[]): Promise<UploadResponse[]> {
const formData = new FormData();
files.forEach((file, index) => {
formData.append(`files[${index}]`, file);
});
const response = await this.client.post<UploadResponse[]>("/upload/multiple", formData, {
headers: { "Content-Type": "multipart/form-data" }
});
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
async downloadFile(fileId: string): Promise<Blob> {
const response = await this.client.get(`/files/${fileId}`, {
responseType: "blob"
});
if (response.success) {
return response.data;
}
throw new Error(response.message);
}
}
// Problem: Frequent network errors
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
retries: 5, // Increase retries
retryDelay: (retryCount) => Math.pow(2, retryCount) * 1000, // Exponential backoff
timeout: 30000 // Increase timeout
});
// Problem: Token expiration
const client = new AxiosApiClient({
baseUrl: "https://api.example.com"
});
// Solution: Implement token refresh
const axiosInstance = client.getInstance();
axiosInstance.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
const newToken = await refreshToken();
client.setHeader("Authorization", `Bearer ${newToken}`);
error.config.headers.Authorization = `Bearer ${newToken}`;
return axiosInstance.request(error.config);
}
return Promise.reject(error);
}
);
// Problem: 429 Too Many Requests
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
retries: 3,
retryDelay: (retryCount) => retryCount * 2000 // Wait 2s, 4s, 6s
});
// Problem: Large files timing out
const client = new AxiosApiClient({
baseUrl: "https://api.example.com",
timeout: 300000, // 5 minutes for large uploads
retries: 1 // Don't retry large uploads
});
// Enable detailed logging
const client = new AxiosApiClient({
baseUrl: "https://api.example.com"
});
const axiosInstance = client.getInstance();
// Log all requests
axiosInstance.interceptors.request.use((config) => {
console.log("🚀 Request:", {
method: config.method?.toUpperCase(),
url: config.url,
headers: config.headers,
data: config.data
});
return config;
});
// Log all responses
axiosInstance.interceptors.response.use(
(response) => {
console.log("✅ Response:", {
status: response.status,
url: response.config.url,
data: response.data
});
return response;
},
(error) => {
console.log("❌ Error:", {
status: error.response?.status,
url: error.config?.url,
message: error.message
});
return Promise.reject(error);
}
);
new AxiosApiClient({
baseUrl: string,
headers?: Headers,
timeout?: number, // default: 30000
retries?: number, // default: 3
retryDelay?: (retryCount: number) => number,
onRetry?: (retryCount: number, error: any) => void
})
get<TResponse>(url: string, config?: AxiosRequestConfig): Promise<AkadeniaApiResponse<TResponse>>post<TResponse, TBody>(url: string, data?: TBody, config?: AxiosRequestConfig): Promise<AkadeniaApiResponse<TResponse>>put<TResponse, TBody>(url: string, data?: TBody, config?: AxiosRequestConfig): Promise<AkadeniaApiResponse<TResponse>>patch<TResponse, TBody>(url: string, data?: TBody, config?: AxiosRequestConfig): Promise<AkadeniaApiResponse<TResponse>>delete<TResponse>(url: string, config?: AxiosRequestConfig): Promise<AkadeniaApiResponse<TResponse>>setHeader(name: string, value: string): void - Set a header for all future requestsgetInstance(): AxiosInstance - Get the underlying axios instanceinterface AkadeniaApiSuccessResponse<T = any> extends AxiosResponse<T> {
success: true;
message?: string;
}
interface AkadeniaApiErrorResponse {
success: false;
message: string;
data?: any;
error: any;
status?: number;
statusText?: string;
}
type AkadeniaApiResponse<T = any> = AkadeniaApiSuccessResponse<T> | AkadeniaApiErrorResponse;
response.success before accessing response.dataWe welcome contributions! Please feel free to submit a Pull Request.
git clone https://github.com/akadenia/AkadeniaAPI.git
cd AkadeniaAPI
npm install
npm run build
npm test
We follow Conventional Commits for our semantic release process. We prefer commit messages to include a scope in parentheses for better categorization and changelog generation.
type(scope): description
[optional body]
[optional footer]
## ✅ Preferred - with scope
feat(api): add new retry configuration options
fix(auth): resolve token refresh issue
docs(readme): add troubleshooting section
chore(deps): update axios to latest version
## ❌ Less preferred - without scope
feat: add new retry configuration options
fix: resolve token refresh issue
docs: add troubleshooting section
chore: update axios to latest version
api - API client functionalityauth - Authentication and authorizationtypes - TypeScript type definitionsdocs - Documentation updatesdeps - Dependency updatestest - Test-related changesbuild - Build and build toolingci - CI/CD configurationfeat - New featuresfix - Bug fixesdocs - Documentation changesstyle - Code style changes (formatting, etc.)refactor - Code refactoringtest - Adding or updating testschore - Maintenance tasksFor support, please open an issue on GitHub.
FAQs
Akadenia library: API
The npm package @akadenia/api receives a total of 336 weekly downloads. As such, @akadenia/api popularity was classified as not popular.
We found that @akadenia/api demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.