@consolify/api
v0.1.7
Published
A powerful and flexible API client package built on top of TanStack React Query and react-query-kit. This package provides a type-safe fetcher function with automatic URL parameter replacement, configurable base URLs, and seamless integration with React Q
Readme
@consolify/api
A powerful and flexible API client package built on top of TanStack React Query and react-query-kit. This package provides a type-safe fetcher function with automatic URL parameter replacement, configurable base URLs, and seamless integration with React Query.
Features
- 🚀 Type-safe API calls with full TypeScript support
- 🔧 Configurable base URL - set once per app, use everywhere
- 🎯 Automatic URL parameter replacement - use
:paramsyntax in URLs - 📦 Built on TanStack React Query - leverage the power of React Query
- 🔄 Support for all HTTP methods (GET, POST, PUT, DELETE, etc.)
- 📝 Automatic JSON serialization for request bodies
- 📋 FormData support for file uploads
- 🛡️ Error handling with proper error propagation
Installation
npm install @consolify/api
# or
yarn add @consolify/api
# or
pnpm add @consolify/apiQuick Start
1. Configure the Base URL
Configure the API base URL once in your app initialization (e.g., in main.tsx or App.tsx):
import { configureApi } from '@consolify/api';
// Configure the base URL for your app
configureApi({
baseUrl: 'https://api.yourapp.com'
// or for development: 'http://localhost:3000/api'
});2. Create API Fetchers
import { createFetcher } from '@consolify/api';
// Define your data types
interface User {
id: number;
name: string;
email: string;
}
interface CreateUserRequest {
name: string;
email: string;
}
// Create fetchers for different endpoints
const getUserFetcher = createFetcher<User>('/users/:id');
const getUsersFetcher = createFetcher<User[]>('/users');
const createUserFetcher = createFetcher<User, CreateUserRequest>('/users', 'POST');
const updateUserFetcher = createFetcher<User, Partial<User>>('/users/:id', 'PUT');
const deleteUserFetcher = createFetcher<void>('/users/:id', 'DELETE');3. Use with react-query-kit Router
import { router } from '@consolify/api';
// Define your API routes using the router pattern
const userApi = router('users', {
// GET /users
list: router.query({
fetcher: createFetcher<User[]>('/users')
}),
// GET /users/:id
get: router.query({
fetcher: createFetcher<User>('/users/:id')
}),
// POST /users
create: router.mutation({
fetcher: createFetcher<User, CreateUserRequest>('/users', 'POST')
}),
// PUT /users/:id
update: router.mutation({
fetcher: createFetcher<User, Partial<User>>('/users/:id', 'PUT')
}),
// DELETE /users/:id
delete: router.mutation({
fetcher: createFetcher<void>('/users/:id', 'DELETE')
})
});
// Use in components
function UserProfile({ userId }: { userId: number }) {
const { data: user, isLoading } = userApi.get.useQuery({
variables: { id: userId }
});
if (isLoading) return <div>Loading...</div>;
return <div>{user?.name}</div>;
}
function UsersList() {
const { data: users } = userApi.list.useQuery();
const createUser = userApi.create.useMutation();
const updateUser = userApi.update.useMutation();
const deleteUser = userApi.delete.useMutation();
const handleCreate = (userData: CreateUserRequest) => {
createUser.mutate(userData);
};
const handleUpdate = (id: number, userData: Partial<User>) => {
updateUser.mutate({ id, ...userData });
};
const handleDelete = (id: number) => {
deleteUser.mutate({ id });
};
// ... component implementation
}Alternative: Direct React Query Usage
import { useQuery, useMutation } from '@tanstack/react-query';
// GET request with URL parameters
function UserProfile({ userId }: { userId: number }) {
const { data: user, isLoading } = useQuery({
queryKey: ['user', userId],
queryFn: ({ queryKey }) => getUserFetcher({ id: queryKey[1] }, {} as any),
});
if (isLoading) return <div>Loading...</div>;
return <div>{user?.name}</div>;
}
// POST request
function CreateUserForm() {
const createUser = useMutation({
mutationFn: (userData: CreateUserRequest) =>
createUserFetcher(userData, {} as any),
});
const handleSubmit = (userData: CreateUserRequest) => {
createUser.mutate(userData);
};
// ... form implementation
}API Reference
configureApi(config)
Configure the API client with global settings.
Parameters:
config.baseUrl(string): The base URL for all API requests
Example:
configureApi({
baseUrl: 'https://api.example.com'
});getApiConfig()
Get the current API configuration.
Returns: Current configuration object
Example:
const config = getApiConfig();
console.log(config.baseUrl); // 'https://api.example.com'createFetcher<TFnData, TVariables, TPageParam>(url, method?, options?)
Create a type-safe fetcher function for API endpoints.
Type Parameters:
TFnData: The expected response data typeTVariables: The request variables/payload type (default:Record<string, any>)TPageParam: The pagination parameter type (default:never)
Parameters:
url(string): The endpoint URL (can include:paramplaceholders)method(string, optional): HTTP method (default: 'GET')options(RequestInit, optional): Additional fetch options (excluding method)
Returns: A fetcher function compatible with TanStack React Query
URL Parameter Replacement
The package automatically replaces URL parameters using the :param syntax:
const getUserFetcher = createFetcher<User>('/users/:id/posts/:postId');
// Usage
const { data } = useQuery({
queryKey: ['user-posts', userId, postId],
queryFn: getUserFetcher,
variables: {
id: userId, // Replaces :id in URL
postId: postId // Replaces :postId in URL
},
});HTTP Methods
GET Requests
const getFetcher = createFetcher<ResponseType>('/endpoint');
// or explicitly
const getFetcher = createFetcher<ResponseType>('/endpoint', 'GET');POST Requests
const postFetcher = createFetcher<ResponseType, RequestType>('/endpoint', 'POST');PUT/PATCH Requests
const putFetcher = createFetcher<ResponseType, RequestType>('/endpoint/:id', 'PUT');
const patchFetcher = createFetcher<ResponseType, Partial<RequestType>>('/endpoint/:id', 'PATCH');DELETE Requests
const deleteFetcher = createFetcher<void>('/endpoint/:id', 'DELETE');Request Body Handling
The package automatically handles different types of request bodies:
JSON Data
const createUserFetcher = createFetcher<User, CreateUserRequest>('/users', 'POST');
// Automatically serialized as JSON
createUser.mutate({
name: 'John Doe',
email: '[email protected]'
});FormData (File Uploads)
const uploadFetcher = createFetcher<UploadResponse, FormData>('/upload', 'POST');
const formData = new FormData();
formData.append('file', file);
formData.append('description', 'My file');
upload.mutate(formData); // Sent as multipart/form-dataCustom Headers and Options
const fetcher = createFetcher<ResponseType>('/endpoint', 'POST', {
headers: {
'Authorization': 'Bearer token',
'X-Custom-Header': 'value'
},
credentials: 'include',
cache: 'no-cache'
});Error Handling
Errors are automatically propagated and can be handled using React Query's error handling:
const { data, error, isError } = useQuery({
queryKey: ['users'],
queryFn: getUsersFetcher,
onError: (error) => {
console.error('Failed to fetch users:', error);
}
});
if (isError) {
return <div>Error: {error.message}</div>;
}Advanced Usage
Complex API Structure with Nested Routes
import { router } from '@consolify/api';
// Organize your API with nested structures
const api = router('api', {
users: router('users', {
list: router.query({
fetcher: createFetcher<User[]>('/users')
}),
get: router.query({
fetcher: createFetcher<User>('/users/:id')
}),
posts: router('posts', {
list: router.query({
fetcher: createFetcher<Post[]>('/users/:userId/posts')
}),
get: router.query({
fetcher: createFetcher<Post>('/users/:userId/posts/:postId')
}),
create: router.mutation({
fetcher: createFetcher<Post, CreatePostRequest>('/users/:userId/posts', 'POST')
})
})
}),
auth: router('auth', {
login: router.mutation({
fetcher: createFetcher<AuthResponse, LoginRequest>('/auth/login', 'POST')
}),
logout: router.mutation({
fetcher: createFetcher<void>('/auth/logout', 'POST')
}),
refresh: router.mutation({
fetcher: createFetcher<AuthResponse>('/auth/refresh', 'POST')
})
})
});
// Usage
function UserPosts({ userId }: { userId: number }) {
const { data: posts } = api.users.posts.list.useQuery({
variables: { userId }
});
const createPost = api.users.posts.create.useMutation();
const handleCreatePost = (postData: CreatePostRequest) => {
createPost.mutate({ userId, ...postData });
};
// ... component implementation
}Simple Router Pattern
import { router } from '@consolify/api';
// Simple API structure
const testApi = router('test', {
get: router.query({
fetcher: createFetcher<TestData>('/test')
}),
create: router.mutation({
fetcher: createFetcher<TestData, CreateTestRequest>('/test', 'POST')
}),
update: router.mutation({
fetcher: createFetcher<TestData, UpdateTestRequest>('/test/:id', 'PUT')
})
});
// Usage in components
function TestComponent() {
const { data: testData, isLoading } = testApi.get.useQuery();
const createTest = testApi.create.useMutation();
const updateTest = testApi.update.useMutation();
if (isLoading) return <div>Loading...</div>;
return (
<div>
<h1>{testData?.title}</h1>
<button onClick={() => createTest.mutate({ title: 'New Test' })}>
Create Test
</button>
</div>
);
}Multiple Base URLs
If you need different base URLs for different parts of your app, you can create separate fetcher instances:
// Configure main API
configureApi({ baseUrl: 'https://api.example.com' });
// For a specific endpoint that uses a different base URL
const externalFetcher = createFetcher<ExternalData>('https://external-api.com/data');Dependencies
@tanstack/react-query: ^5.87.1react-query-kit: ^3.3.2
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
