flint_dart 1.0.0+27
flint_dart: ^1.0.0+27 copied to clipboard
A modern, expressive, and extensible server-side framework by Eulogia Technologies.
Flint Dart #
A modern, production‑ready backend framework for Dart. Flint Dart gives you routing, middleware, ORM, authentication, validation, views, and auto‑generated Swagger docs—built for real apps, not just demos.
- Website: flintdart.eulogia.net
- Status: Internal Build (v1.0.0+27)
- Maintainers: Eulogia Technologies
Why Flint Dart #
- Fast, clean routing for REST APIs
- Middleware-first design for security and control
- Built‑in ORM for MySQL and PostgreSQL
- Auth helpers (JWT + social providers)
- Validation on requests (JSON + form)
- Views with a simple template engine
- Swagger UI auto‑docs from route annotations
- CLI to scaffold projects and files
Installation #
1) Install CLI globally #
# Activate CLI
dart pub global activate flint_dart
# Create a new project
flint create my_app
cd my_app
# Run the app
flint run
2) Add to an existing project #
dart pub add flint_dart
Hello World #
import 'package:flint_dart/flint_dart.dart';
void main() {
final app = Flint();
app.get('/', (Context ctx) async {
return ctx.res?.send('Welcome to Flint Dart!');
});
app.listen(port: 3000);
}
Routing #
app.get('/users/:id', (Context ctx) async {
final id = ctx.req.param('id');
return ctx.res?.json({'id': id});
});
app.post('/users', (Context ctx) async {
final body = await ctx.req.json();
return ctx.res?.json({'created': true, 'data': body});
});
Unified Context handlers #
Flint uses a unified Context object for route handlers.
ctx.reqis always available.ctx.resis available for HTTP routes.ctx.socketis available for WebSocket routes.
app.get('/health', (Context ctx) {
return ctx.res?.json({'ok': true});
});
app.websocket('/chat', (Context ctx) {
ctx.socket?.on('ping', (_) {
ctx.socket?.emit('pong', {'ok': true});
});
});
Request helpers #
ctx.req.param('id')— route parameterctx.req.queryParam('page')— query parameterctx.req.body()— raw body stringctx.req.json()— JSON body (Map)ctx.req.form()— form data (Map)
Response helpers #
ctx.res?.send('text')ctx.res?.json({...})ctx.res?.view('home', data: {...})ctx.res?.respond(data)— auto‑detects typectx.res?.back(fallback: '/settings')— redirect to previous URLctx.res?.withSuccess('Saved')/ctx.res?.withError('Failed')— flash messages for next template render
You can also return data directly from a handler. Flint will serialize:
Map/Listas JSON- primitives as text/auto response
- custom objects that implement
toMap()ortoJson()
app.post('/settings', (Context ctx) async {
final data = await ctx.req.validate({'name': 'required|string|min:2'});
// ... persist data
return ctx.res
?.withSuccess('Settings updated successfully.')
.back(fallback: '/settings');
});
class UserDto {
final int id;
final String email;
UserDto(this.id, this.email);
Map<String, dynamic> toMap() => {'id': id, 'email': email};
}
app.get('/me', (Context ctx) async {
return UserDto(1, 'ada@example.com'); // auto JSON via toMap()
});
Middleware #
Global middleware #
app.use(AuthMiddleware());
Route middleware #
app.get('/admin', handler).use(AuthMiddleware());
Route group middleware #
class AdminRoutes extends RouteGroup {
@override
String get prefix => '/admin';
@override
List<Middleware> get middlewares => [AuthMiddleware()];
@override
void register(Flint app) {
app.get('/users', (Context ctx) async => ctx.res?.json([]));
}
}
Built‑in middleware:
ExceptionMiddleware— error handlingCookieSessionMiddleware— cookies/sessionsCorsMiddleware— CORS headersLoggerMiddleware— request loggingStaticFileMiddleware— static files
Validation #
app.post('/register', (Context ctx) async {
final data = await ctx.req.validate({
'name': 'required|string|min:3',
'email': 'required|email',
'password': 'required|string|min:8',
}, messages: {
'email.required': 'Email is required.',
'password.min': 'Password must be at least :min characters.',
});
return ctx.res?.json({'ok': true, 'data': data});
});
Views & Templates #
Flint’s view engine uses .flint.html templates with a small set of processors. Views live in
lib/views and can extend layouts, include partials, loop, and render variables.
Render a view #
app.get('/', (Context ctx) async {
return ctx.res?.view('home', data: {'title': 'Flint Docs'});
});
Layouts, sections, and yield #
<!-- lib/views/layouts/app.flint.html -->
<!doctype html>
<html>
<head>
<title>{{ title ?? 'Flint' }}</title>
</head>
<body>
{{ yield('content') }}
</body>
</html>
<!-- lib/views/home.flint.html -->
{{ extends('layouts.app') }}
{{ section('content') }}
<h1>{{ title }}</h1>
{{ endsection }}
Includes (partials) #
{{ include('partials.nav') }}
Variables #
<h2>{{ user.name }}</h2>
Conditionals #
{{ if user }}
<p>Welcome back, {{ user.name }}</p>
{{ endif }}
Loops #
<ul>
{{ for item in items }}
<li>{{ item }}</li>
{{ endfor }}
</ul>
Switch / Case #
{{ switch status }}
{{ case 'paid' }}<span>Paid</span>{{ endcase }}
{{ case 'pending' }}<span>Pending</span>{{ endcase }}
{{ default }}<span>Unknown</span>{{ enddefault }}
{{ endswitch }}
Built‑in Processors #
extends— layout inheritancesection/yield— slot content into layoutsinclude— partials/partials with datavariables—{{ ... }}interpolationif_statement—if/endiffor_loop—for/endforswitch_cases—switch/case/defaultcomment— template commentsassets— asset helper tagssession— session/error helpers in templatesold_processor— legacy tags support
ORM (CRUD) #
// READ
final user = await User().find(1);
final users = await User()
.where('email', 'test@example.com')
.orderBy('created_at', desc: true)
.limit(10)
.get();
// CREATE
final created = await User().create({
'name': 'Ada',
'email': 'ada@example.com',
'password': 'secret',
});
// UPDATE
await User()
.where('id', 1)
.update(data: {'name': 'Ada Lovelace'});
// DELETE
await User().delete(1);
Extra ORM helpers #
save()— create/update based on PKfirstOrCreate(where, data)upsert(where, data)/upsertMany([...])countAll()/countWhere(field, value)
Models & Tables #
class User extends Model<User> {
User() : super(() => User());
String get name => getAttribute('name');
String get email => getAttribute('email');
@override
Table get table => Table(
name: 'users',
columns: [
Column(name: 'name', type: ColumnType.string, length: 255),
Column(name: 'email', type: ColumnType.string, length: 255),
],
);
}
Relations #
class User extends Model<User> {
@override
Map<String, RelationDefinition> get relations => {
'posts': Relations.hasMany('posts', () => Post()),
};
}
class Post extends Model<Post> {
@override
Map<String, RelationDefinition> get relations => {
'author': Relations.belongsTo('author', () => User()),
};
}
final posts = await Post().withRelation('author').get();
Auth (JWT + Providers) #
Register / Login #
final user = await Auth.register(
email: data['email'],
password: data['password'],
name: data['name'],
additionalData: {'role': 'admin'},
);
final result = await Auth.login(data['email'], data['password']);
OAuth Providers #
final url = Auth.providerRedirectUrl(
provider: 'google',
redirectPath: '/auth/google/callback',
);
Supported providers: Google, GitHub, Facebook, Apple.
File Uploads & Storage #
final upload = await ctx.req.file('avatar');
if (upload != null) {
final path = await ctx.req.storeFile('avatar', directory: 'public/uploads');
}
Swagger UI (Auto Docs) #
Enable docs and visit:
http://localhost:3000/docs
Flint parses annotations above your routes:
/// @summary Create a new user
/// @auth bearer
/// @response 201 Created
/// @query page integer optional Page
/// @body {"name": "string", "email": "string"}
app.post('/users', controller.create);
Database #
Configure .env:
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_NAME=flint
DB_USER=root
DB_PASSWORD=secret
DB_SECURE=false
Auto‑connect runs on server start. You can disable it and call DB.connect() manually.
CLI #
flint create my_app
flint run
flint build
flint docs:generate
flint make:model User
flint make:controller UserController
flint make:middleware AuthMiddleware
Contributing #
Contributions are welcome. Open issues, suggest improvements, or submit PRs.
License #
MIT