What is create_dart_frog?
maincreate_dart_frog is a Mason brick that serves as a starter app template for Dart Frog. It allows developers to quickly scaffold a new Dart Frog project with a predefined structure.repository·main·Indexed 25 days ago
https://github.com/dart-frog-dev/dart_frogA fast, minimalistic backend framework for Dart built on shelf and mason. It provides a unified tech stack for Flutter/Dart developers to build backends that aggregate, compose, and normalize data. The ecosystem includes the dart_frog_cli for project management, dart_frog_auth for header-based authentication, dart_frog_test for mocking RequestContext, and a dedicated VSCode extension for scaffolding routes and middleware.
create_dart_frog is a Mason brick that serves as a starter app template for Dart Frog. It allows developers to quickly scaffold a new Dart Frog project with a predefined structure.dart_frog_new brick is a Mason brick designed to automate the creation of Dart Frog routes and middleware. It allows you to quickly scaffold the necessary file structures and boilerplate code required for these components in a Dart Frog project.Middleware in Dart Frog allows you to execute code before and after a request is processed. It can be used to modify inbound requests, modify outbound responses, or provide dependencies to your routes.
In Dart Frog, middleware is defined by a middleware function exported from a _middleware.dart file located within a subdirectory of the routes folder.
There are two levels of middleware application:
_middleware.dart file located directly in the routes/ directory is executed for all inbound requests._middleware.dart file located within a specific subdirectory (e.g., routes/hello/_middleware.dart) is executed only for requests matching that route and its children.import 'package:dart_frog/dart_frog.dart';
Handler middleware(Handler handler) {
return (context) async {
// Execute code before request is handled.
// Forward the request to the respective handler.
final response = await handler(context);
// Execute code after request is handled.
// Return a response.
return response;
};
}Dart Frog does not include built-in authentication features, helpers, or resources out of the box. This design provides developers with full freedom to implement any authentication protocol or service that fits their specific business logic.
For a head start, you can use the dart_frog_auth package, which provides foundations for common authentication methods like Basic, Bearer, and Cookie-based authentication.
The dart_frog_auth package handles Authentication (verifying who a user is). It does not handle Authorization (verifying what a user can do).
401 Unauthorized if the authenticator returns null.403 Forbidden.Example of manual authorization check:
Future<Response> _deleteUser(RequestContext context, String id) async {
// If there is no authenticated user, `dart_frog_auth` automatically
// responds with a 401.
final user = context.read<User>();
if (user.id != id) {
// If the current authenticated user is not the owner of the resource,
// return a forbidden response.
return Response(statusCode: HttpStatus.forbidden);
}
await context.read<UserRepository>().deleteUser(user.id);
return Response(statusCode: HttpStatus.noContent);
}In Dart Frog, you can inject dependencies into a RequestContext using provider middleware. A provider takes a create callback that is called lazily to produce an instance of type T. You can access these injected values within route handlers or other middleware using context.read<T>().
To inject a value, use handler.use(provider<T>((context) => ...)) in your middleware. To retrieve it, use context.read<T>() in your onRequest handler.
import 'package:dart_frog/dart_frog.dart';
// Injecting a String
Handler middleware(Handler handler) {
return handler.use(provider<String>((context) => 'Welcome to Dart Frog!'));
}
// Accessing the String
Response onRequest(RequestContext context) {
final greeting = context.read<String>();
return Response(body: greeting);
}Wildcard routes match any number of path segments. Use the [...name] syntax in the filename.
routes/posts/[...page].dart will match /posts/today, /posts/features/starred, etc.String argument to onRequest.Caution: Wildcard routes must be unique leaf routes on their route node. This means they must be a file and must be the only route in that specific folder.
import 'package:dart_frog/dart_frog.dart';
// For routes/posts/[...page].dart
Response onRequest(RequestContext context, String page) {
return Response(body: 'post page: $page');
}v1.2.9, the CLI fully supports pub workspaces. This allows you to organize large repositories into a workspace with a single shared resolution for all packages, which reduces memory usage during analysis and improves performance. This feature requires Dart 3.6.0 or later.The Dart Frog daemon uses specific naming conventions for events to distinguish between operational logs and long-running task progress.
Logs are identified by the logger prefix followed by a severity level. The domain of the log is determined by the operation that generated it (e.g., dev_server.loggerInfo).
| Severity | Event Identifier |
|---|---|
| debug | loggerDetail |
| info | loggerInfo, loggerSuccess, loggerWrite |
| warn | loggerWarning |
| error | loggerError |
| critical | loggerAlert |
Progress events signal the lifecycle of long-running operations (like generating server code). They are identified by the progress prefix and include the following identifiers:
progressStart: Signals the start of an operation. Includes a progressId for tracking.progressUpdate: Signals an update in progress.progressCancel: Signals that progress was cancelled.progressFail: Signals that progress failed.progressComplete: Signals that progress completed successfully.In Dart Frog, you can use middleware to inject dependencies into the RequestContext. This allows all subsequent route handlers to access the dependency using context.read<T>().
To provide a dependency, use the provider<T> middleware within your routes/_middleware.dart file. It is common practice to instantiate the dependency once at the top level of the middleware file to ensure a single instance is shared across the application lifetime.
import 'package:dart_frog/dart_frog.dart';
import 'package:in_memory_todos_data_source/in_memory_todos_data_source.dart';
final _dataSource = InMemoryTodosDataSource();
Handler middleware(Handler handler) {
return handler
.use(requestLogger())
.use(provider<TodosDataSource>((_) => _dataSource));
}You can combine package:broadcast_bloc with WebSockets to broadcast state changes to connected clients.
BroadcastCubit to manage state.provider and inject it into the request context using middleware.context.read<YourCubit>() to access the instance.cubit.subscribe(channel) to link the WebSocket channel to the Cubit's state stream.cubit.unsubscribe(channel) in the onDone callback of the stream listener to clean up when the client disconnects.import 'package:dart_frog/dart_frog.dart';
import 'package:dart_frog_web_socket/dart_frog_web_socket.dart';
import 'package:web_socket_counter/counter/counter.dart';
Future<Response> onRequest(RequestContext context) async {
final handler = webSocketHandler(
(channel, protocol) {
// Subscribe the new client to receive notifications whenever the cubit state changes.
final cubit = context.read<CounterCubit>()..subscribe(channel);
// Send the current count to the new client.
channel.sink.add('${cubit.state}');
// Listen for messages from the client.
channel.stream.listen(
(event) {
switch (event) {
case '__increment__':
cubit.increment();
break;
case '__decrement__':
cubit.decrement();
break;
default:
break;
}
},
// Unsubscribe the channel when the client disconnects.
onDone: () => cubit.unsubscribe(channel),
);
},
);
return handler(context);
}