Skip to content

ts-client: generate contracts for message-only proto files #133

@elzalem

Description

@elzalem

Is your feature request related to a problem?
Yes.

protoc-gen-ts-client is currently designed around services. If a .proto file contains only messages and enums, TypeScript users do not get generated contracts even though those types are still useful in
frontend apps, shared SDKs, and cross-language integrations.

This makes TypeScript behave differently from other contract-generation use cases and makes simple proto files harder to reuse.

Describe the solution you'd like
Allow protoc-gen-ts-client to generate TypeScript types for message-only proto files, even when the file has no service definitions.

This should generate:

  • message interfaces
  • enum types
  • shared error types if needed

It should not require an HTTP client class when there are no services.

Describe alternatives you've considered
Current workaround:

  • add a dummy service just to force generation
  • duplicate the types manually in TypeScript
  • write a separate custom generator for contract-only files

These workarounds add maintenance cost and create drift between proto definitions and generated TS types.

Use Cases

  1. Shared contracts between backend and frontend where some proto files only define data models.
  2. Contract generation across multiple languages, where TS should be able to consume the same message-only files as C# and Python.
  3. Frontend apps that need strongly typed DTOs from proto without needing generated RPC client code.

Example
Proto file:

syntax = "proto3";

package test.messageonly.v1;

message Worker {
  string worker_id = 1;
  string display_name = 2;
}

Current behavior:

  • no useful TS output if there is no service

Expected behavior:

export interface Worker {
  workerId: string;
  displayName: string;
}

Optional support for raw proto field names is also useful in some integrations:

export interface Worker {
  worker_id: string;
  display_name: string;
}

Additional Context
This is mainly a contract-generation gap, not an HTTP client feature gap.

The goal is to let TypeScript participate cleanly in contract-only generation flows, while preserving existing ts-client behavior for normal service-based client generation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions