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-types

Generated 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 --validate

Custom 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