Skip to content

Commit 50a2832

Browse files
chore: prepare for release
1 parent cd46d48 commit 50a2832

12 files changed

Lines changed: 107 additions & 271 deletions

File tree

.website/microservices/grpc.md

Lines changed: 18 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,9 @@ class GreeterService extends GreeterServiceBase {
8080
}
8181
```
8282

83-
::: info
84-
Right now the methods implementation won't affect the result of the gRPC calls done through Serinus, since the actual handling of the calls will be done by the controllers. However, it's a good practice to implement the methods in case you want to use the service outside of Serinus.
85-
86-
Also we are working on a feature that will also call the service methods from the controllers, so stay tuned for that!
87-
:::
88-
8983
### Configure the gRPC transport
9084

91-
To instantiate a gRPC microservice application, you can use the `GrpcTransport` transport layer from the `serinus_microservices` package. You will need to provide the list of gRPC services to the `GrpcOptions`.
85+
To instantiate a gRPC microservice application, you can use the `GrpcTransport` transport layer from the `serinus_microservices` package.
9286

9387
```dart
9488
final microservice = await serinus.createMicroservice(
@@ -97,15 +91,28 @@ final microservice = await serinus.createMicroservice(
9791
GrpcOptions(
9892
port: 50051,
9993
host: InternetAddress.loopbackIPv4,
100-
services: [
101-
GreeterService(),
102-
],
10394
),
10495
),
10596
);
10697
```
10798

108-
As you can see we are passing the concrete implementation of the `GreeterService` to the `services` option of the `GrpcOptions` instead of the generated `GreeterServiceBase` abstract class.
99+
We also need to register the `GreeterService` in the `AppModule` so that it can be injected into the gRPC controller:
100+
101+
```dart
102+
class GreeterController extends GrpcServiceController {
103+
GreeterController(): super(service: GreeterService());
104+
}
105+
106+
class AppModule extends Module {
107+
108+
AppModule(): super(
109+
controllers: [GreeterController()],
110+
);
111+
112+
}
113+
```
114+
115+
And that's it! From this moment on the gRPC service will take care of handling incoming gRPC requests and routing them to the appropriate controller methods based on the service and method names defined in the proto file. Also since you have tapped into the full power of the Serinus framework, you can use all the features of Serinus in your gRPC controllers, such as dependency injection, hooks, filters and more.
109116

110117
## Options
111118

@@ -119,112 +126,3 @@ The `GrpcOptions` class allows you to configure various settings for the gRPC tr
119126
| codecRegistry | An optional codec registry for custom serialization and deserialization of messages. |
120127
| keepAliveOptions | Options for configuring keep-alive behavior for the gRPC server. |
121128
| security | An optional security configuration for the gRPC server, such as TLS settings. |
122-
123-
## gRPC Controller
124-
125-
Since Controllers in Serinus are transport-agnostic, to create a gRPC controller you need to augment your own controller with the `GrpcController` mixin from the `serinus_microservices` package.
126-
127-
```dart
128-
import 'package:serinus_microservices/serinus_microservices.dart';
129-
130-
class MyGrpcController extends Controller with GrpcController {
131-
132-
MyGrpcController() : super('/my_grpc_controller') {
133-
grpc<HelloRequest, HelloReply>(
134-
GrpcRoute(GreeterService, 'SayHello'),
135-
(call, request, context) async {
136-
final reply = HelloReply()..message = context.use<GreeterProvider>().getGreeting(request.name);
137-
return reply;
138-
}
139-
);
140-
}
141-
142-
}
143-
```
144-
145-
As you can see to define a gRPC route you need to use the `grpc` method provided by the `GrpcController` mixin. This method takes a `GrpcRoute` object that specifies the service and method names, and a handler function that will be called when the route is invoked.
146-
147-
The GrpcRoute constructor takes two parameters:
148-
149-
- The gRPC service class (the concrete implementation, **not** the abstract class).
150-
- The name of the gRPC method as defined in the proto file.
151-
152-
The reason behind the need to specify the concrete implementation of the service is that Serinus uses it as the key to identify the service and route the incoming requests to the correct controller.
153-
154-
> [!IMPORTANT]
155-
> Make sure to use the concrete implementation of the gRPC service when defining the `GrpcRoute`, otherwise Serinus won't be able to route the requests correctly and you will get an error at runtime.
156-
157-
In this example, we defined a unary gRPC method `SayHello` in the `GreeterService`. The handler function takes three parameters: the `ServiceCall` object, the request message, and the `GrpcRouteContext` object.
158-
159-
## gRPC Streaming
160-
161-
Serinus also supports gRPC streaming methods.
162-
163-
### Define a streaming gRPC method
164-
165-
Let's define a server streaming method in our `Greeter` service:
166-
167-
```proto
168-
// The greeting service definition.
169-
service Greeter {
170-
// Sends a greeting
171-
rpc SayHello (HelloRequest) returns (HelloReply) {}
172-
rpc LotsOfHellos (stream HelloRequest) returns (stream HelloReply) {}
173-
}
174-
175-
// The request message containing the user's name.
176-
message HelloRequest {
177-
string name = 1;
178-
}
179-
180-
// The response message containing the greetings
181-
message HelloReply {
182-
string message = 1;
183-
}
184-
```
185-
186-
As before, you need to regenerate the gRPC code after modifying the proto file.
187-
188-
```console
189-
protoc --dart_out=grpc:lib/generated -Iprotos protos/greeter.proto
190-
```
191-
192-
### Implement the streaming method
193-
194-
Now we need to define the method in the concrete implementation of the `GreeterService`:
195-
196-
```dart
197-
class GreeterService extends GreeterServiceBase {
198-
@override
199-
Stream<HelloReply> lotsOfHellos(ServiceCall call, Stream<HelloRequest> requestStream) async* {
200-
await for (final request in requestStream) {
201-
final reply = HelloReply()..message = 'Hello, ${request.name}!';
202-
yield reply;
203-
}
204-
}
205-
206-
@override
207-
Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async {
208-
final reply = HelloReply()..message = 'Hello, ${request.name}!';
209-
return reply;
210-
}
211-
}
212-
```
213-
214-
### Define the streaming route
215-
216-
And now we can define the streaming route in our gRPC controller using the `grpcStream` method provided by the `GrpcController` mixin.
217-
218-
```dart
219-
grpcStream<HelloRequest, HelloReply>(
220-
GrpcRoute(GreeterService, 'StreamHellos'),
221-
(call, requestStream, context) async* {
222-
await for (final request in requestStream) {
223-
final reply = HelloReply()..message = context.use<GreeterProvider>().getGreeting(request.name);
224-
yield reply;
225-
}
226-
}
227-
);
228-
```
229-
230-
And that's it! You have successfully created a gRPC microservice with Serinus that supports both unary and streaming methods.

packages/serinus/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 2.1.6
4+
5+
**Released on:** 12-03-2026
6+
7+
### Fixes
8+
9+
- Reduce overhead of the request handling by using weak eTags and by using the request relative uri instead of the absolute uri.
10+
311
## 2.1.5
412

513
**Released on:** 07-03-2026

packages/serinus/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: Serinus is a framework written in Dart
44
documentation: https://serinus.app
55
homepage: https://serinus.app
66
repository: https://github.com/francescovallone/serinus
7-
version: 2.1.5
7+
version: 2.1.6
88
funding:
99
- https://github.com/sponsors/francescovallone
1010
topics:

packages/serinus_microservices/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 1.0.0
4+
5+
- Stablilized API and released the first stable version of the microservices package.
6+
37
## 0.1.1
48

59
- chore: update dependencies.

packages/serinus_microservices/bin/serinus_microservices.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ class TestModule extends Module {
1616
}
1717

1818
class Test2Module extends Module {
19-
Test2Module() : super(imports: [TestModule()], controllers: [Test2Controller()]);
19+
Test2Module()
20+
: super(imports: [TestModule()], controllers: [Test2Controller()]);
2021
}
2122

2223
class Test2Controller extends Controller {
@@ -43,7 +44,11 @@ class AppController extends Controller with RpcController {
4344
}
4445

4546
class AppModule extends Module {
46-
AppModule() : super(imports: [Test2Module(), TestModule()], controllers: [AppController()]);
47+
AppModule()
48+
: super(
49+
imports: [Test2Module(), TestModule()],
50+
controllers: [AppController()],
51+
);
4752
}
4853

4954
void main(List<String> arguments) async {

packages/serinus_microservices/lib/transporters/grpc/grpc_client.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ class GrpcClient extends TransportClient<GrpcClientOptions> {
7575
}
7676

7777
@override
78-
Future<ResponsePacket?> send({required String pattern, required String id, Uint8List? payload}) {
78+
Future<ResponsePacket?> send({
79+
required String pattern,
80+
required String id,
81+
Uint8List? payload,
82+
}) {
7983
throw UnimplementedError('send is not implemented for GrpcClient.');
8084
}
8185
}
Lines changed: 2 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,15 @@
11
import 'package:grpc/grpc.dart';
2-
import 'package:protobuf/protobuf.dart';
32
import 'package:serinus/serinus.dart';
43

54
/// The [GrpcServiceController] allows defining gRPC services as Serinus Controllers.
6-
///
5+
///
76
/// It extends the base [Controller] class and includes a reference to the gRPC [Service] it represents. This controller can be used to group related gRPC methods together, making it easier to manage and organize your gRPC services within the Serinus framework.
87
class GrpcServiceController extends Controller {
9-
108
/// The [service] property contains the gRPC service definition that this controller represents.
119
final Service service;
1210

1311
/// Creates a gRPC service controller.
1412
GrpcServiceController({
1513
required this.service,
16-
}): super(service.$name);
17-
}
18-
19-
/// The [GrpcRoute] class is the gRPC
20-
class GrpcRoute extends Route {
21-
/// The [serviceName] property contains the name of the gRPC service.
22-
final Type serviceName;
23-
24-
/// The [methodName] property contains the name of the gRPC method.
25-
final String methodName;
26-
27-
/// Creates a gRPC route.
28-
GrpcRoute(this.serviceName, this.methodName) : super(path: '$serviceName.$methodName', method: HttpMethod.get);
29-
}
30-
31-
/// The [GrpcHandler] typedef is the gRPC handler function.
32-
class GrpcUnaryHandler<Req extends GeneratedMessage, Res extends GeneratedMessage> {
33-
/// The handler function.
34-
final Future<Res> Function(ServiceCall call, Req request, RpcContext context) _handler;
35-
36-
/// Creates a gRPC unary handler.
37-
GrpcUnaryHandler(this._handler);
38-
39-
/// Calls the handler function.
40-
Future<Res> call(ServiceCall call, Req request, RpcContext context) {
41-
return _handler(call, request, context);
42-
}
43-
}
44-
45-
/// The [GrpcStreamHandler] typedef is the gRPC stream handler function.
46-
class GrpcStreamHandler<Req extends GeneratedMessage, Res extends GeneratedMessage> {
47-
/// The handler function.
48-
final Stream<Res> Function(ServiceCall call, Stream<Req> requests, RpcContext context) _handler;
49-
50-
/// Creates a gRPC stream handler.
51-
GrpcStreamHandler(this._handler);
52-
53-
/// Calls the handler function.
54-
Stream<Res> call(ServiceCall call, Stream<Req> requests, RpcContext context) {
55-
return _handler(call, requests, context);
56-
}
57-
}
58-
59-
/// The [GrpcRouteSpec] class is used to define a gRPC route handler specification.
60-
abstract class GrpcRouteSpec<T, Req, Res> extends RouteHandlerSpec<T> {
61-
@override
62-
T get handler => super.handler;
63-
64-
/// The [GrpcRouteSpec] constructor is used to create a new instance of the [GrpcRouteSpec] class.
65-
GrpcRouteSpec(super.route, super.handler);
66-
}
67-
68-
/// The [GrpcRouteHandlerSpec] class is the gRPC route handler specification.
69-
class GrpcRouteHandlerSpec<Req extends GeneratedMessage, Res extends GeneratedMessage>
70-
extends GrpcRouteSpec<GrpcUnaryHandler, Req, Res> {
71-
/// Creates a gRPC route handler specification.
72-
GrpcRouteHandlerSpec(
73-
GrpcRoute route,
74-
Future<Res> Function(ServiceCall call, Req request, RpcContext context) handler,
75-
) : super(route, GrpcUnaryHandler<Req, Res>(handler));
76-
}
77-
78-
/// The [GrpcStreamRouteHandlerSpec] class is the gRPC streaming route handler specification.
79-
class GrpcStreamRouteHandlerSpec<Req extends GeneratedMessage, Res extends GeneratedMessage>
80-
extends GrpcRouteSpec<GrpcStreamHandler, Req, Res> {
81-
/// Creates a gRPC stream route handler specification.
82-
GrpcStreamRouteHandlerSpec(
83-
GrpcRoute route,
84-
Stream<Res> Function(ServiceCall call, Stream<Req> requests, RpcContext context) handler,
85-
) : super(route, GrpcStreamHandler<Req, Res>(handler));
86-
}
87-
88-
/// The [GrpcController] mixin is used to add gRPC routes to a controller.
89-
mixin GrpcController on Controller {
90-
/// The [grpcRoutes] property contains the gRPC routes of the controller.
91-
final Map<String, GrpcRouteSpec> grpcRoutes = {};
92-
93-
/// Registers a gRPC route with the controller.
94-
void grpc<Req extends GeneratedMessage, Res extends GeneratedMessage>(
95-
GrpcRoute route,
96-
Future<Res> Function(ServiceCall call, Req request, RpcContext context) handler,
97-
) {
98-
if (grpcRoutes.containsKey(route.path)) {
99-
throw StateError(
100-
'A gRPC method with name "${route.path}" is already registered in the controller.',
101-
);
102-
}
103-
grpcRoutes[route.path] = GrpcRouteHandlerSpec<Req, Res>(
104-
route,
105-
handler,
106-
);
107-
}
108-
109-
/// Registers a gRPC streaming route with the controller.
110-
void grpcStream<Req extends GeneratedMessage, Res extends GeneratedMessage>(
111-
GrpcRoute route,
112-
Stream<Res> Function(ServiceCall call, Stream<Req> requests, RpcContext context) handler,
113-
) {
114-
if (grpcRoutes.containsKey(route.path)) {
115-
throw StateError(
116-
'A gRPC streaming method with name "${route.path}" is already registered in the controller.',
117-
);
118-
}
119-
grpcRoutes[route.path] = GrpcStreamRouteHandlerSpec<Req, Res>(
120-
route,
121-
handler,
122-
);
123-
}
14+
}) : super(service.$name);
12415
}

0 commit comments

Comments
 (0)