Skip to content

Commit e94301d

Browse files
authored
docs: serving static files (#112)
1 parent d252db2 commit e94301d

File tree

6 files changed

+45
-269
lines changed

6 files changed

+45
-269
lines changed

β€ŽREADME.md

Lines changed: 6 additions & 218 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ Developed with πŸ’™ by [Very Good Ventures][very_good_ventures_link] πŸ¦„
1616

1717
Dart Frog is an experimental project under development and should not be used in production at this time.
1818

19+
## Documentation πŸ“
20+
21+
For official documentation, please visit https://verygoodopensource.github.io/dart_frog.
22+
1923
## Quick Start πŸš€
2024

2125
### Prerequisites πŸ“
@@ -88,240 +92,24 @@ Dart Frog provides a simple core with a small API surface area in order to reduc
8892

8993
βœ… Docker 🐳
9094

95+
βœ… Static File Support πŸ“
96+
9197
🚧 Generated Dart Client Package πŸ“¦
9298

9399
🚧 Generated API Documentation πŸ“”
94100

95-
## Documentation πŸ“
96-
97-
### Routes 🚏
98-
99-
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.
100-
101-
For example, if you create `routes/hello.dart` that exports an `onRequest` method like below, it will be accessible at `/hello`.
102-
103-
```dart
104-
import 'package:dart_frog/dart_frog.dart';
105-
106-
Response onRequest(RequestContext context) {
107-
return Response(body: 'Hello World');
108-
}
109-
```
110-
111-
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).
112-
113-
```dart
114-
import 'package:dart_frog/dart_frog.dart';
115-
116-
Response onRequest(RequestContext context) {
117-
// Access the incoming request.
118-
final request = context.request;
119-
120-
// Return a response.
121-
return Response(body: 'Hello World');
122-
}
123-
```
124-
125-
We can customize the status code of the response via the `statusCode` parameter on the `Response` object:
126-
127-
```dart
128-
import 'package:dart_frog/dart_frog.dart';
129-
130-
Response onRequest(RequestContext context) {
131-
return Response(statusCode: 204);
132-
}
133-
```
134-
135-
In addition, we can return JSON via the `Response.json` constructor:
136-
137-
```dart
138-
import 'package:dart_frog/dart_frog.dart';
139-
140-
Response onRequest(RequestContext context) {
141-
return Response.json(
142-
body: <String, dynamic>{'hello': 'world!'},
143-
);
144-
}
145-
```
146-
147-
We can also return any Dart object in the `body` of the `Response.json` constructor and it will be serialized correctly as long as it has a `toJson` method that returns a `Map<String, dynamic>`.
148-
149-
πŸ’‘ _Tip: Check out [json_serializable](https://pub.dev/packages/json_serializable) to automate the `toJson` generation_.
150-
151-
```dart
152-
import 'package:json_annotation/json_annotation.dart';
153-
154-
part 'user.g.dart';
155-
156-
@JsonSerializable()
157-
class User {
158-
const User({required this.name, required this.age});
159-
160-
final String name;
161-
final int age;
162-
163-
Map<String, dynamic> toJson() => _$UserToJson(this);
164-
}
165-
```
166-
167-
```dart
168-
import 'package:dart_frog/dart_frog.dart';
169-
170-
Response onRequest(RequestContext context) {
171-
return Response.json(
172-
body: User(name: 'Dash', age: 42),
173-
);
174-
}
175-
```
176-
177-
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`.
178-
179-
```dart
180-
import 'package:dart_frog/dart_frog.dart';
181-
182-
Future<Response> onRequest(RequestContext context) async {
183-
final result = await _someFuture();
184-
return Response(body: 'Result is: $result!');
185-
}
186-
```
187-
188-
#### Dynamic Routes πŸŒ“
189-
190-
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.
191-
192-
Routing parameters are forwarded to the `onRequest` method as seen below.
193-
194-
```dart
195-
import 'package:dart_frog/dart_frog.dart';
196-
197-
Response onRequest(RequestContext context, String id) {
198-
return Response(body: 'post id: $id');
199-
}
200-
```
201-
202-
### Middleware πŸ”
203-
204-
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, provide dependencies, and more!
205-
206-
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.
207-
208-
```dart
209-
import 'package:dart_frog/dart_frog.dart';
210-
211-
Handler middleware(Handler handler) {
212-
return (context) async {
213-
// Execute code before request is handled.
214-
215-
// Forward the request to the respective handler.
216-
final response = await handler(context);
217-
218-
// Execute code after request is handled.
219-
220-
// Return a response.
221-
return response;
222-
};
223-
}
224-
```
225-
226-
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.
227-
228-
```dart
229-
import 'package:dart_frog/dart_frog.dart';
230-
231-
Handler middleware(Handler handler) {
232-
return handler.use(requestLogger());
233-
}
234-
```
235-
236-
#### Dependency Injection πŸ’‰
237-
238-
Middleware can also be used to provide dependencies to a `RequestContext` via a `provider`.
239-
240-
`provider` is a type of middleware that can create and provide an instance of type `T` to the request context. The `create` callback is called lazily and the injected `RequestContext` can be used to perform additional lookups to access values provided upstream.
241-
242-
In the following example, we'll use a `provider` to inject a `String` into our request context.
243-
244-
```dart
245-
import 'package:dart_frog/dart_frog.dart';
246-
247-
Handler middleware(Handler handler) {
248-
return handler
249-
.use(requestLogger())
250-
.use(provider<String>((context) => 'Welcome to Dart Frog!'));
251-
}
252-
```
253-
254-
We can later access the provided via from within a route handler using `context.read<T>()`:
255-
256-
```dart
257-
import 'package:dart_frog/dart_frog.dart';
258-
259-
Response onRequest(RequestContext context) {
260-
final greeting = context.read<String>();
261-
return Response(body: greeting);
262-
}
263-
```
264-
265-
### Testing πŸ§ͺ
266-
267-
In Dart Frog, we can unit test our route handlers and middleware effectively because they are plain functions.
268-
269-
For example, we can test our route handler above using `package:test`:
270-
271-
```dart
272-
import 'dart:io';
273-
274-
import 'package:dart_frog/dart_frog.dart';
275-
import 'package:mocktail/mocktail.dart';
276-
import 'package:test/test.dart';
277-
278-
import '../../routes/index.dart' as route;
279-
280-
class _MockRequestContext extends Mock implements RequestContext {}
281-
282-
void main() {
283-
group('GET /', () {
284-
test('responds with a 200 and greeting.', () async {
285-
const greeting = 'Hello World!';
286-
final context = _MockRequestContext();
287-
when(() => context.read<String>()).thenReturn(greeting);
288-
final response = route.onRequest(context);
289-
expect(response.statusCode, equals(HttpStatus.ok));
290-
expect(response.body(), completion(equals(greeting)));
291-
});
292-
});
293-
}
294-
```
295-
296-
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.
297-
298-
### Additional Resources πŸ“š
299-
300-
- [Example][example_link]
301-
- [Roadmap][roadmap_link]
302-
- [Blog post][blog_link]
303-
- [Livestream demo][livestream_link]
304-
305-
_πŸ’‘ Fun Fact: the [dart2js][dart2js_compiler_link] compiler [used to be called frog][dart2js_frog_pr_link]._
306-
307-
[dart2js_compiler_link]: https://dart.dev/tools/dart2js
308-
[dart2js_frog_pr_link]: https://github.com/dart-lang/sdk/issues/2194
309101
[dart_installation_link]: https://dart.dev/get-dart
310-
[blog_link]: https://verygood.ventures/blog/dart-frog
311102
[ci_badge]: https://github.com/VeryGoodOpenSource/dart_frog/actions/workflows/dart_frog.yaml/badge.svg
312103
[ci_link]: https://github.com/VeryGoodOpenSource/dart_frog/actions/workflows/dart_frog.yaml
313104
[coverage_badge]: https://raw.githubusercontent.com/VeryGoodOpenSource/dart_frog/main/packages/dart_frog/coverage_badge.svg
314105
[dart_frog_link_dark]: https://github.com/verygoodopensource/dart_frog#gh-dark-mode-only
315106
[dart_frog_link_light]: https://github.com/verygoodopensource/dart_frog#gh-light-mode-only
316-
[example_link]: https://github.com/VeryGoodOpenSource/dart_frog/tree/main/example
317107
[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg
318108
[license_link]: https://opensource.org/licenses/MIT
319-
[livestream_link]: https://youtu.be/N7l0b09c6DA
320109
[logo_black]: https://raw.githubusercontent.com/VeryGoodOpenSource/dart_frog/main/assets/dart_frog_logo_black.png#gh-light-mode-only
321110
[logo_white]: https://raw.githubusercontent.com/VeryGoodOpenSource/dart_frog/main/assets/dart_frog_logo_white.png#gh-dark-mode-only
322111
[pub_badge]: https://img.shields.io/pub/v/dart_frog.svg
323112
[pub_link]: https://pub.dartlang.org/packages/dart_frog
324-
[roadmap_link]: https://github.com/VeryGoodOpenSource/dart_frog/blob/main/ROADMAP.md
325113
[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg
326114
[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis
327115
[very_good_ventures_link]: https://verygood.ventures

β€ŽROADMAP.md

Lines changed: 0 additions & 48 deletions
This file was deleted.

β€Ždocs/docs/basics/congratulations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
sidebar_position: 6
2+
sidebar_position: 7
33
---
44

55
# Congratulations! πŸŽ‰
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
sidebar_position: 6
3+
---
4+
5+
# Serving Static Files πŸ“
6+
7+
Dart Frog supports serving static files including images, text, json, html, and more. To serve static files, place the files within the `public` directory at the root of the project.
8+
9+
For example, if you create a file in `public/hello.txt` which contains the following:
10+
11+
```
12+
Hello World!
13+
```
14+
15+
The contents of the file will be available at [http://localhost:8080/hello.txt](http://localhost:8080/hello.txt).
16+
17+
The `public` directory can contain static files within subdirectories. For example, if you created an image in `public/images/unicorn.png`, the contents of the file will be available at [http://localhost:8080/images/unicorn.png](http://localhost:8080/images/unicorn.png)
18+
19+
When running a development server, static files can be added, removed, and modified without needing to restart the server thanks to hot reload ⚑️.
20+
21+
:::note
22+
Static file support requires `dart_frog ^0.0.2-dev.7` and `dart_frog_cli ^0.0.1-dev.8`
23+
:::
24+
25+
:::note
26+
The `/public` folder must be at the root of the project and cannot be renamed. This is the only directory used to serve static files.
27+
:::
28+
29+
:::note
30+
In production, only files that are in the `/public` directory at build time will be served.
31+
:::
32+
33+
:::caution
34+
Be sure not to have a static file with the same name as a file in the `/routes` directory as this will result in a conflict.
35+
:::

β€Ždocs/docs/overview.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ dart_frog build
8080

8181
βœ… Docker 🐳
8282

83+
βœ… Static File Support πŸ“
84+
8385
🚧 Generated Dart Client Package πŸ“¦
8486

8587
🚧 Generated API Documentation πŸ“”

β€Ždocs/docs/roadmap.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ In the interest of transparency, we want to share high-level details of our road
3333
### Features ✨
3434

3535
- [x] Configurable Port
36-
- [ ] Improve HTTP method specification per handler
37-
- [ ] Static Asset support
36+
- [x] Static Asset support
3837
- [ ] Dart API Client Generation
3938
- [ ] Open API Documentation Generation
4039
- [ ] DartFrog Testing Library (utilities for unit and e2e testing)

0 commit comments

Comments
Β (0)