A CLI tool that generates TypeScript API clients and React Query hooks from OpenAPI specifications or Postman collections.
- Generate TypeScript clients from OpenAPI specs or Postman collections
- Create React Query hooks for all API endpoints
- Type-safe API calls with TypeScript support
- Simple configuration and easy setup
# Using npm
npm install crag
# Using yarn
yarn add crag
# Using pnpm
pnpm add crag# Generate from an OpenAPI spec
crag generate api --path ./api-spec.yaml
# Generate from a Postman collection
crag generate api --path ./collection.json
# Specify output directory
crag generate api --path ./api-spec.yaml --destination ./src/api--path: Path to the API specification file (required)--destination: Output directory (default: current directory)--client-only: Generate only the TypeScript client--hooks-only: Generate only React Query hooks
- Clone the repository
- Install dependencies:
pnpm install
- Build the project:
pnpm build
- Run in development mode:
pnpm dev
ISC
Contributions are welcome! Please feel free to submit a Pull Request.
Create a crag.config.ts file:
export default {
input: [
{ path: './api-spec.json', type: 'postman' }
],
output: './src/api'
}The generated client is imported and configured like this:
import { client } from './client.gen';
// Configure the client
client.setConfig({
baseUrl: 'https://api.example.com',
headers: {
'Authorization': `Bearer ${token}`
}
});import { getAccessCohorts, postAccessLogin } from './sdk.gen';
// GET request
const cohorts = await getAccessCohorts();
// POST request with body
const loginResult = await postAccessLogin({
body: {
email: '[email protected]',
password: 'password123'
}
});
// Request with path parameters
const learner = await getAccessGetLearners({
path: { learner_id: '123' }
});
// Request with query parameters
const searchResults = await getAccessSearchLearners({
query: {
cohort_id: 'abc',
search: 'john'
}
});You can call the generated API functions directly:
These are used with useQuery:
// Example: Get learners
export const getAccessGetLearnersOptions = (options?: Options<GetAccessGetLearnersData>) => {
return queryOptions({
queryFn: async ({ queryKey, signal }) => {
const { data } = await getAccessGetLearners({
...options,
...queryKey[0],
signal,
throwOnError: true
});
return data;
},
queryKey: getAccessGetLearnersQueryKey(options)
});
};These are used with useMutation:
// Example: Create user mutation
export const postAccessRegisterLearnerMutation = (options?: Partial<Options<PostAccessRegisterLearnerData>>): UseMutationOptions<...> => {
const mutationOptions: UseMutationOptions<...> = {
mutationFn: async (localOptions) => {
const { data } = await postAccessRegisterLearner({
...options,
...localOptions,
throwOnError: true
});
return data;
}
};
return mutationOptions;
};export const getSearchPhotosInfiniteOptions = (options?: Options<GetSearchPhotosData>) => {
return infiniteQueryOptions<...>({
queryFn: async ({ pageParam, queryKey, signal }) => {
// Handle pagination
},
queryKey: getSearchPhotosInfiniteQueryKey(options)
});
};import { useQuery, useMutation } from '@tanstack/react-query';
import {
getAccessGetLearnersOptions,
postAccessRegisterLearnerMutation
} from './react-query.gen';
function LearnersComponent() {
// Query hook - automatically fetches data
const { data: learners, isLoading, error } = useQuery(
getAccessGetLearnersOptions()
);
// Mutation hook - for creating/updating
const registerLearnerMutation = useMutation(
postAccessRegisterLearnerMutation()
);
const handleRegister = (learnerData: any) => {
registerLearnerMutation.mutate({
body: learnerData
});
};
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{/* Your component JSX */}
<button onClick={() => handleRegister({ name: 'John', email: '[email protected]' })}>
Register Learner
</button>
</div>
);
}# Install dependencies
pnpm install
# Build
pnpm build
# Run in dev mode
pnpm devISC