Type Generation
Learn how Query-2jz automatically generates TypeScript types from your models for end-to-end type safety.
What is Type Generation?
Query-2jz automatically generates TypeScript types from your model definitions, ensuring type safety from your database schema all the way to your frontend components.
Type Generation Features
Automatic type generation
Real-time type updates
Multiple output formats
Customizable generation
Validation types
API client types
Basic Type Generation
Generate types from your model definitions with a simple command.
Generate Types
Generate TypeScript types from your models
CLI Command
query-2jz generate-typesGenerated Types
// Generated types in ./generated/models.ts
export interface User {
id: string;
name: string;
email: string;
age?: number;
isActive: boolean;
createdAt: Date;
updatedAt: Date;
posts?: Post[];
}
export interface Post {
id: string;
title: string;
content?: string;
authorId: string;
publishedAt?: Date;
createdAt: Date;
updatedAt: Date;
author?: User;
}
export interface CreateUserInput {
name: string;
email: string;
age?: number;
isActive?: boolean;
}
export interface UpdateUserInput {
name?: string;
email?: string;
age?: number;
isActive?: boolean;
}
export interface UserWhereInput {
id?: string;
name?: string;
email?: string;
age?: number;
isActive?: boolean;
createdAt?: Date;
updatedAt?: Date;
}
export interface UserOrderByInput {
id?: 'asc' | 'desc';
name?: 'asc' | 'desc';
email?: 'asc' | 'desc';
age?: 'asc' | 'desc';
isActive?: 'asc' | 'desc';
createdAt?: 'asc' | 'desc';
updatedAt?: 'asc' | 'desc';
}API Client Types
Generate fully typed API client functions for your models.
Generated API Client
Type-safe API client functions
// Generated API client in ./generated/client.ts
export class Query-2jzClient {
constructor(private baseUrl: string = '/api/query-2jz') {}
// User operations
async getUser(id: string): Promise<User> {
const response = await fetch(`${this.baseUrl}/User/${id}`);
return response.json();
}
async getUsers(params?: {
where?: UserWhereInput;
orderBy?: UserOrderByInput;
limit?: number;
offset?: number;
select?: string[];
include?: string[];
}): Promise<{ data: User[]; meta: { total: number; limit: number; offset: number } }> {
const searchParams = new URLSearchParams();
if (params?.where) searchParams.set('where', JSON.stringify(params.where));
if (params?.orderBy) searchParams.set('orderBy', JSON.stringify(params.orderBy));
if (params?.limit) searchParams.set('limit', params.limit.toString());
if (params?.offset) searchParams.set('offset', params.offset.toString());
if (params?.select) searchParams.set('select', params.select.join(','));
if (params?.include) searchParams.set('include', params.include.join(','));
const response = await fetch(`${this.baseUrl}/User?${searchParams}`);
return response.json();
}
async createUser(data: CreateUserInput): Promise<User> {
const response = await fetch(`${this.baseUrl}/User`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
async updateUser(id: string, data: UpdateUserInput): Promise<User> {
const response = await fetch(`${this.baseUrl}/User/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
async deleteUser(id: string): Promise<void> {
await fetch(`${this.baseUrl}/User/${id}`, {
method: 'DELETE'
});
}
// Post operations
async getPost(id: string): Promise<Post> {
const response = await fetch(`${this.baseUrl}/Post/${id}`);
return response.json();
}
async getPosts(params?: {
where?: PostWhereInput;
orderBy?: PostOrderByInput;
limit?: number;
offset?: number;
select?: string[];
include?: string[];
}): Promise<{ data: Post[]; meta: { total: number; limit: number; offset: number } }> {
// Similar implementation...
}
async createPost(data: CreatePostInput): Promise<Post> {
// Similar implementation...
}
async updatePost(id: string, data: UpdatePostInput): Promise<Post> {
// Similar implementation...
}
async deletePost(id: string): Promise<void> {
// Similar implementation...
}
}Validation Types
Generate validation schemas and types for runtime validation.
Validation Schemas
Runtime validation with Zod schemas
// Generated validation schemas in ./generated/validation.ts
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string(),
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().min(0).max(120).optional(),
isActive: z.boolean().default(true),
createdAt: z.date(),
updatedAt: z.date(),
posts: z.array(PostSchema).optional()
});
export const CreateUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().min(0).max(120).optional(),
isActive: z.boolean().default(true)
});
export const UpdateUserSchema = z.object({
name: z.string().min(1).max(100).optional(),
email: z.string().email().optional(),
age: z.number().min(0).max(120).optional(),
isActive: z.boolean().optional()
});
export const UserWhereSchema = z.object({
id: z.string().optional(),
name: z.string().optional(),
email: z.string().optional(),
age: z.number().optional(),
isActive: z.boolean().optional(),
createdAt: z.date().optional(),
updatedAt: z.date().optional()
});
// Type inference from schemas
export type User = z.infer<typeof UserSchema>;
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
export type UpdateUserInput = z.infer<typeof UpdateUserSchema>;
export type UserWhereInput = z.infer<typeof UserWhereSchema>;Configuration Options
Customize type generation with various configuration options.
Type Generation Config
{
"typeGeneration": {
"enabled": true,
"outputDir": "./generated",
"formats": ["typescript", "zod", "client"],
"options": {
"typescript": {
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true
},
"zod": {
"strict": true,
"coerce": false
},
"client": {
"baseUrl": "/api/query-2jz",
"timeout": 5000,
"retries": 3
}
},
"watch": true,
"include": ["User", "Post", "Comment"],
"exclude": ["InternalLog", "AuditTrail"]
}
}CLI Options
# Generate types with specific options
query-2jz generate-types --output ./types --format typescript,zod --watch
# Generate types for specific models
query-2jz generate-types --include User,Post --exclude InternalLog
# Generate types with custom configuration
query-2jz generate-types --config ./custom-types.config.js
# Watch for changes and regenerate
query-2jz generate-types --watch
# Generate types and validate
query-2jz generate-types --validateCustom Type Generation
Create custom type generators for specific needs.
// Custom type generator
// ./generators/react-query.ts
import { Model, Field } from './types';
export function generateReactQueryTypes(models: Model[]) {
const imports = [
"import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';",
"import { Query-2jzClient } from './client';"
];
const hooks = models.map(model => {
const modelName = model.name;
const lowerModelName = modelName.toLowerCase();
return `
// ${modelName} hooks
export function use${modelName}(id: string) {
return useQuery({
queryKey: ['${lowerModelName}', id],
queryFn: () => client.get${modelName}(id)
});
}
export function use${modelName}s(params?: Get${modelName}sParams) {
return useQuery({
queryKey: ['${lowerModelName}s', params],
queryFn: () => client.get${modelName}s(params)
});
}
export function useCreate${modelName}() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: Create${modelName}Input) => client.create${modelName}(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['${lowerModelName}s'] });
}
});
}
export function useUpdate${modelName}() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: string; data: Update${modelName}Input }) =>
client.update${modelName}(id, data),
onSuccess: (_, { id }) => {
queryClient.invalidateQueries({ queryKey: ['${lowerModelName}', id] });
queryClient.invalidateQueries({ queryKey: ['${lowerModelName}s'] });
}
});
}
export function useDelete${modelName}() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => client.delete${modelName}(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['${lowerModelName}s'] });
}
});
}`;
});
return [...imports, ...hooks].join('\n\n');
}Integration Examples
See how to use generated types in different frameworks and libraries.
React with TypeScript
import { User, CreateUserInput } from './generated/models';
import { useUsers, useCreateUser } from './generated/react-query';
function UserList() {
const { data: users, isLoading } = useUsers();
const createUser = useCreateUser();
const handleCreateUser = (data: CreateUserInput) => {
createUser.mutate(data);
};
if (isLoading) return <div>Loading...</div>;
return (
<div>
{users?.data.map((user: User) => (
<div key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
))}
</div>
);
}Vue with TypeScript
import { ref, computed } from 'vue';
import { User, CreateUserInput } from './generated/models';
import { Query-2jzClient } from './generated/client';
const client = new Query-2jzClient();
export function useUsers() {
const users = ref<User[]>([]);
const loading = ref(false);
const fetchUsers = async () => {
loading.value = true;
try {
const response = await client.getUsers();
users.value = response.data;
} finally {
loading.value = false;
}
};
const createUser = async (data: CreateUserInput) => {
const newUser = await client.createUser(data);
users.value.push(newUser);
};
return {
users: computed(() => users.value),
loading: computed(() => loading.value),
fetchUsers,
createUser
};
}Best Practices
Do
- • Keep generated types in version control
- • Use watch mode during development
- • Validate types at build time
- • Use strict TypeScript settings
- • Generate types for all environments
- • Use custom generators for specific needs
- • Keep type generation in CI/CD
Don't
- • Manually edit generated types
- • Ignore type generation errors
- • Use loose TypeScript settings
- • Skip type validation in production
- • Generate types only once
- • Mix generated and manual types
- • Forget to update types after schema changes