Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ash-project/ash_typescript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

AshTypescript

πŸ”₯ Automatic TypeScript type generation for Ash resources and actions

Generate type-safe TypeScript clients directly from your Elixir Ash resources, ensuring end-to-end type safety between your backend and frontend. Never write API types manually again.

Hex.pm Documentation License

⚑ Quick Start

Get up and running in under 5 minutes:

1. Installation

Add to your mix.exs:

def deps do
  [
    {:ash_typescript, "~> 0.1.2"}
  ]
end

2. Configure your domain

defmodule MyApp.Domain do
  use Ash.Domain, extensions: [AshTypescript.Rpc]

  typescript_rpc do
    resource MyApp.Todo do
      rpc_action :list_todos, :read
      rpc_action :create_todo, :create
      rpc_action :get_todo, :get
    end
  end

  resources do
    resource MyApp.Todo
  end
end

3. Set up Phoenix RPC controller

defmodule MyAppWeb.RpcController do
  use MyAppWeb, :controller

  def run(conn, params) do
    # Actor (and tenant if needed) must be set on the conn before calling run/2 or validate/2
    # If your pipeline does not set these, you must add something like the following code:
    # conn = Ash.PlugHelpers.set_actor(conn, conn.assigns[:current_user])
    # conn = Ash.PlugHelpers.set_tenant(conn, conn.assigns[:tenant])
    result = AshTypescript.Rpc.run_action(:my_app, conn, params)
    json(conn, result)
  end

  def validate(conn, params) do
    result = AshTypescript.Rpc.validate_action(:my_app, conn, params)
    json(conn, result)
  end
end

4. Add RPC routes

Add these routes to your router.ex to map the RPC endpoints:

scope "/rpc", MyAppWeb do
  pipe_through :api  # or :browser if using session-based auth

  post "/run", RpcController, :run
  post "/validate", RpcController, :validate
end

5. Generate TypeScript types

Recommended approach (runs codegen for all Ash extensions in your project):

mix ash.codegen --dev"

Alternative approach (runs codegen only for AshTypescript):

mix ash_typescript.codegen --output "assets/js/ash_rpc.ts"

6. Use in your frontend

import { listTodos, createTodo } from './ash_rpc';

// βœ… Fully type-safe API calls
const todos = await listTodos({
  fields: ["id", "title", "completed"],
  filter: { completed: false }
});

const newTodo = await createTodo({
  fields: ["id", "title", { user: ["name", "email"] }],
  input: { title: "Learn AshTypescript", priority: "high" }
});

πŸŽ‰ That's it! Your TypeScript frontend now has compile-time type safety for your Elixir backend.

πŸš€ Features

  • πŸ”₯ Zero-config TypeScript generation - Automatically generates types from Ash resources
  • πŸ›‘οΈ End-to-end type safety - Catch integration errors at compile time, not runtime
  • ⚑ Smart field selection - Request only needed fields with full type inference
  • 🎯 RPC client generation - Type-safe function calls for all action types
  • 🏒 Multitenancy ready - Automatic tenant parameter handling
  • πŸ“¦ Advanced type support - Enums, unions, embedded resources, and calculations
  • πŸ”§ Highly configurable - Custom endpoints, formatting, and output options
  • πŸ§ͺ Runtime validation - Zod schemas for runtime type checking and form validation
  • πŸ” Auto-generated filters - Type-safe filtering with comprehensive operator support
  • πŸ“‹ Form validation - Client-side validation functions for all actions
  • 🎯 Typed queries - Pre-configured queries for SSR and optimized data fetching
  • 🎨 Flexible field formatting - Separate input/output formatters (camelCase, snake_case, etc.)
  • πŸ”Œ Custom HTTP clients - Support for custom fetch functions and request options (axios, interceptors, etc.)

πŸ“š Table of Contents

πŸ—οΈ Core Concepts

How it works

  1. Resource Definition: Define your Ash resources with attributes, relationships, and actions
  2. RPC Configuration: Expose specific actions through your domain's RPC configuration
  3. Type Generation: Run mix ash_typescript.codegen to generate TypeScript types
  4. Frontend Integration: Import and use fully type-safe client functions

Type Safety Benefits

  • Compile-time validation - TypeScript compiler catches API misuse
  • Autocomplete support - Full IntelliSense for all resource fields and actions
  • Refactoring safety - Rename fields in Elixir, get TypeScript errors immediately
  • Documentation - Generated types serve as living API documentation

