axiosflow
Version:
AxiosFlow is a powerful TypeScript library that simplifies API consumption. It uses decorators and reflection to automatically generate type-safe API clients and types for your backend APIs, reducing boilerplate code and enhancing development efficiency.
426 lines (332 loc) • 12 kB
Markdown
# AxiosFlow
[](https://www.npmjs.com/package/axiosflow)
[](https://www.npmjs.com/package/axiosflow)
[](https://www.npmjs.com/package/axiosflow)
[](https://github.com/Noble-TS/AxiosFlow/issues)
[](https://github.com/Noble-TS/AxiosFlow/stargazers)
[](https://github.com/Noble-TS/AxiosFlow/actions)
**Automatically Generate Type-Safe API Functions for Your RESTful APIs**
AxiosFlow is a powerful TypeScript library that simplifies API consumption by automatically generating type-safe API functions for your RESTful APIs. It eliminates boilerplate code, ensures compile-time type safety, and works seamlessly with Express.js and Axios.
---
## Why AxiosFlow?
Building type-safe APIs can be time-consuming and error-prone. AxiosFlow solves this by:
- **Automating API Function Generation**: No more writing repetitive API call functions.
- **Ensuring Compile-Time Type Safety**: Catch errors before runtime with TypeScript.
- **Reducing Boilerplate Code**: Focus on building features, not writing API glue code.
- **Seamless Integration**: Works with your existing Express.js backend and Axios client.
---
## Key Features
### Comprehensive Type Safety
- **Robust Error Prevention**: Ensures type-safe API calls.
- **Compile-Time Validation**: Catches type mismatches before runtime.
- **Enhanced Code Quality**: Reduces potential errors in API interactions.
### Intelligent Type Generation
- **Automatic Function Creation**: Generates fully typed API client functions.
- **Eliminates Manual Type Definitions**: Dramatically reduces boilerplate code.
- **Compile-Time Type Safety**: Guarantees type consistency across API interactions.
### End-to-End Type Inference
- **Backend to Frontend Type Mapping**: Seamless type propagation.
- **Catches Potential Type Mismatches**: Ensures consistency during development.
### Core Capabilities
- **Automatic API Function Generation**: Generates type-safe API functions with minimal configuration.
- **Dynamic URL Parameter Support**: Easily handle dynamic routes like `/users/:id`.
- **Minimal Configuration Required**: Works out of the box with Express.js and Axios.
---
## How AxiosFlow Compares to Other Tools
| Feature/Tool | AxiosFlow | tRPC | OpenAPI (Swagger) | GraphQL Code Generator | Zodios | Manual Typing |
|------------------------|-------------------------|------------------------|------------------------|------------------------|------------------------|------------------------|
| **Type Safety** | Compile-time | Compile-time | Runtime | Compile-time | Runtime (Zod) | Manual |
| **Ease of Use** | Easy (Express.js + Axios)| Moderate (tRPC setup) | Moderate (YAML/JSON) | Moderate (GraphQL) | Moderate (Zod schemas) | Manual |
| **Flexibility** | High (framework-agnostic)| Low (tRPC-specific) | High (RESTful APIs) | Low (GraphQL-only) | High (Zod integration) | High |
| **Boilerplate Code** | Minimal | Minimal | Moderate | Minimal | Moderate | High |
AxiosFlow is the **simplest and most flexible solution** for adding type safety to RESTful APIs without requiring a new framework or maintaining a separate schema.
---
## Quick Start
## Examples
Check out the [`examples`](./examples) folder for complete implementation samples:
- **[Client](./examples/client)**: A React/TypeScript frontend using AxiosFlow.
- **[Server](./examples/server)**: An Express.js backend with AxiosFlow integration.
## CodeSandbox
Check out the live examples on CodeSandbox to see AxiosFlow in action:
[](https://codesandbox.io/p/github/Noble-TS/examples)
### Backend setup
#### Installation
#### Install core dependencies
```
npm install express@4.21.2 cors dotenv axiosflow-api
```
#### Install TypeScript and types
```
npm install -D typescript @types/express @types/cors ts-node
```
## Step-by-Step Setup
### 1. Create Controller (userController.ts)
```typescript
import { Request, Response } from 'express';
// Interfaces for type safety
export class UserRequest {
name: string | undefined;
}
export class User {
id: number | undefined;
name: string | undefined;
}
export class UserController {
private users: User[] = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' },
];
getUsers(req: Request, res: Response): Response {
return res.status(200).json({
status: 'success',
data: this.users,
});
}
getUserById(req: Request, res: Response): Response {
const userId = parseInt(req.params.id);
const user = this.users.find((u) => u.id === userId);
if (!user) {
return res.status(404).json({
status: 'error',
message: 'User not found',
});
}
return res.status(200).json({
status: 'success',
data: user,
});
}
createUser(req: Request, res: Response): Response {
const { name } = req.body as UserRequest;
if (!name) {
return res.status(400).json({
status: 'error',
message: 'Name is required',
});
}
const newUser = {
id: this.users.length + 1,
name
};
this.users.push(newUser);
return res.status(201).json({
status: 'success',
data: newUser,
});
}
}
```
### 2. Create User Routes (userRoutes.ts)
```typescript
import { registerRoute, typeRef } from 'axiosflow-api';
import { UserController, UserRequest, User } from '../controllers/userController';
// Create an instance of the controller
const userController = new UserController();
// Define routes and register them dynamically
export function registerUserRoutes() {
registerRoute(
userController,
'GET',
'/users',
null,
typeRef<User>('User', { id: 'number', name: 'string' }),
[],
'getUsers'
);
registerRoute(
userController,
'GET',
'/users/:id',
null,
typeRef<User>('User', { id: 'number', name: 'string' }),
[],
'getUserById'
);
registerRoute(
userController,
'POST',
'/users',
typeRef<UserRequest>('UserRequest', { name: 'string' }),
typeRef<User>('User', { id: 'number', name: 'string' }),
[],
'createUser'
);
}
// Export the controller instance for use in the router
export const controllerInstances = {
UserController: userController,
};
```
### 3. Create Router (router.ts)
```typescript
import { Router } from 'express';
import { createRoutes } from 'axiosflow-api';
import { registerUserRoutes, controllerInstances } from './userRoutes';
const router = Router();
// Register user routes
registerUserRoutes();
// Dynamically create routes based on registered metadata
createRoutes(router, controllerInstances);
export default router;
```
### 4. Create Server (server.ts)
```typescript
import express from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import router from './routes/router';
import { exportRoutesForSchema } from 'axiosflow-api';
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(express.json());
// Routes
app.use('/api', router);
// Route to export API schema
app.get('/axiosflow', (req, res) => {
const routes = exportRoutesForSchema();
res.json(routes);
});
// Global error handler
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error(err.stack);
res.status(500).json({
message: 'Something went wrong',
error: process.env.NODE_ENV !== 'production' ? err : {}
});
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
```
### Advanced Configuration: Middleware and Interceptors
```typescript
import { Request, Response, NextFunction } from 'express';
import { registerRoute, typeRef } from 'axiosflow-api';
// CSRF middleware
const csrfProtection: RequestHandler = csrf({ cookie: true });
// logger middleware
const logger: RequestHandler = (req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
};
export function registerUserRoutes() {
// GET Users - Simple logging middleware
registerRoute(
userController,
'GET',
'/users',
null,
typeRef<User>('User', { id: 'number', name: 'string' }),
[csrfProtection,logger],
'getUsers'
);
// GET User by ID - Authentication and logging
registerRoute(
userController,
'GET',
'/users/:id',
null,
typeRef<User>('User', { id: 'number', name: 'string' }),
[csrfProtection, logger],
'getUserById'
);
}
```
## Client Setup
### Prerequisites
Node.js (v16+)
TypeScript
Axios
#### Global installation via npm
```
npm install -g axiosflow
```
#### Verify Installation
```t
axiosflow --help
```
#### Generate API Functions
```t
# Basic generation
axiosflow generate
# With specific options
axiosflow generate -b http://localhost:3000 -o ./src/services
```
### 3. Generated Files Structure in project structure
```t
src/services/
├── apiFunctions.ts # Generated API functions
├── apiConfig.ts # API endpoint configurations
├── types.ts # Type definitions
└── api-schema.json # API schema documentation
```
## Usage in React/TypeScript Project
### Fetch Users
```typescript
import { get_users } from './services/apiFunctions';
async function UserList() {
try {
const users = await get_users();
// Handle users
} catch (error) {
// Handle error
}
}
```
### Create User
```typescript
import { post_users } from './services/apiFunctions';
async function createUser(name: string) {
try {
const newUser = await post_users({ name });
// Handle new user
} catch (error) {
// Handle error
}
}
```
or in components :
```typescript
import React, { useEffect, useState } from 'react';
import { get_users, post_users, get_users_id } from '../services/apiFunctions';
import { User, UserRequest } from '../services/types';
const UserComponent: React.FC = () => {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
// Fetch the list of users from the API on component mount
useEffect(() => {
const fetchUsers = async () => {
try {
setLoading(true);
setError(null);
console.log('Attempting to fetch users...');
const userList = await get_users();
console.log('Fetched Users:', userList);
if (userList.length > 0) {
setUsers(userList);
} else {
setError('No users found');
}
} catch (err) {
console.error('Fetch Users Error:', err);
const errorMessage = err instanceof Error
? err.message
: 'An unexpected error occurred';
setError(errorMessage);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
return (
<div>
<h1>Users Lists</h1>
</div>
);
};
```