Skip to content

Commit

Permalink
feat(dart_frog): add tryRead method to RequestContext
Browse files Browse the repository at this point in the history
  • Loading branch information
mtwichel committed Dec 19, 2024
1 parent 26a05a6 commit a2c8721
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 0 deletions.
11 changes: 11 additions & 0 deletions docs/docs/basics/dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ Response onRequest(RequestContext context) {
}
```

If you want to attempt to `read` a value that has not been provided, it will throw a `StateError`. However, if you want to _attempt_ to `read` a value whether it's provided or not, you can use `context.tryRead<T>()`. If no value matching that time has been provided, it will return `null`.

```dart
import 'package:dart_frog/dart_frog.dart';
Response onRequest(RequestContext context) {
final greeting = context.tryRead<String>();
return Response(body: greeting ?? 'Default Greeting');
}
```

### Extracting Providers

In the above example, we defined the `provider` inline. This is fine for simple cases, but for more complex providers or providers which you want to reuse, it can be helpful to extract the provider to its own file:
Expand Down
15 changes: 15 additions & 0 deletions packages/dart_frog/lib/src/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ This can happen if $T was not provided to the request context:
return (value as T Function())();
}

/// Attempt to lookup an instance of [T] from the [request] context.
///
/// Returns `null` if [T] is not available within the provided
/// [request] context.
T? tryRead<T>() {
try {
return read<T>();
// Explicitly catching [StateError] as it's what it throw
// when [read] fails
// ignore: avoid_catching_errors
} on StateError catch (_) {
return null;
}
}

/// Get URL parameters captured by the [Router.mount].
/// They can be accessed from inside the mounted routes.
Map<String, String> get mountedParams {
Expand Down
86 changes: 86 additions & 0 deletions packages/dart_frog/test/src/provider_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,90 @@ void main() {

await server.close();
});

group('using tryRead()', () {
test('values can be provided and read via middleware', () async {
const value = '__test_value__';
String? nullableValue;
Handler middleware(Handler handler) {
return handler
.use(provider<String>((_) => value))
.use(provider<String?>((_) => nullableValue));
}

Response onRequest(RequestContext context) {
final value = context.tryRead<String>();
final nullableValue = context.tryRead<String?>();
return Response(body: '$value:$nullableValue');
}

final handler =
const Pipeline().addMiddleware(middleware).addHandler(onRequest);

final server = await serve(handler, 'localhost', 3010);
final client = http.Client();
final response = await client.get(Uri.parse('http://localhost:3010/'));

await expectLater(response.statusCode, equals(HttpStatus.ok));
await expectLater(response.body, equals('$value:$nullableValue'));

await server.close();
});

test('descendant providers can access provided values', () async {
const url = 'http://localhost/';
Handler middleware(Handler handler) {
return handler.use(
provider<Uri?>((context) {
final stringValue = context.tryRead<String>();
return stringValue == null ? null : Uri.parse(stringValue);
}),
).use(provider<String>((context) => url));
}

Response onRequest(RequestContext context) {
final value = context.tryRead<Uri?>();
return Response(body: value.toString());
}

final handler =
const Pipeline().addMiddleware(middleware).addHandler(onRequest);

final server = await serve(handler, 'localhost', 3011);
final client = http.Client();
final response = await client.get(Uri.parse('http://localhost:3011/'));

await expectLater(response.statusCode, equals(HttpStatus.ok));
await expectLater(response.body, equals(url));

await server.close();
});

test('null is returned and no StateError is thrown', () async {
Object? exception;
Uri? value;
Response onRequest(RequestContext context) {
try {
value = context.tryRead<Uri>();
} catch (e) {
exception = e;
}
return Response();
}

final handler = const Pipeline()
.addMiddleware((handler) => handler)
.addHandler(onRequest);

final server = await serve(handler, 'localhost', 3012);
final client = http.Client();
final response = await client.get(Uri.parse('http://localhost:3012/'));

await expectLater(response.statusCode, equals(HttpStatus.ok));
expect(exception, isNull);
expect(value, isNull);

await server.close();
});
});
}

0 comments on commit a2c8721

Please sign in to comment.