Skip to content

Commit 866271c

Browse files
committed
docs: update README and ROADMAP
1 parent 731ac72 commit 866271c

File tree

2 files changed

+160
-12
lines changed

2 files changed

+160
-12
lines changed

README.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,153 @@ Create a production build which includes a `DockerFile` so that you can deploy a
4747
dart_frog build
4848
```
4949

50+
## Routes 🚏
51+
52+
In Dart Frog, a route consists of an `onRequest` function (called a route handler) exported from a `.dart` file in the `routes` directory. Each endpoint is associated with a routes file based on its file name. Files named, `index.dart` will correspond to a `/` endpoint.
53+
54+
For example, if you create `routes/hello.dart` that exports an `onRequest` method like below, it will be accessible at `/hello`.
55+
56+
```dart
57+
import 'package:dart_frog/dart_frog.dart';
58+
59+
Response onRequest(RequestContext context) {
60+
return Response(body: 'Hello World');
61+
}
62+
```
63+
64+
All route handlers have access to a `RequestContext` which can be used to access the incoming request as well as dependencies provided to the request context (see middleware).
65+
66+
```dart
67+
import 'package:dart_frog/dart_frog.dart';
68+
69+
Response onRequest(RequestContext context) {
70+
// Access the incoming request.
71+
final request = context.request;
72+
73+
// Return a response.
74+
return Response(body: 'Hello World');
75+
}
76+
```
77+
78+
Route handlers can be synchronous or asynchronous. To convert the above route handlers to async, we just need to update the return type from `Response` to `Future<Response>`. We can add the `async` keyword in order to `await` futures within our handler before returning a `Response`.
79+
80+
```dart
81+
import 'package:dart_frog/dart_frog.dart';
82+
83+
Future<Response> onRequest(RequestContext context) async {
84+
final result = await _someFuture();
85+
return Response(body: 'Result is: $result!');
86+
}
87+
```
88+
89+
### Dynamic Routes 🌓
90+
91+
Dart Frog supports dynamic routes. For example, if you create a file called `routes/posts/<id>.dart`, then it will be accessible at `/posts/1`, `/posts/2`, etc.
92+
93+
```dart
94+
import 'package:dart_frog/dart_frog.dart';
95+
96+
Response onRequest(RequestContext context, String id) {
97+
return Response(body: 'post id: $id');
98+
}
99+
```
100+
101+
## Middleware 🍔
102+
103+
Middleware in Dart Frog allows you to execute code before and after a request is processed. You can modify the inbound request and outbound responses by adding headers, provide dependencies, and more!
104+
105+
In Dart Frog, a piece of middleware consists of a `middleware` function exported from a `_middleware.dart` file within a subdirectory of the `routes` folder. There can only ever be once piece of middleware per route directory with `routes/_middleware.dart` being middleware that is executed for all inbound requests.
106+
107+
```dart
108+
import 'package:dart_frog/dart_frog.dart';
109+
110+
Handler middleware(Handler handler) {
111+
return (context) async {
112+
// Execute code before request is handled.
113+
114+
// Forward the request to the respective handler.
115+
final response = await handler(context);
116+
117+
// Execute code after request is handled.
118+
119+
// Return a response.
120+
return response;
121+
};
122+
}
123+
```
124+
125+
We can chain built-in middleware, such as the `requestLogger` middleware via the `use` API. For example, if we create `routes/_middleware.dart` with the following contents, we will automatically log all requests to our server.
126+
127+
```dart
128+
import 'package:dart_frog/dart_frog.dart';
129+
130+
Handler middleware(Handler handler) {
131+
return handler.use(requestLogger());
132+
}
133+
```
134+
135+
### Dependency Injection 💉
136+
137+
Middleware can also be used to provide dependencies to a `RequestContext` via a `provider`.
138+
139+
`provider` is a type of middleware that can return an object of type `T` and has access to a `RequestContext`. The `create` callback is called lazily and the injected `RequestContext` can be used to perform additional lookups to access values provided upstream.
140+
141+
In the following example, we'll use a `provider` to inject a `String` into our request context.
142+
143+
```dart
144+
import 'package:dart_frog/dart_frog.dart';
145+
146+
Handler middleware(Handler handler) {
147+
return handler
148+
.use(requestLogger())
149+
.use(provider<String>((context) => 'Welcome to Dart Frog!'));
150+
}
151+
```
152+
153+
We can later access the provided via from within a route handler using `context.read<T>()`:
154+
155+
```dart
156+
import 'package:dart_frog/dart_frog.dart';
157+
158+
Response onRequest(RequestContext context) {
159+
final greeting = context.read<String>();
160+
return Response(body: greeting);
161+
}
162+
```
163+
164+
## Testing 🧪
165+
166+
In Dart Frog, we can unit test our route handlers and middleware effectively because they are just pure functions.
167+
168+
For example, we can test our route handler above using `package:test`:
169+
170+
```dart
171+
import 'dart:io';
172+
173+
import 'package:dart_frog/dart_frog.dart';
174+
import 'package:mocktail/mocktail.dart';
175+
import 'package:test/test.dart';
176+
177+
import '../../routes/index.dart' as route;
178+
179+
class _MockRequestContext extends Mock implements RequestContext {}
180+
181+
void main() {
182+
group('GET /', () {
183+
test('responds with a 200 and greeting.', () async {
184+
const greeting = 'Hello World!';
185+
final context = _MockRequestContext();
186+
when(() => context.read<String>()).thenReturn(greeting);
187+
final response = route.onRequest(context);
188+
expect(response.statusCode, equals(HttpStatus.ok));
189+
expect(response.body(), completion(equals(greeting)));
190+
});
191+
});
192+
}
193+
```
194+
195+
In the above test, we're using `package:mocktail` to create a mock `RequestContext` and stub the return value when calling `context.read<String>()`. Then, all we need to do is call `onRequest` with the mocked context and we can assert that the response is what we expect. In this case, we're checking the statusCode and response body to ensure that the response is a 200 with the provided greeting.
196+
50197
## Feature Set ✨
51198

52199
✅ Hot Reload ⚡️
@@ -84,3 +231,7 @@ dart_frog build
84231
[logo_white]: ./assets/dart_frog_logo_white.png#gh-dark-mode-only
85232
[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg
86233
[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis
234+
235+
```
236+
237+
```

ROADMAP.md

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,15 @@ In the interest of transparency, we want to share high-level details of our road
1818

1919
#### Documentation 🗒️
2020

21-
- [ ] Comprehensive Documentation for getting started with DartFrog
22-
- [ ] Creating a project
23-
- [ ] Project structure
24-
- [ ] Development workflow
25-
- [ ] Debugging
26-
- [ ] Hot Reload
27-
- [ ] Creating routes
28-
- [ ] Creating middleware
29-
- [ ] Providing a dependency
30-
- [ ] Testing
31-
- [ ] Production Builds
32-
- [ ] Deployments
21+
- [X] Comprehensive Documentation for getting started with DartFrog
22+
- [X] Creating a project
23+
- [X] Project structure
24+
- [X] Development workflow
25+
- [X] Creating routes
26+
- [X] Creating middleware
27+
- [X] Providing a dependency
28+
- [X] Testing
29+
- [X] Production Builds
3330

3431
### Features ✨
3532

0 commit comments

Comments
 (0)