πŸ’‘ Usage Examples

Basic CRUD Operations

import { listTodos, getTodo, createTodo, updateTodo, destroyTodo } from './ash_rpc';

// List todos with field selection
const todos = await listTodos({
  fields: ["id", "title", "completed", "priority"],
  filter: { status: "active" },
  sort: "-priority,+createdAt"
});

// Get single todo with relationships
const todo = await getTodo({
  fields: ["id", "title", { user: ["name", "email"] }],
  id: "todo-123"
});

// Create new todo
const newTodo = await createTodo({
  fields: ["id", "title", "createdAt"],
  input: {
    title: "Learn AshTypescript",
    priority: "high",
    dueDate: "2024-01-01"
  }
});

// Update existing todo (primary key separate from input)
const updatedTodo = await updateTodo({
  fields: ["id", "title", "priority", "updatedAt"],
  primaryKey: "todo-123",  // Primary key as separate parameter
  input: {
    title: "Updated: Learn AshTypescript",
    priority: "urgent"
  }
});

// Delete todo (primary key separate from input)
const deletedTodo = await destroyTodo({
  fields: [],
  primaryKey: "todo-123"    // Primary key as separate parameter
});

Advanced Field Selection

// Complex nested field selection
const todoWithDetails = await getTodo({
  fields: [
    "id", "title", "description",
    {
      user: ["name", "email", "avatarUrl"],
      comments: ["id", "text", { author: ["name"] }],
      tags: ["name", "color"]
    }
  ],
  id: "todo-123"
});

// Calculations with arguments
const todoWithCalc = await getTodo({
  fields: [
    "id", "title",
    {
      "priorityScore": {
        "args": { "multiplier": 2 },
        "fields": ["score", "rank"]
      }
    }
  ],
  id: "todo-123"
});

Error Handling

All generated RPC functions return a {success: true/false} structure instead of throwing exceptions:

const result = await createTodo({
  fields: ["id", "title"],
  input: { title: "New Todo" }
});

if (result.success) {
  // Access the created todo
  console.log("Created todo:", result.data);
  const todoId: string = result.data.id;
  const todoTitle: string = result.data.title;
} else {
  // Handle validation errors, network errors, etc.
  result.errors.forEach(error => {
    console.error(`Error: ${error.message}`);
    if (error.fieldPath) {
      console.error(`Field: ${error.fieldPath}`);
    }
  });
}

Custom Headers and Authentication

import { listTodos, buildCSRFHeaders } from './ash_rpc';

// With CSRF protection
const todos = await listTodos({
  fields: ["id", "title"],
  headers: buildCSRFHeaders()
});

// With custom authentication
const todos = await listTodos({
  fields: ["id", "title"],
  headers: {
    "Authorization": "Bearer your-token-here",
    "X-Custom-Header": "value"
  }
});

Custom Fetch Functions and Request Options

AshTypescript allows you to customize the HTTP client used for requests by providing custom fetch functions and additional fetch options.

Using fetchOptions for Request Customization

All generated RPC functions accept an optional fetchOptions parameter that allows you to customize the underlying fetch request:

import { createTodo, listTodos } from './ash_rpc';

// Add request timeout and custom cache settings
const todo = await createTodo({
  fields: ["id", "title"],
  input: { title: "New Todo" },
  fetchOptions: {
    signal: AbortSignal.timeout(5000), // 5 second timeout
    cache: 'no-cache',
    credentials: 'include'
  }
});

// Use with abort controller for cancellable requests
const controller = new AbortController();

const todos = await listTodos({
  fields: ["id", "title"],
  fetchOptions: {
    signal: controller.signal
  }
});

// Cancel the request if needed
controller.abort();

Custom Fetch Functions

You can replace the native fetch function entirely by providing a customFetch parameter. This is useful for:

  • Adding global authentication
  • Using alternative HTTP clients like axios
  • Adding request/response interceptors
  • Custom error handling
// Custom fetch with user preferences and tracking
const enhancedFetch = async (url: RequestInfo | URL, init?: RequestInit) => {
  // Get user preferences from localStorage (safe, non-sensitive data)
  const userLanguage = localStorage.getItem('userLanguage') || 'en';
  const userTimezone = localStorage.getItem('userTimezone') || 'UTC';
  const apiVersion = localStorage.getItem('preferredApiVersion') || 'v1';

  // Generate correlation ID for request tracking
  const correlationId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

  const customHeaders = {
    'Accept-Language': userLanguage,
    'X-User-Timezone': userTimezone,
    'X-API-Version': apiVersion,
    'X-Correlation-ID': correlationId,
  };

  return fetch(url, {
    ...init,
    headers: {
      ...init?.headers,
      ...customHeaders
    }
  });
};

// Use custom fetch function
const todos = await listTodos({
  fields: ["id", "title"],
  customFetch: enhancedFetch
});

Using Axios with AshTypescript

While AshTypescript uses the fetch API by default, you can create an adapter to use axios or other HTTP clients:

import axios from 'axios';

// Create axios adapter that matches fetch API
const axiosAdapter = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
  try {
    const url = typeof input === 'string' ? input : input.toString();

    const axiosResponse = await axios({
      url,
      method: init?.method || 'GET',
      headers: init?.headers,
      data: init?.body,
      timeout: 10000,
      // Add other axios-specific options
      validateStatus: () => true // Don't throw on HTTP errors
    });

    // Convert axios response to fetch Response
    return new Response(JSON.stringify(axiosResponse.data), {
      status: axiosResponse.status,
      statusText: axiosResponse.statusText,
      headers: new Headers(axiosResponse.headers as any)
    });
  } catch (error) {
    if (error.response) {
      // HTTP error status
      return new Response(JSON.stringify(error.response.data), {
        status: error.response.status,
        statusText: error.response.statusText
      });
    }
    throw error; // Network error
  }
};

// Use axios for all requests
const todos = await listTodos({
  fields: ["id", "title"],
  customFetch: axiosAdapter
});

Advanced Filtering and Pagination

import { listTodos } from './ash_rpc';

// Complex filtering with pagination
const result = await listTodos({
  fields: ["id", "title", "priority", "dueDate", { user: ["name"] }],
  filter: {
    and: [
      { status: { eq: "ongoing" } },
      { priority: { in: ["high", "urgent"] } },
      {
        or: [
          { dueDate: { lessThan: "2024-12-31" } },
          { user: { name: { eq: "John Doe" } } }
        ]
      }
    ]
  },
  sort: "-priority,+dueDate",
  page: {
    limit: 20,
    offset: 0,
    count: true
  }
});

if (result.success) {
  console.log(`Found ${result.data.count} todos`);
  console.log(`Showing ${result.data.results.length} results`);
  console.log(`Has more: ${result.data.hasMore}`);
}

πŸ”§ Advanced Features

Embedded Resources

Full support for embedded resources with type safety:

# In your resource
attribute :metadata, MyApp.TodoMetadata do
  public? true
end
// TypeScript usage
const todo = await getTodo({
  fields: [
    "id", "title",
    { metadata: ["priority", "tags", "customFields"] }
  ],
  id: "todo-123"
});

Union Types

Support for Ash union types with selective field access:

# In your resource
attribute :content, :union do
  constraints types: [
    text: [type: :string],
    checklist: [type: MyApp.ChecklistContent]
  ]
end
// TypeScript usage with union field selection
const todo = await getTodo({
  fields: [
    "id", "title",
    { content: ["text", { checklist: ["items", "completedCount"] }] }
  ],
  id: "todo-123"
});

Multitenancy Support

Automatic tenant parameter handling for multitenant resources:

# Configuration
config :ash_typescript, require_tenant_parameters: true
// Tenant parameters automatically added to function signatures
const todos = await listTodos({
  fields: ["id", "title"],
  tenant: "org-123"
});

Calculations and Aggregates

Full support for Ash calculations with type inference:

# In your resource
calculations do
  calculate :full_name, :string do
    expr(first_name <> " " <> last_name)
  end
end
// TypeScript usage
const users = await listUsers({
  fields: ["id", "firstName", "lastName", "fullName"]
});

πŸš€ Advanced Features

Zod Runtime Validation

AshTypescript generates Zod schemas for all your actions, enabling runtime type checking and form validation.

Enable Zod Generation

# config/config.exs
config :ash_typescript,
  generate_zod_schemas: true,
  zod_import_path: "zod",  # or "@hookform/resolvers/zod" etc.
  zod_schema_suffix: "ZodSchema"

Generated Zod Schemas

For each action, AshTypescript generates validation schemas:

Zod Schema Examples

// Generated schema for creating a todo
export const createTodoZodSchema = z.object({
  title: z.string().min(1),
  description: z.string().optional(),
  priority: z.enum(["low", "medium", "high", "urgent"]).optional(),
  dueDate: z.date().optional(),
  tags: z.array(z.string()).optional()
});

Form Validation Functions

AshTypescript generates dedicated validation functions for client-side form validation:

import { validateCreateTodo } from './ash_rpc';

// Validate form input before submission
const validationResult = await validateCreateTodo({
  input: {
    title: "New Todo",
    priority: "high"
  }
});

if (!validationResult.success) {
  // Handle validation errors
  validationResult.errors.forEach(error => {
    console.log(`Field ${error.fieldPath}: ${error.message}`);
  });
}

Type-Safe Filtering

AshTypescript automatically generates comprehensive filter types for all resources:

import { listTodos } from './ash_rpc';

// Complex filtering with full type safety
const todos = await listTodos({
  fields: ["id", "title", "status", "priority"],
  filter: {
    and: [
      { status: { eq: "ongoing" } },
      { priority: { in: ["high", "urgent"] } },
      {
        or: [
          { dueDate: { lessThan: "2024-12-31" } },
          { isOverdue: { eq: true } }
        ]
      }
    ]
  },
  sort: "-priority,+dueDate"
});

Available Filter Operators

  • Equality: eq, notEq, in
  • Comparison: greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual
  • Logic: and, or, not
  • Relationships: Nested filtering on related resources

Typed Queries for SSR

Define reusable, type-safe queries for server-side rendering and optimized data fetching:

Define Typed Queries

defmodule MyApp.Domain do
  use Ash.Domain, extensions: [AshTypescript.Rpc]

  typescript_rpc do
    resource MyApp.Todo do
      # Regular RPC actions
      rpc_action :list_todos, :read

      # Typed query with predefined fields
      typed_query :dashboard_todos, :read do
        ts_result_type_name "DashboardTodosResult"
        ts_fields_const_name "dashboardTodosFields"

        fields [
          :id, :title, :priority, :isOverdue,
          %{
            user: [:name, :email],
            comments: [:id, :content]
          }
        ]
      end
    end
  end
end

Generated TypeScript Types

// Generated type for the typed query result
export type DashboardTodosResult = Array<InferResult<TodoResourceSchema,
  ["id", "title", "priority", "isOverdue",
   {
     user: ["name", "email"],
     comments: ["id", "content"]
   }]
>>;

// Reusable field constant for client-side refetching
export const dashboardTodosFields = [
  "id", "title", "priority", "isOverdue",
  {
    user: ["name", "email"],
    comments: ["id", "content"]
  }
] as const;

Server-Side Usage

# In your Phoenix controller
defmodule MyAppWeb.DashboardController do
  use MyAppWeb, :controller

  def index(conn, _params) do
    result = AshTypescript.Rpc.run_typed_query(:my_app, :dashboard_todos, %{}, conn)

    case result do
      %{"success" => true, "data" => todos} ->
        render(conn, "index.html", todos: todos)
      %{"success" => false, "errors" => errors} ->
        conn
        |> put_status(:bad_request)
        |> render("error.html", errors: errors)
    end
  end
end

Client-Side Refetching

// Use the same field selection for client-side updates
const refreshedTodos = await listTodos({
  fields: dashboardTodosFields,
  filter: { isOverdue: { eq: true } }
});

Flexible Field Formatting

Configure separate formatters for input parsing and output generation:

# config/config.exs
config :ash_typescript,
  # How client field names are converted to internal Elixir fields (default is :camel_case)
  input_field_formatter: :camel_case,
  # How internal Elixir fields are formatted for client consumption (default is :camel_case)
  output_field_formatter: :camel_case

Available Formatters

  • :camel_case - user_name β†’ userName
  • :pascal_case - user_name β†’ UserName
  • :snake_case - user_name β†’ user_name
  • Custom formatter: {MyModule, :format_field} or {MyModule, :format_field, [extra_args]}

Different Input/Output Formatting

# Use different formatting for input vs output
config :ash_typescript,
  input_field_formatter: :snake_case,    # Client sends snake_case
  output_field_formatter: :camel_case    # Client receives camelCase

βš™οΈ Configuration

Application Configuration

# config/config.exs
config :ash_typescript,
  # File generation
  output_file: "assets/js/ash_rpc.ts",

  # RPC endpoints
  run_endpoint: "/rpc/run",
  validate_endpoint: "/rpc/validate",

  # Field formatting
  input_field_formatter: :camel_case,
  output_field_formatter: :camel_case,

  # Multitenancy
  require_tenant_parameters: false,

  # Zod schema generation
  generate_zod_schemas: true,
  zod_import_path: "zod",
  zod_schema_suffix: "ZodSchema",

  # Custom type imports
  import_into_generated: [
    %{
      import_name: "CustomTypes",
      file: "./customTypes"
    }
  ]

Domain Configuration

defmodule MyApp.Domain do
  use Ash.Domain, extensions: [AshTypescript.Rpc]

  typescript_rpc do
    resource MyApp.Todo do
      # Standard CRUD actions
      rpc_action :list_todos, :read
      rpc_action :get_todo, :get
      rpc_action :create_todo, :create
      rpc_action :update_todo, :update
      rpc_action :destroy_todo, :destroy

      # Custom actions
      rpc_action :complete_todo, :complete
      rpc_action :archive_todo, :archive

      # Typed queries for SSR and optimized data fetching
      typed_query :dashboard_todos, :read do
        ts_result_type_name "DashboardTodosResult"
        ts_fields_const_name "dashboardTodosFields"

        fields [
          :id, :title, :priority, :status,
          %{
            user: [:name, :email],
            comments: [:id, :content]
          },
        ]
      end
    end

    resource MyApp.User do
      rpc_action :list_users, :read
      rpc_action :get_user, :get
    end
  end
end

Field Formatting

Customize how field names are formatted in generated TypeScript:

# Default: snake_case β†’ camelCase
# user_name β†’ userName
# created_at β†’ createdAt

Custom Types

Create custom Ash types with TypeScript integration:

# 1. Create custom type in Elixir
defmodule MyApp.PriorityScore do
  use Ash.Type

  def storage_type(_), do: :integer
  def cast_input(value, _) when is_integer(value) and value >= 1 and value <= 100, do: {:ok, value}
  def cast_input(_, _), do: {:error, "must be integer 1-100"}
  def cast_stored(value, _), do: {:ok, value}
  def dump_to_native(value, _), do: {:ok, value}
  def apply_constraints(value, _), do: {:ok, value}

  # AshTypescript integration
  def typescript_type_name, do: "CustomTypes.PriorityScore"
end
// 2. Create TypeScript type definitions in customTypes.ts
export type PriorityScore = number;

export type ColorPalette = {
  primary: string;
  secondary: string;
  accent: string;
};
# 3. Use in your resources
defmodule MyApp.Todo do
  use Ash.Resource, domain: MyApp.Domain

  attributes do
    uuid_primary_key :id
    attribute :title, :string, public?: true
    attribute :priority_score, MyApp.PriorityScore, public?: true
  end
end

The generated TypeScript will automatically include your custom types:

// Generated TypeScript includes imports
import * as CustomTypes from "./customTypes";

// Your resource types use the custom types
interface TodoFieldsSchema {
  id: string;
  title: string;
  priorityScore?: CustomTypes.PriorityScore | null;
}

πŸ› οΈ Mix Tasks

Code Generation Commands

mix ash.codegen (Recommended)

Preferred approach for generating TypeScript types along with other Ash extensions in your project.

# Generate types for all Ash extensions including AshTypescript
mix ash.codegen --dev

# With custom output location
mix ash.codegen --dev --output "assets/js/ash_rpc.ts"

When to use: When you have multiple Ash extensions (AshPostgres, etc.) and want to run codegen for all of them together. This is the recommended approach for most projects.

mix ash_typescript.codegen (Specific)

Generate TypeScript types, RPC clients, Zod schemas, and validation functions only for AshTypescript.

When to use: When you want to run codegen specifically for AshTypescript only in your project.

