Learn by example with practical, end-to-end walkthroughs.
Browse curated starter projects and tutorials.
Build a blog with role-based publishing, comments, and moderation.
Beginneradmin and contributor can publish blog posts.dev can create questions/issues, answer, and comment.Create a new Flint project and generate models:
flint create blog_platform
cd blog_platform
flint --make-model User
flint --make-model Post
flint --make-model Comment
flint migrate
Define User, Post, and Comment models with relations:
class User extends Model {
User() : super(() => User());
String? get name => getAttribute('name');
String? get email => getAttribute('email');
String? get role => getAttribute('role');
@override
Table get table => Table(
name: 'users',
columns: [
Column(name: 'name', type: ColumnType.string, length: 255),
Column(name: 'email', type: ColumnType.string, length: 255),
Column(name: 'password', type: ColumnType.string),
Column(name: 'role', type: ColumnType.string, length: 50),
],
);
@override
Map get relations => {
'posts': Relations.hasMany('posts', () => Post(), foreignKey: 'user_id'),
'comments': Relations.hasMany('comments', () => Comment(), foreignKey: 'user_id'),
};
}
class Post extends Model {
Post() : super(() => Post());
String? get title => getAttribute('title');
String? get content => getAttribute('content');
@override
Table get table => Table(
name: 'posts',
columns: [
Column(name: 'title', type: ColumnType.string, length: 255),
Column(name: 'content', type: ColumnType.text),
Column(name: 'slug', type: ColumnType.string, length: 255),
Column(name: 'user_id', type: ColumnType.string),
],
);
@override
Map get relations => {
'author': Relations.belongsTo('author', () => User(), foreignKey: 'user_id'),
'comments': Relations.hasMany('comments', () => Comment(), foreignKey: 'post_id'),
};
}
Gate publishing routes to admin and contributor roles:
class RoleGuard extends Middleware {
RoleGuard(this.allowedRoles);
final List allowedRoles;
@override
Handler handle(Handler next) {
return (Context ctx) async {
final user = await ctx.req.user;
if (user == null) {
return ctx.res.status(401).json({'error': 'Unauthorized'});
}
final role = user['role'];
if (!allowedRoles.contains(role)) {
return ctx.res.status(403).json({'error': 'Forbidden'});
}
return next(ctx);
};
}
}
Publish posts with role checks and comment support:
app.post('/posts', (Context ctx) async {
final data = await ctx.req.validate({
'title': 'required|string',
'content': 'required|string',
});
final user = await ctx.req.user;
final post = await Post().create({
'title': data['title'],
'content': data['content'],
'slug': data['title'].toString().toLowerCase().replaceAll(' ', '-'),
'user_id': user?['id'],
});
return ctx.res.json({'success': true, 'post': post}, status: 201);
}).useMiddleware(RoleGuard(['admin', 'contributor']));
app.post('/posts/:id/comments', (Context ctx) async {
final data = await ctx.req.validate({'content': 'required|string'});
final user = await ctx.req.user;
final comment = await Comment().create({
'content': data['content'],
'post_id': ctx.req.param('id'),
'user_id': user?['id'],
});
return ctx.res.json({'success': true, 'comment': comment});
}).useMiddleware(RoleGuard(['admin', 'contributor', 'dev']));
Build a Q&A system with role-based posting for issues and answers.
Intermediatedev can post questions/issues and answers.admin and contributor can answer and comment as well.Define Question and Answer models:
class Question extends Model {
Question() : super(() => Question());
@override
Table get table => Table(
name: 'questions',
columns: [
Column(name: 'title', type: ColumnType.string, length: 255),
Column(name: 'body', type: ColumnType.text),
Column(name: 'user_id', type: ColumnType.string),
Column(name: 'status', type: ColumnType.string, length: 50),
],
);
}
class Answer extends Model {
Answer() : super(() => Answer());
@override
Table get table => Table(
name: 'answers',
columns: [
Column(name: 'body', type: ColumnType.text),
Column(name: 'question_id', type: ColumnType.string),
Column(name: 'user_id', type: ColumnType.string),
],
);
}
Allow devs to post questions and answers:
app.post('/questions', (Context ctx) async {
final data = await ctx.req.validate({
'title': 'required|string',
'body': 'required|string',
});
final user = await ctx.req.user;
final question = await Question().create({
'title': data['title'],
'body': data['body'],
'status': 'open',
'user_id': user?['id'],
});
return ctx.res.json({'success': true, 'question': question}, status: 201);
}).useMiddleware(RoleGuard(['dev', 'admin', 'contributor']));
app.post('/questions/:id/answers', (Context ctx) async {
final data = await ctx.req.validate({'body': 'required|string'});
final user = await ctx.req.user;
final answer = await Answer().create({
'body': data['body'],
'question_id': ctx.req.param('id'),
'user_id': user?['id'],
});
return ctx.res.json({'success': true, 'answer': answer}, status: 201);
}).useMiddleware(RoleGuard(['dev', 'admin', 'contributor']));
Reuse the comment model to attach comments to questions or answers:
app.post('/questions/:id/comments', (Context ctx) async {
final data = await ctx.req.validate({'content': 'required|string'});
final user = await ctx.req.user;
final comment = await Comment().create({
'content': data['content'],
'question_id': ctx.req.param('id'),
'user_id': user?['id'],
});
return ctx.res.json({'success': true, 'comment': comment});
}).useMiddleware(RoleGuard(['dev', 'admin', 'contributor']));