Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a generator for TypeScript meta-definition #256

Open
thekemlin opened this issue Nov 1, 2022 · 2 comments
Open

Add a generator for TypeScript meta-definition #256

thekemlin opened this issue Nov 1, 2022 · 2 comments

Comments

@thekemlin
Copy link

thekemlin commented Nov 1, 2022

This issue is a feature request.

What feature

A generator which output TypeScript code which has no runtime and protobuf encode/decode relative logic.

It should be a clean TypeScript code one-to-one mapping to proto schema.

For example, the output code is:

import { Method, Service, RequestType, ResponseType, method, service } from "./meta-definition";

interface SearchRequest {
  query: string;
  page_number: number;
  result_per_page: number;
}

interface SearchResponse {
  paths: string[];
}

interface ListRequest {
  path: string;
}

interface ListResponse {
  paths: string[];
}

const search = method<'search', SearchRequest, SearchResponse>('search');

const list = method<'list', ListRequest, ListResponse>('list');

const fileService = service('file', [search, list]);

And the ./meta-definition is:

import { readonlyArray, readonlyRecord } from "fp-ts";
import { pipe } from "fp-ts/lib/function";

export interface Service<S extends string, M extends readonly Method<string, unknown, unknown>[]> {
    name: S;
    methods: {
        [K in M[number] as K["name"]]: K;
    };
}

export interface Method<S extends string, R, A> {
    name: S;
}

export type ResponseType<M extends Method<string, unknown, unknown>> = M extends Method<string, unknown, infer A> ? A : never;

export type RequestType<M extends Method<string, unknown, unknown>> = M extends Method<string, infer R, unknown> ? R : never;

export const service = <S extends string, M extends readonly Method<string, unknown, unknown>[]>(name: S, methods: M): Service<S, M> => ({
    name,
    methods: pipe(
        methods,
        readonlyArray.map((s) => [s.name, s] as const),
        readonlyRecord.fromEntries,
    ) as any
});

export const method = <S extends string, R, A>(name: S): Method<S, R, A> => ({
    name
});

Why

There are common use scenarios which are only using proto as API protocol.

The backend use grpc-gateway to expose HTTP server.

And the frontend use traditional HTTP to communicate with backend.

In such case, trpc like client is desired.

For leveraging meta-programming, such as Proxy, schema isomorphic TypeScript definition is required.

I am happy to make an MR for that.

@disjukr
Copy link
Member

disjukr commented Nov 1, 2022

The existing structure is also made protocol agnostic.
You can use a different protocol instead of grpc by injecting the client impl when creating service client instance.

You can find the various protocol implementation here. (See grpc-client, grpc-web-client, frpc-client)

As you said, it contains binary encoding and decoding runtime code.
But depending on the client impl detail, you don't actually need to use the encoding and decoding cost at runtime.

And I'm not sure if it's possible to make the existing code follow the ./meta-definition you suggested.

@disjukr
Copy link
Member

disjukr commented Nov 1, 2022

And in my opinion, if you put the value emitted by grpc gateway into trpc without a separate decoding process, there will be a problem.
Because protobuf's canonical json serialization format (grpc gateway will send the message in this way) and pbkit's message type are different. (I intentionally designed it differently to handle the map type and oneof easily in TypeScript)
So you need a runtime to decode the json sent by grpc-gateway into pbkit message type.

And you probably don't need trpc to create a client that talks to grpc-gateway with pbkit.
I think just making the client impl with fetch api (or axios) for grpc-gateway is enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants