better-grpc 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -4
- package/dist/index.cjs +313 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -12
- package/dist/index.d.ts +41 -12
- package/dist/index.js +314 -53
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
# better-grpc
|
|
4
6
|
|
|
5
7
|
> Simple, typed gRPC for TypeScript
|
|
@@ -38,7 +40,7 @@ yarn add better-grpc
|
|
|
38
40
|
Create an abstract class that extends `Service` to define your service. Use the `server` and `client` helpers to define where your function is implemented and executed.
|
|
39
41
|
|
|
40
42
|
```typescript
|
|
41
|
-
import { Service, client, server } from 'better-grpc';
|
|
43
|
+
import { Service, client, server, bidi } from 'better-grpc';
|
|
42
44
|
|
|
43
45
|
abstract class MyService extends Service('MyService') {
|
|
44
46
|
// This function is implemented and executed on the server.
|
|
@@ -46,6 +48,9 @@ abstract class MyService extends Service('MyService') {
|
|
|
46
48
|
|
|
47
49
|
// This function is implemented and executed on the client.
|
|
48
50
|
log = client<(message: string) => void>();
|
|
51
|
+
|
|
52
|
+
// This function supports bidirectional streaming between client and server.
|
|
53
|
+
chat = bidi<(message: string) => void>();
|
|
49
54
|
}
|
|
50
55
|
```
|
|
51
56
|
|
|
@@ -104,6 +109,78 @@ await server.MyService.log('Greeting from server');
|
|
|
104
109
|
// The client's console will show: '[Server]: Greeting from server'
|
|
105
110
|
```
|
|
106
111
|
|
|
112
|
+
### 6. Use bidirectional streams
|
|
113
|
+
|
|
114
|
+
Bidirectional gRPCs expose a function that both emits values (when you invoke it) and acts as an async iterator so you can consume the opposite side's messages.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// Client usage
|
|
118
|
+
await client.MyService.chat('hello from client'); // emit to the server
|
|
119
|
+
|
|
120
|
+
for await (const [message] of client.MyService.chat) {
|
|
121
|
+
console.log('Server replied:', message);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Server usage mirrors the client
|
|
126
|
+
await server.MyService.chat('hello from server'); // emit to the client
|
|
127
|
+
|
|
128
|
+
for await (const [message] of server.MyService.chat) {
|
|
129
|
+
console.log('Client replied:', message);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 7. Attach typed metadata
|
|
135
|
+
|
|
136
|
+
Define metadata requirements with [Zod](https://github.com/colinhacks/zod) schemas, and `better-grpc` will automatically type the context on both sides and marshal the payload into gRPC metadata.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { Service, server, bidi } from 'better-grpc';
|
|
140
|
+
import { z } from 'zod';
|
|
141
|
+
|
|
142
|
+
abstract class GreeterService extends Service('GreeterService') {
|
|
143
|
+
greet = server<(name: string) => string>()({
|
|
144
|
+
metadata: z.object({ requestId: z.string() }),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
chat = bidi<(message: string) => void>()({
|
|
148
|
+
metadata: z.object({ room: z.string() }),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Server implementations receive the typed metadata as the first argument:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
const GreeterServerImpl = GreeterService.Server({
|
|
157
|
+
async greet(context, name) {
|
|
158
|
+
console.log('Request', context.metadata.requestId);
|
|
159
|
+
return `Hello, ${name}!`;
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
On the client, unary calls that require metadata expose a `.withMeta()` helper, and bidi streams provide a `.context()` helper that must be awaited and called before sending messages (the bidi stream will be established after calling `.context()`):
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
await client.GreeterService.greet('Ada').withMeta({ requestId: crypto.randomUUID() });
|
|
168
|
+
|
|
169
|
+
await client.GreeterService.chat.context({
|
|
170
|
+
metadata: { room: 'general' },
|
|
171
|
+
});
|
|
172
|
+
// you must provide the context before calling the bidi function;
|
|
173
|
+
// otherwise, it will continue to wait.
|
|
174
|
+
await client.GreeterService.chat('hello from client');
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
On the server side, the bidi function expose a `.context` value that can be used to access metadata:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const chatContext = await server.GreeterService.chat.context;
|
|
181
|
+
console.log(chatContext.metadata.room); // 'general'
|
|
182
|
+
````
|
|
183
|
+
|
|
107
184
|
## Why `better-grpc`?
|
|
108
185
|
|
|
109
186
|
The traditional workflow for creating gRPC services with TypeScript involves writing `.proto` files, using `protoc` to generate TypeScript code, and then using that generated code. This process can be cumbersome and result in a disconnect between your service definition and your code.
|
|
@@ -123,11 +200,15 @@ A factory function that creates an abstract service class.
|
|
|
123
200
|
|
|
124
201
|
- `server<T>()`
|
|
125
202
|
|
|
126
|
-
|
|
203
|
+
Defines a server-side unary function signature. `T` should be a function type. Call the returned descriptor with `({ metadata: z.object({...}) })` to require typed metadata for that RPC. Client code then calls `client.MyService.fn(...args).withMeta({...})`, and server handlers receive the context object as the first argument.
|
|
127
204
|
|
|
128
205
|
- `client<T>()`
|
|
129
206
|
|
|
130
|
-
|
|
207
|
+
Defines a client-side unary function signature. `T` should be a function type.
|
|
208
|
+
|
|
209
|
+
- `bidi<T>()`
|
|
210
|
+
|
|
211
|
+
Defines a bidirectional stream signature. `T` should be a function type that returns `void`. Like `server()`, you can pass `({ metadata: schema })` to type the attached metadata; client stubs expose `bidiFn.context({ metadata })` and server stubs expose `await bidiFn.context` to read it.
|
|
131
212
|
|
|
132
213
|
- `createGrpcServer(port: number, ...services: ServiceImpl[])`
|
|
133
214
|
|
|
@@ -164,4 +245,4 @@ better-grpc: 126.681042ms
|
|
|
164
245
|
|
|
165
246
|
## License
|
|
166
247
|
|
|
167
|
-
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
248
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|