netron_axum_ts

Crates.ionetron_axum_ts
lib.rsnetron_axum_ts
version0.0.2
created_at2025-11-27 06:42:59.826652+00
updated_at2025-11-27 06:44:39.723642+00
descriptionGenerate TypeScript TRPC client code from Axum Rust server code.
homepage
repositoryhttps://github.com/netrondev/netron-axum
max_upload_size
id1953202
size46,178
Rouan van der Ende (rvdende)

documentation

README

netron_axum_ts

A code generator that automatically creates TypeScript tRPC routers from Rust #[axumhandler] route definitions.

Overview

This tool bridges Rust backend APIs (built with the netron framework) and TypeScript frontend applications (using tRPC), providing:

  • Type-safe API calls - TypeScript types generated from Rust structs
  • Automatic Zod validation - Runtime type checking with Zod schemas
  • tRPC integration - Native tRPC router generation
  • Single source of truth - Rust types define both backend and frontend

Installation

This is a standalone Rust crate with a CLI tool. Build it with:

cargo build --release

Usage

Basic Command

netron-axum-ts \
  --input sfprs/src/api/products.rs \
  --output sfpt3/src/server/api/routers/products.generated.ts \
  --router-name productsRouter

With npm Script

Add to your package.json:

{
  "scripts": {
    "generate:api": "cd ../netron_axum_ts && cargo run --release -- -i ../sfprs/src/api/products.rs -o ../sfpt3/src/server/api/routers/products.generated.ts --router-name productsRouter"
  }
}

Then run:

pnpm generate:api

Input: Rust Routes

The tool parses Rust files looking for functions annotated with #[axumhandler]:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Brand {
    brand_name: String,
}

#[axumhandler(method = "GET", path = "/api/brands")]
pub async fn brandlist() -> Result<Vec<Brand>, AppError> {
    // Implementation
}

#[axumhandler(path = "/api/models")]
pub async fn modellist(brand: Brand) -> Result<Vec<String>, AppError> {
    // Implementation
}

Output: TypeScript tRPC Router

Generates a fully-typed tRPC router:

// Auto-generated
import { z } from "zod";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";

const RUST_API_URL = process.env.RUST_API_URL || "http://localhost:4000";

export const BrandSchema = z.object({
  brand_name: z.string(),
});

export type Brand = z.infer<typeof BrandSchema>;

export const productsRouter = createTRPCRouter({
  brandlist: publicProcedure
    .input(z.void())
    .query(async () => {
      const response = await fetch(`${RUST_API_URL}/api/brands`);
      if (!response.ok) throw new Error('API request failed');
      return BrandSchema.array().parse(await response.json());
    }),

  modellist: publicProcedure
    .input(BrandSchema)
    .mutation(async ({ input }) => {
      const response = await fetch(`${RUST_API_URL}/api/models`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(input),
      });
      if (!response.ok) throw new Error('API request failed');
      return z.string().array().parse(await response.json());
    }),
});

Frontend Usage

In your Next.js application:

// Server-side
const brands = await api.products.brandlist();

// Client-side
const { data: brands } = api.products.brandlist.useQuery();
const modelsMutation = api.products.modellist.useMutation();

Type Mapping

Rust Type Zod Schema TypeScript Type
String z.string() string
i32, u32, f64 z.number() number
bool z.boolean() boolean
Vec<T> z.array(T) T[]
Option<T> z.nullable(T) T | null
Result<T, E> (unwrapped to T) T
Custom structs z.object({...}) interface

Features

Serde Attribute Support

Handles #[serde(rename = "...")]:

struct Product {
    #[serde(rename = "type")]
    product_type: String,  // Becomes "type" in JSON/TypeScript
}

HTTP Method Mapping

  • GET.query() (read-only)
  • POST/PUT/DELETE.mutation() (state-changing)

Environment Configuration

Set the backend URL via environment variable:

# .env
RUST_API_URL=http://localhost:4000

CLI Options

netron-axum-ts [OPTIONS]

Options:
  -i, --input <FILE>           Input Rust source file
  -o, --output <FILE>          Output TypeScript file
      --api-url <URL>          Backend URL [default: http://localhost:4000]
      --router-name <NAME>     Router name [default: derived from filename]
  -h, --help                   Print help
  -V, --version                Print version

Requirements

Rust Side

  • Rust functions must be annotated with #[axumhandler]
  • Types must implement Serialize and Deserialize

TypeScript Side

  • tRPC v11+
  • Zod v3+
  • Next.js (for T3 Stack integration)

Limitations

  • Only supports named struct fields (no tuple structs)
  • Generic types beyond Vec, Option, Result are not supported
  • Requires Rust backend to be running for API calls

Development

Run tests:

cargo test

Build for release:

cargo build --release

License

Same as parent project.

Commit count: 0

cargo fmt