title
Examples v1.0.0+27 Hands-on tutorials

Examples & Tutorials

Learn by example with practical, end-to-end walkthroughs.

Auth ready ORM included Views + APIs

Example Projects

Browse curated starter projects and tutorials.

Blog Platform Tutorial

Build a blog with role-based publishing, comments, and moderation.

Beginner

Roles & Permissions

  • admin and contributor can publish blog posts.
  • dev can create questions/issues, answer, and comment.
  • All authenticated roles can comment on posts.
  1. Setup Project

    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
  2. Create Models

    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'),
          };
    }
  3. Role Guard Middleware

    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);
        };
      }
    }
  4. Create Controllers

    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']));

Questions & Answers Tutorial

Build a Q&A system with role-based posting for issues and answers.

Intermediate

Roles & Permissions

  • dev can post questions/issues and answers.
  • admin and contributor can answer and comment as well.
  • All authenticated roles can comment on Q&A threads.
  1. Models

    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),
            ],
          );
    }
  2. Routes & Role Guard

    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']));
  3. Comments

    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']));