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

feat(#18): add hooks to Serinus, remove guards and pipes #25

Merged
merged 13 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .website/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ export default defineConfig({
base: '/overview/',
collapsed: false,
items: [
{ text: 'Getting Started', link: 'getting_started' },
{ text: 'Modules', link: 'modules' },
{ text: 'Controllers', link: 'controllers' },
{ text: 'Routes', link: 'routes' },
{ text: 'Providers', link: 'providers' },
{ text: 'Middlewares', link: 'middlewares' },
{ text: 'Guards', link: 'guards' },
{ text: 'Pipes', link: 'pipes' },
{ text: 'WebSockets', link: 'websockets' },
{ text: 'Getting Started', link: '/overview/getting_started' },
{ text: 'Modules', link: '/overview/modules' },
{ text: 'Controllers', link: '/overview/controllers' },
{ text: 'Routes', link: '/overview/routes' },
{ text: 'Providers', link: '/overview/providers' },
{ text: 'Middlewares', link: '/overview/middlewares' },
{ text: 'Guards', link: '/overview/guards' },
{ text: 'WebSockets', link: '/overview/websockets' },
{ text: 'Hooks', link: '/overview/hooks' },
]
},
{
Expand Down
62 changes: 4 additions & 58 deletions .website/overview/controllers.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Controllers

Controllers in Serinus are groups of routes that shares the same base path, pipes and guards.
Controllers in Serinus are groups of routes that shares the same base base path and guards.

## Creating a Controller

Expand Down Expand Up @@ -48,62 +48,6 @@ class GetRoute extends Route {

:::

## Adding Pipes

To add pipes to a controller, you can override the `pipes` getter and add to the list the pipes that you need.

::: info
Pipes defined in a controller will be executed before the pipes defined in the routes.
:::

::: code-group

```dart [my_controller.dart]
import 'package:serinus/serinus.dart';
import 'my_routes.dart';
import 'my_pipes.dart';

class MyController extends Controller {

@override
List<Pipe> get pipes => [MyPipe()];

MyController({super.path = '/'}) {
on(GetRoute(path: '/'), (context) {
return Response.text(
data: 'Hello World!',
);
});
}
}
```

```dart [my_routes.dart]
import 'package:serinus/serinus.dart';

class GetRoute extends Route {

const GetRoute({
required super.path,
super.method = HttpMethod.get,
});

}
```

```dart [my_pipes.dart]
import 'package:serinus/serinus.dart';

class MyPipe extends Pipe {
@override
Future<void> transform(ExecutionContext context){
print('Pipe executed');
}
}
```

:::

## Adding Guards

To add guards to a controller, you can override the `guards` getter and add to the list the guards that you need.
Expand Down Expand Up @@ -156,4 +100,6 @@ class MyGuard extends Guard {
return true;
}
}
```
```

:::
66 changes: 66 additions & 0 deletions .website/overview/hooks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Hooks

Hooks are a way to execute custom code at specific points in the Serinus request lifecycle. They can be used to add custom logic to your application, such as tracing, loggin, or error handling.

## Creating a Hook

To create a hook, you need to create a class that extends the `Hook` class. Each hook has multiple methods that you can override to add custom logic.

These are the methods that you can override:

- `onRequest`: This method is called before the router is called.
- `beforeHandle`: This method is called before the route handler is called.
- `afterHandle`: This method is called after the route handler is called.
- `onResponse`: This method is called after the response is generated.

```dart
import 'package:serinus/serinus.dart';

class MyHook extends Hook {
@override
Future<void> onRequest(Request request, InternalResponse response) async {
print('Request received');
}

@override
Future<void> beforeHandle(Request request, InternalResponse response) async {
print('Before handle');
}

@override
Future<void> afterHandle(Request request, InternalResponse response) async {
print('After handle');
}

@override
Future<void> onResponse(Response response) async {
print('Response sent');
}
}
```

In the `MyHook` class, you can override the methods that you want to add custom logic to. You can access the `Request` object and the `InternalResponse` object in the `onRequest`, `beforeHandle`, and `afterHandle` methods, and the `Response` object in the `onResponse` method.

The `onResponse` can also know if the response was successful or not by checking the `response.isError` property.
This property will be `true` if the response status code is greater than or equal to 400.

## Adding a Hook

To add a hook to your application, you can use the `use` method on the `SerinusApplication` object.

```dart
import 'package:serinus/serinus.dart';

void main() {
final app = SerinusApplication();

app.use(MyHook());

await app.serve();

}
```

In the example above, the `MyHook` hook is added to the application using the `use` method. This will execute the hook methods at the specified points in the request lifecycle.

Hooks are executed in the order that they are added to the application. If you need to execute a hook before another hook, you can add it before the other hook.
30 changes: 0 additions & 30 deletions .website/overview/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,33 +122,3 @@ class AppModule extends Module {
List<Guard> get guards => [MyGuard()];
}
```

## Adding pipes

Modules can have pipes, and they will be executed before the pipes defined in the controllers and routes.
Also if a module has submodules, the pipes will be executed before the pipes defined in the submodules.

To add a pipe to a module, you can override the `pipes` getter and add to the list the pipes that you need.

```dart
import 'package:serinus/serinus.dart';

class MyPipe extends Pipe {
@override
Future<void> transform(ExecutionContext context) async {
print('Pipe executed');
}
}

class AppModule extends Module {
AppModule() : super(
imports: [], // Add the modules that you want to import
controllers: [],
providers: [],
middlewares: [],
);

@override
List<Pipe> get pipes => [MyPipe()];
}
```
39 changes: 0 additions & 39 deletions .website/overview/pipes.md

This file was deleted.

78 changes: 41 additions & 37 deletions .website/overview/routes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Routes

Routes in Serinus are the endpoints of your application. They are grouped in controllers and can have pipes and guards.
Routes in Serinus are the endpoints of your application. They are grouped in controllers and can have guards.
They only exposes the endpoint and the method that the route will respond to so you can create reusable routes that can be added to multiple controllers.

## Create a route
Expand Down Expand Up @@ -113,20 +113,19 @@ class GetRoute extends Route {

:::

## Adding Pipes
## Adding Guards

To add pipes to a route, you can override the `pipes` getter and add to the list the pipes that you need.
To add guards to a route, you can override the `guards` getter and add to the list the guards that you need.

::: info
Pipes defined in a route will be executed after the pipes defined in the controller.
Guards defined in a route will be executed after the guards defined in the controller.
:::

::: code-group

```dart [my_routes.dart]
import 'package:serinus/serinus.dart';
import 'my_pipes.dart';

import 'my_guards.dart';

class GetRoute extends Route {

Expand All @@ -136,61 +135,66 @@ class GetRoute extends Route {
});

@override
List<Pipe> get pipes => [MyPipe()];
List<Guard> get guards => [MyGuard()];

}
```

```dart [my_pipes.dart]
```dart [my_guards.dart]
import 'package:serinus/serinus.dart';

class MyPipe extends Pipe {
class MyGuard extends Guard {
@override
Future<void> transform(ExecutionContext context){
print('Pipe executed');
Future<bool> canActivate(ExecutionContext context){
print('Guard executed');
return Future.value(true);
}
}
```

:::

## Adding Guards

To add guards to a route, you can override the `guards` getter and add to the list the guards that you need.
## Transform the RequestContext

::: info
Guards defined in a route will be executed after the guards defined in the controller.
:::
You can transform the `RequestContext` before it reaches the route handler by overriding the `transform` method.

::: code-group

```dart [my_routes.dart]
```dart
import 'package:serinus/serinus.dart';
import 'my_guards.dart';

class GetRoute extends Route {
const GetRoute({
required super.path,
super.method = HttpMethod.get,
});

const GetRoute({
required super.path,
super.method = HttpMethod.get,
});

@override
List<Guard> get guards => [MyGuard()];

@override
Future<RequestContext> transform(RequestContext context) async {
return context;
}
}
```

```dart [my_guards.dart]
## Parsing (and validate) the RequestContext

You can parse the `RequestContext` before it reaches the route handler by overriding the `parse` method.
Serinus follows the [Parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) principle, so if the parsing fails, the request is not valid and should be rejected.

```dart
import 'package:serinus/serinus.dart';

class MyGuard extends Guard {
@override
Future<bool> canActivate(ExecutionContext context){
print('Guard executed');
return Future.value(true);
}
class GetRoute extends Route {
const GetRoute({
required super.path,
super.method = HttpMethod.get,
});

@override
Future<RequestContext> parse(RequestContext context) async {
return context;
}
}
```

:::
::: info
If you need an amazing validation library you can try [Acanthis](https://pub.dev/packages/acanthis). 🐤
:::
4 changes: 2 additions & 2 deletions benchmarks/all_results.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
| Server | Req/sec | Trans/sec | Req/sec DIFF | Avg Latency |
|:-|:-|:-|:-|:-|
| serinus | 24529.39 | 5.50MB | +0.00% | 45.08 |
| dart_http | 32624.36 | 6.16MB | +33.00% | 31.33 |
| serinus | 29499.5 | 72.85 KB/s | +0.00% | 3.389893 |
| dart_http | 37617.8 | 95.35 KB/s | +27.52% | 2.658316 |
Loading
Loading