npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

proto2fetch

v1.3.0

Published

Generate TypeScript-friendly API client from protobuf definitions with ky HTTP client

Readme

proto2fetch

🚀 Generate TypeScript-friendly API clients from protobuf definitions with modern HTTP client support.

https://www.npmjs.com/package/proto2fetch

npm version TypeScript MIT License

✨ Features

  • 🔥 Auto-generate TypeScript API clients from protobuf definitions
  • 🚀 Modern HTTP client powered by ky
  • 💪 Full type safety with TypeScript support
  • 🛠️ Request/Response interceptors and error handling
  • 📦 NPM ready - publish to public or private registries
  • Flexible authentication - JWT, Paseto, custom auth with plugin architecture
  • 🔄 Dynamic token management - Update authentication tokens at runtime
  • 🎯 Framework agnostic - works with any TypeScript/JavaScript project
  • 🔧 Highly configurable - customize everything to your needs

📦 Installation

# Install as dev dependency for code generation
npm install -D proto2fetch

# Install as runtime dependency for generated clients
npm install proto2fetch

🚀 Quick Start

1. Generate API Client

# Generate from your protobuf files
npx proto2fetch --proto-path ./proto --output-dir ./src/generated --base-url https://api.example.com

2. Use Generated Client

import { CleanGoAPIClient } from './generated/client';
import { SimpleAuth } from 'proto2fetch/runtime';

const client = new CleanGoAPIClient({
  baseUrl: 'https://api.example.com',
  auth: new SimpleAuth('your-auth-token')
});

// Type-safe API calls
const users = await client.getUsers({
  pagination: { page: 1, size: 10 },
  filter: { isActive: true }
});

📖 Usage

Command Line Interface

proto2fetch [options]

Options:
  --proto-path <path>           Path to protobuf files directory
  --output-dir <path>           Output directory for generated files
  --base-url <url>              Base URL for API client (default: current hostname)
  --package-name <name>         Name for generated package
  --client-name <name>          Name for generated client class
  --include-comments            Include comments in generated code
  --generate-filter-builders    Generate filter builder classes
  --generate-sort-builders      Generate sort builder classes
  --date-type <type>            Type for dates: Date|string
  --bigint-type <type>          Type for bigints: bigint|string
  --config <path>               Path to configuration file
  --help                        Show help message
  --version                     Show version

Configuration File

Create proto2fetch.config.js in your project root:

module.exports = {
  protoPath: './proto',
  outputDir: './src/generated',
  baseUrl: 'https://api.example.com',
  packageName: 'my-api-client',
  clientName: 'MyAPIClient',
  includeComments: true,
  generateFilterBuilders: true,
  generateSortBuilders: true,
  dateType: 'Date', // or 'string'
  bigintType: 'string' // or 'bigint'
};

Programmatic Usage

import { generate } from 'proto2fetch';

await generate({
  protoPath: './proto',
  outputDir: './generated',
  baseUrl: 'https://api.example.com',
  clientName: 'MyAPIClient'
});

🎯 Generated Client Features

Basic API Calls

// User management
const createResponse = await client.createUser({
  name: 'John Doe',
  email: '[email protected]',
  phone: '+1-555-0123',
  password: 'secure123'
});

const currentUser = await client.getCurrentUser({});
const users = await client.getUsers({ pagination: { page: 1, size: 10 } });

Authentication

proto2fetch supports multiple authentication methods through a flexible plugin architecture, with dynamic token management for real-world applications:

JWT Authentication with Auto-Refresh

import { JWTAuth } from 'proto2fetch/runtime';

const client = new MyAPIClient({
  baseUrl: 'https://api.example.com',
  auth: new JWTAuth('your-jwt-token', {
    onExpired: async () => {
      const response = await fetch('/auth/refresh', { method: 'POST' });
      const { token } = await response.json();
      return token;
    }
  })
});

Paseto Authentication

import { SimpleAuth } from 'proto2fetch/runtime';

const client = new MyAPIClient({
  baseUrl: 'https://api.example.com',
  auth: new SimpleAuth('your-paseto-token', 'Bearer')
});

Custom Authentication

import { CustomAuth } from 'proto2fetch/runtime';

const client = new MyAPIClient({
  baseUrl: 'https://api.example.com',
  auth: new CustomAuth(async () => ({
    'X-API-Key': await getApiKey(),
    'X-Timestamp': Date.now().toString(),
    'X-Signature': await generateSignature()
  }))
});

Dynamic Token Updates

Method 1: Direct Token Update

import { KyAPIClient, SimpleAuth } from 'proto2fetch/runtime';

const client = new MyAPIClient({
  baseUrl: 'https://api.example.com',
  auth: new SimpleAuth('initial-token')
}) as KyAPIClient;

// User logs in, get new token
async function handleUserLogin() {
  const loginResponse = await client.request('POST', '/auth/login', {
    username: '[email protected]',
    password: 'password'
  }, { skipAuth: true });
  
  // Update existing auth provider
  client.updateAuthToken(loginResponse.token);
}

Method 2: Real-time Dynamic Token

// Token stored in variable, supports real-time updates
let currentToken = null;

const client = new MyAPIClient({
  baseUrl: 'https://api.example.com',
  auth: new SimpleAuth(() => currentToken || 'fallback-token')
});

// Token updates automatically affect all requests
function updateToken(newToken: string) {
  currentToken = newToken;
}

Method 3: Async Token Provider

const client = new MyAPIClient({
  baseUrl: 'https://api.example.com',
  auth: new SimpleAuth(async () => {
    // Fetch token from secure storage
    const token = await getTokenFromSecureStorage();
    return token || await refreshTokenFromServer();
  })
});

