Angel3 Framework
Angel3 framework is a high-powered HTTP server with support for dependency injection, sophisticated routing, authentication, ORM, graphql etc. It is designed to keep the core minimal but extensible through a series of plugin packages. It won't dictate which features, databases or web templating engine to use. This flexibility enable Angel3 framework to grow with your application as new features can be added to handle the new use cases.
This package is the core package of Angel3. For more information, visit us at Angel3 Website.
Installation and Setup
(Option 1) Create a new project by cloning from boilerplate templates
-
Download and install Dart
-
Clone one of the following starter projects:
-
Run the project in development mode (hot-reloaded is enabled on file changes).
dart --observe bin/dev.dart -
Run the project in production mode (hot-reloaded is disabled).
dart bin/prod.dart -
Run it in a container with Podman: build and run the provided
Containerfile, or use Podman Compose withcontrib/deployment/compose.yaml, as the project'sREADME.mddescribes.
(Option 2) Create a new project with Angel3 CLI
-
Download and install Dart
-
Install the Angel3 CLI:
dart pub global activate angel3_cli -
On terminal, create a new project:
angel3 init hello -
Run the project in development mode (hot-reloaded is enabled on file changes).
dart --observe bin/dev.dart -
Run the project in production mode (hot-reloaded is disabled).
dart bin/prod.dart -
Run it in a container with Podman: build and run the provided
Containerfile, or use Podman Compose withcontrib/deployment/compose.yaml, as the project'sREADME.mddescribes.
(Option 3) Add Angel3 to an existing project
In your pubspec.yaml:
dependencies:
angel3_framework: ^10.0.0
Usage
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
void main() async {
var app = Angel();
// Index route. Returns JSON.
app.get('/', (req, res) => 'Welcome to Angel!');
// Accepts a URL like /greet/foo or /greet/bob.
app.get('/greet/:name', (req, res) => 'Hello, ${req.params['name']}!');
// Simple fallback to throw a 404 on unknown paths.
app.fallback((req, res) => throw AngelHttpException.notFound());
var http = AngelHttp(app);
await http.startServer('127.0.0.1', 3000);
print('Listening at ${http.uri}');
}
See the example directory for services, views, error handling and more.
Running without dart:mirrors (AOT)
dart compile exe and Flutter do not support dart:mirrors. Create the app without a reflector and it runs with reflection disabled:
var app = Angel(); // no MirrorsReflector
app.container.registerSingleton(Greeter()); // register what you inject
app.use('/api/todos', MapService()); // services and hooks work as usual
// Inject with an explicit InjectionRequest instead of reflecting on the closure.
app.get('/greet/:name', ioc(
(Greeter greeter, String name) => greeter.greet(name),
injection: InjectionRequest.constant(required: [Greeter, ['name', String]]),
));
// Controllers take their mount path from `expose` and add routes in `configureRoutes`.
class HealthController extends Controller {
HealthController() : super(expose: const Expose('/health'));
@override
void configureRoutes(Routable routable) =>
routable.get('/', (req, res) => {'status': 'ok'});
}
Without reflection, these features are unavailable: @Expose on controller methods, the @Middleware and @Hooks annotations (they are ignored), automatic constructor injection in container.make, and ioc without injection:. See example/no_mirrors.dart for a complete app.
To keep them in an AOT-compiled app, mark the classes @injectable, generate their reflection data with angel3_container_generator, and pass it to the app: Angel(reflector: StaticReflector.combine([...])). On the Dart VM, MirrorsReflector of angel3_container_mirrors does the same with dart:mirrors.
Request limits
req.parseBody() (also used by services) reads at most app.maxBodySize bytes, 10 MB by default, and answers larger requests with 413 Payload Too Large:
var app = Angel()..maxBodySize = 1024 * 1024; // 1 MB for the whole app
// Allow larger bodies on one route by raising the limit before parsing.
app.post('/upload', uploadHandler, middleware: [
(req, res) {
req.maxBodySize = 100 * 1024 * 1024; // 100 MB
return true;
},
]);
Set app.maxBodySize = null to remove the limit. In production, resolved routes are cached per request path; the cache holds at most app.maxHandlerCacheSize entries (1024 by default, 0 disables it).
Full-stack with Kelicap
Kelicap, a derivative of AngularDart, builds the web client of an Angel3 application. The client calls the server's services with angel3_client, whose browser.dart compiles to JavaScript with dart2js since 10.0.1:
import 'package:angel3_client/browser.dart';
var app = Rest('http://localhost:3000');
// `TodoSerializer` is generated by `angel3_serialize`, so the client and the server share the model class.
var todos = app.service<String, Todo>(
'todos',
deserializer: (x) => TodoSerializer.fromMap(x as Map),
);
List<Todo> list = await todos.index();
To start a new full-stack project, clone the Angel3 Kelicap Template. Its README.md describes how to serve the client in development.
Available Packages
Angel3 is a collection of packages: pick the ones a project needs. Each is described in its own README.
| Package | Description |
|---|---|
| Core | |
| angel3_framework | The HTTP and HTTP/2 server: routing, services, controllers, middleware and dependency injection |
| angel3_route | Isomorphic router with route parameters, groups and middleware, also usable in Flutter and the browser |
| angel3_container | Hierarchical dependency injection container, with pluggable reflection backends |
| angel3_container_mirrors | MirrorsReflector, a dart:mirrors reflection backend for apps run on the Dart VM (new in 10.0.0) |
| angel3_container_generator | Generates reflection data for @injectable classes, so DI works without dart:mirrors (AOT) |
| angel3_configuration | Loads YAML configuration files, per environment, and .env files |
| angel3_http_exception | An HTTP exception that is serialized to JSON for clients |
| Data and persistence | |
| angel3_model | Base data model classes, such as Model with an id and timestamps |
| angel3_serialize | Annotations for model serialization |
| angel3_serialize_generator | Generates model classes, toJson/fromMap serializers and TypeScript definitions |
| angel3_orm | ORM runtime: a database-agnostic query builder and the loading of relations |
| angel3_orm_generator | Generates the ORM's query classes and migrations from models |
| angel3_orm_postgres | PostgreSQL executor for the ORM, with transactions and savepoints |
| angel3_orm_mysql | MySQL and MariaDB executors for the ORM |
| angel3_orm_service | A service over the ORM's query classes |
| angel3_migration | Schema DSL for database migrations |
| angel3_migration_runner | Runs migrations on PostgreSQL, MySQL and MariaDB |
| angel3_mongo | A service backed by MongoDB |
| angel3_rethinkdb | A service backed by RethinkDB, with changefeeds |
| angel3_sembast | A service backed by Sembast, an embedded NoSQL database |
| angel3_redis | A service backed by Redis, also usable as a cache |
| angel3_file_service | A service that persists data to a JSON file, for development |
| angel3_paginate | Platform-agnostic pagination of lists |
| Authentication and security | |
| angel3_auth | Authentication with JWTs, local and Basic strategies |
| angel3_auth_oauth2 | Sign in users with OAuth2 providers, such as Google or GitHub |
| angel3_auth_twitter | Sign in users with Twitter OAuth 1.0a (not published) |
| angel3_oauth2 | Building blocks of an OAuth 2.0 authorization server, with PKCE |
| angel3_security | CSRF protection, signed cookies and rate limiting |
| angel3_cors | Cross-Origin Resource Sharing (CORS) middleware |
| Views and templates | |
| jael3 | Jael, a server-side HTML templating engine |
| jael3_preprocessor | Resolves the template inheritance and includes of Jael templates |
| angel3_jael | Renders Jael templates as Angel3 views |
| jael3_web | Experimental virtual DOM engine built on Jael (not published) |
| jael3_language_server | Language Server Protocol implementation for Jael (not published) |
| angel3_mustache | Renders Mustache templates as views |
| angel3_jinja | Renders Jinja templates as views |
| angel3_markdown | Renders Markdown files as views |
| angel3_html | Sends belatuk_html_builder nodes as HTML responses |
| angel3_seo | Inlines CSS and JavaScript into HTML pages, for faster first loads |
| Real time, clients and tools | |
| angel3_websocket | WebSockets for services and controllers, with browser and dart:io clients |
| angel3_sync | Synchronizes WebSocket events across instances with belatuk_pub_sub |
| angel3_client | Client for Angel3 services, for the browser, Flutter and the command line |
| angel3_static | Serves static files, with caching and Range requests |
| angel3_cache | Response caching middleware, and a caching service |
| angel3_proxy | Forwards requests to another server |
| angel3_shelf | Runs shelf handlers in Angel3, and Angel3 in shelf (not published) |
| angel3_user_agent | Parses the user agent of requests |
| angel3_validate | Validates request bodies and queries, on the server and the client |
| angel3_hot | Hot reloading of the server during development |
| angel3_production | Runs the app in several isolates, with respawning and graceful shutdown |
| angel3_test | Testing utilities: a client that sends mock requests, and matchers |
| angel3_mock_request | Mock dart:io requests and responses, for tests without a server |
Migrating from 9.x to 10.0.0
The breaking changes of each release are listed in the CHANGELOG. Those of 10.0.0 that need changes in an app:
MirrorsReflector moved to angel3_container_mirrors
Add angel3_container_mirrors to your dependencies, and change the import:
// 9.x
import 'package:angel3_container/mirrors.dart';
// 10.0.0
import 'package:angel3_container_mirrors/angel3_container_mirrors.dart';
var app = Angel(reflector: MirrorsReflector());
To compile the app with dart compile exe, use generated reflection data instead, or no reflector, as Running without dart:mirrors describes.
chain stops at the first handler that does not return true
Like the handlers of a route, each handler given to chain must return true for the next one to run. A handler that returns nothing, such as a middleware that only sets a value, now stops the chain:
// 9.x: `handler` ran after it
chain([(req, res) { req.params['user'] = 'me'; }, handler]);
// 10.0.0
chain([(req, res) { req.params['user'] = 'me'; return true; }, handler]);
Buffered responses send Content-Length
A buffered response is sent with a Content-Length header instead of chunked transfer encoding. Set res.chunked = true to send it chunked.
Default response headers
AngelHttp.addResponseHeader and removeResponseHeader take a Map of headers:
addResponseHeaderthrows anArgumentErrorfor a header that cannot be sent, before it adds any of the call's headers.removeResponseHeaderremoves each value of a list, every value equal to a given one, and, if none matches exactly, those that match ignoring case, such assameoriginfor theSAMEORIGINthatdart:ioadds. Anullvalue removes the header whatever its values.
var http = AngelHttp(app);
http.removeResponseHeader({'X-FRAME-OPTIONS': 'SAMEORIGIN'});
http.removeResponseHeader({'X-XSS-PROTECTION': null});
http.addResponseHeader({'X-XSRF-TOKEN': 'a591a6d4...'});
HTTP/2 sessions
An HTTP/2 request gets a session, and the session cookie, only when req.session is first read. Code that expects the cookie on every response must read req.session.
Performance Benchmark
The performance benchmark can be found at
TechEmpower Framework Benchmarks Round 21
Donation & Support
If you like this project and interested in supporting its development, you can make a donation using the following services:
- paypal service
Libraries
- angel3_framework
- An easily-extensible web server framework in Dart.
- http
- http2