Options:

  • --output - Output file path (default: assets/js/ash_rpc.ts)
  • --run_endpoint - RPC run endpoint (default: /rpc/run)
  • --validate_endpoint - RPC validate endpoint (default: /rpc/validate)
  • --check - Check if generated code is up to date (useful for CI)
  • --dry_run - Print generated code without writing to file

Generated Content:

  • TypeScript interfaces for all resources
  • RPC client functions for each action
  • Filter input types for type-safe querying
  • Zod validation schemas (if enabled)
  • Form validation functions
  • Typed query constants and types
  • Custom type imports

Examples:

# Basic generation (AshTypescript only)
mix ash_typescript.codegen

# Custom output location
mix ash_typescript.codegen --output "frontend/src/api/ash.ts"

# Custom RPC endpoints
mix ash_typescript.codegen \
  --run_endpoint "/api/rpc/run" \
  --validate_endpoint "/api/rpc/validate"

# Check if generated code is up to date (CI usage)
mix ash_typescript.codegen --check

# Preview generated code without writing to file
mix ash_typescript.codegen --dry_run

πŸ“– API Reference

Generated Code Structure

AshTypescript generates:

  1. TypeScript interfaces for all resources with metadata for field selection
  2. RPC client functions for each exposed action
  3. Validation functions for client-side form validation
  4. Filter input types for type-safe querying with comprehensive operators
  5. Zod schemas for runtime validation (when enabled)
  6. Typed query constants and result types for SSR
  7. Field selection types for type-safe field specification
  8. Custom type imports for external TypeScript definitions
  9. Enum types for Ash enum types
  10. Utility functions for headers and CSRF protection

Generated Functions

For each rpc_action in your domain, AshTypescript generates:

// For rpc_action :list_todos, :read
function listTodos<Fields extends ListTodosFields>(params: {
  fields: Fields;
  filter?: TodoFilterInput;
  sort?: string;
  page?: PaginationOptions;
  headers?: Record<string, string>;
  fetchOptions?: RequestInit;
  customFetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
}): Promise<ListTodosResult<Fields>>;

// Validation function for list_todos
function validateListTodos(params: {
  input: ListTodosInput;
  headers?: Record<string, string>;
  fetchOptions?: RequestInit;
  customFetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
}): Promise<ValidateListTodosResult>;

// For rpc_action :create_todo, :create
function createTodo<Fields extends CreateTodosFields>(params: {
  fields: Fields;
  input: CreateTodoInput;
  headers?: Record<string, string>;
  fetchOptions?: RequestInit;
  customFetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
}): Promise<CreateTodoResult<Fields>>;

// Validation function for create_todo
function validateCreateTodo(params: {
  input: CreateTodoInput;
  headers?: Record<string, string>;
  fetchOptions?: RequestInit;
  customFetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
}): Promise<ValidateCreateTodoResult>;

// Zod schemas (when enabled)
export const createTodoZodSchema: z.ZodObject<...>;
export const listTodosZodSchema: z.ZodObject<...>;

Utility Functions

// CSRF protection for Phoenix applications
function getPhoenixCSRFToken(): string | null;
function buildCSRFHeaders(): Record<string, string>;

πŸ“‹ Requirements

  • Elixir ~> 1.15
  • Ash ~> 3.5
  • AshPhoenix ~> 2.0 (for RPC endpoints)

πŸ› Troubleshooting

Common Issues

TypeScript compilation errors:

  • Ensure generated types are up to date: mix ash_typescript.codegen
  • Check that all referenced resources are properly configured

RPC endpoint errors:

  • Verify AshPhoenix RPC endpoints are configured in your router
  • Check that actions are properly exposed in domain RPC configuration

Type inference issues:

  • Ensure all attributes are marked as public? true
  • Check that relationships are properly defined

Debug Commands

# Check generated output without writing
mix ash_typescript.codegen --dry_run

# Validate TypeScript compilation
cd assets/js && npx tsc --noEmit

# Check for updates
mix ash_typescript.codegen --check

🀝 Contributing

Development Setup

# Clone the repository
git clone https://github.com/ash-project/ash_typescript.git
cd ash_typescript

# Install dependencies
mix deps.get

# Run tests
mix test

# Generate test types
mix test.codegen

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ†˜ Support


Built with ❀️ by the Ash Framework team

Generate once, type everywhere. Make your Elixir-TypeScript integration bulletproof.

About

Automatic TypeScript type generation for Ash resources and actions

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published