User Login/Logout Workflow

class UserAuthManager {
  private apiClient = new MyAPIClient({
    baseUrl: 'https://api.example.com'
    // No auth initially
  }) as KyAPIClient;
  
  async login(username: string, password: string) {
    // Login without authentication
    const response = await this.apiClient.request('POST', '/auth/login', {
      username, password
    }, { skipAuth: true });
    
    const { token, refreshToken } = response;
    
    // Set up authentication for future requests
    this.apiClient.updateAuthProvider(new JWTAuth(token, {
      onExpired: async () => {
        const refreshResponse = await fetch('/auth/refresh', {
          method: 'POST',
          headers: { 'Authorization': `Bearer ${refreshToken}` }
        });
        const { accessToken } = await refreshResponse.json();
        return accessToken;
      }
    }));
  }
  
  logout() {
    this.apiClient.clearAuthToken();
  }
  
  // Now all API calls are authenticated
  async getUserProfile() {
    return this.apiClient.request('GET', '/user/profile');
  }
}

Simple Token (Legacy Support)

// Backward compatible - automatically converts to SimpleAuth
const client = new MyAPIClient({
  baseUrl: 'https://api.example.com',
  auth: { token: 'your-token', tokenType: 'Bearer' }
});

// Traditional method still works
client.updateAuthToken(newToken);

Error Handling

import { APIError } from 'proto2fetch/runtime';

try {
  await client.createUser(invalidData);
} catch (error) {
  if (error instanceof APIError) {
    console.log('Status:', error.status);
    console.log('Code:', error.code);
    console.log('Message:', error.message);
    
    // Get field-specific errors
    const fieldErrors = error.getFieldErrors();
    console.log('Field errors:', fieldErrors);
    
    // Check error type
    if (error.isValidationError) {
      // Handle validation error
    } else if (error.isAuthError) {
      // Handle authentication error
    }
  }
}

Advanced Features

// Pagination with filtering and sorting
const response = await client.getUsers({
  pagination: { page: 1, size: 20 },
  filter: {
    isActive: true,
    nameLike: 'john',
    createdAfter: new Date('2024-01-01')
  },
  sort: [
    { field: 'created_at', direction: 'desc' },
    { field: 'name', direction: 'asc' }
  ]
});

// Using filter builders (if enabled)
const userFilter = new UserFilterBuilder()
  .name('John')
  .isActive(true)
  .createdAfter(new Date('2024-01-01'))
  .build();

const users = await client.getUsers({
  filter: userFilter
});

Request/Response Hooks

const client = new MyAPIClient({
  baseUrl: 'https://api.example.com',
  hooks: {
    beforeRequest: [
      (request) => {
        console.log('Making request:', request.url);
        return request;
      }
    ],
    afterResponse: [
      (request, response) => {
        console.log('Response received:', response.status);
        return response;
      }
    ],
    beforeError: [
      (error) => {
        console.error('Request failed:', error);
        return error;
      }
    ]
  }
});

🏗️ Architecture

proto2fetch consists of three main components:

  1. Generator - Parses protobuf files and generates TypeScript code
  2. Runtime - HTTP client, authentication, and error handling
  3. Types - TypeScript type definitions and utilities
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   protobuf      │ →  │   generator     │ →  │   TypeScript    │
│                 │    │                 │    │   API Client    │
└─────────────────┘    └─────────────────┘    └─────────────────┘
                                  ↓
                          ┌─────────────────┐
                          │   runtime       │
                          └─────────────────┘

🔧 Configuration Options

Generator Options

interface GeneratorOptions {
  protoPath: string;              // Path to protobuf files
  outputDir: string;              // Output directory
  baseUrl?: string;               // API base URL
  packageName?: string;           // Generated package name
  clientName?: string;            // Client class name
  includeComments?: boolean;      // Include JSDoc comments
  generateFilterBuilders?: boolean; // Generate filter helpers
  generateSortBuilders?: boolean;   // Generate sort helpers
  dateType?: 'Date' | 'string';     // Date representation
  bigintType?: 'bigint' | 'string'; // BigInt representation
}

Client Configuration

interface APIClientConfig {
  baseUrl: string;
  timeout?: number;
  retry?: {
    limit: number;
    methods?: string[];
    statusCodes?: number[];
  };
  hooks?: {
    beforeRequest?: RequestHook[];
    beforeRetry?: RetryHook[];
    beforeError?: ErrorHook[];
    afterResponse?: ResponseHook[];
  };
  auth?: 
    | AuthProvider                    // New plugin-based auth
    | {                               // Legacy support
        token?: string;
        tokenType?: 'Bearer' | 'Basic';
        refreshTokenHandler?: () => Promise<string>;
      };
  debug?: boolean;
}

Client Authentication Management API

interface APIClient {
  // Basic request method
  request<T>(method: string, path: string, data?: any, options?: RequestOptions): Promise<T>;
  
  // Authentication management methods
  updateAuthToken(token: string): void;        // Update existing provider token or create new SimpleAuth
  updateAuthProvider(provider: AuthProvider): void; // Replace auth provider
  clearAuthToken(): void;                      // Remove authentication
}

Method Descriptions:

  • updateAuthToken(): Tries to update existing provider's token; falls back to creating new SimpleAuth instance
  • updateAuthProvider(): Completely replaces the authentication provider
  • clearAuthToken(): Removes all authentication

🧪 Testing

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Run tests in watch mode
npm run test:watch

🤝 Contributing

Contributions are welcome! Please read our Contributing Guide for details.

📄 License

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

🙏 Acknowledgments

📚 Related Projects


Made with ❤️ for the TypeScript community