Skip to content

Complex Structures Examples

Learn how to validate complex, nested data structures by combining Validasi's schema types.

E-Commerce Examples

Shopping Cart

dart
import 'package:validasi/validasi.dart';
import 'package:validasi/rules.dart';

final cartSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'user_id': Validasi.number<int>([
      NumberRules.moreThan(0),
    ]),
    'items': Validasi.list<Map<String, dynamic>>([
      IterableRules.minLength(1, message: 'Cart cannot be empty'),
      IterableRules.forEach(
        Validasi.map<dynamic>([
          MapRules.hasFields({
            'product_id': Validasi.number<int>([
              NumberRules.moreThan(0),
            ]),
            'name': Validasi.string([
              StringRules.minLength(1),
            ]),
            'price': Validasi.number<double>([
              NumberRules.moreThan(0.0),
            ]),
            'quantity': Validasi.number<int>([
              NumberRules.moreThan(0),
              NumberRules.lessThanEqual(99),
            ]),
          }),
        ]),
      ),
    ]),
    'shipping': Validasi.map<dynamic>([
      MapRules.hasFields({
        'address': Validasi.string([StringRules.minLength(10)]),
        'city': Validasi.string([StringRules.minLength(2)]),
        'postal_code': Validasi.string([StringRules.minLength(3)]),
        'country': Validasi.string([StringRules.minLength(2)]),
      }),
    ]),
    'payment_method': Validasi.string([
      StringRules.oneOf(['card', 'paypal', 'bank_transfer']),
    ]),
  }),
]);

final cart = {
  'user_id': 123,
  'items': [
    {
      'product_id': 1,
      'name': 'Wireless Mouse',
      'price': 29.99,
      'quantity': 2,
    },
    {
      'product_id': 2,
      'name': 'USB Cable',
      'price': 9.99,
      'quantity': 3,
    },
  ],
  'shipping': {
    'address': '123 Main Street, Apt 4B',
    'city': 'San Francisco',
    'postal_code': '94102',
    'country': 'USA',
  },
  'payment_method': 'card',
};

final result = cartSchema.validate(cart);
print('Valid: ${result.isValid}');

Product Catalog

dart
final catalogSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'categories': Validasi.list<Map<String, dynamic>>([
      IterableRules.minLength(1),
      IterableRules.forEach(
        Validasi.map<dynamic>([
          MapRules.hasFields({
            'id': Validasi.number<int>([NumberRules.moreThan(0)]),
            'name': Validasi.string([StringRules.minLength(1)]),
            'slug': Validasi.string([
              Transform((value) => value?.toLowerCase().replaceAll(' ', '-')),
            ]),
            'products': Validasi.list<Map<String, dynamic>>([
              IterableRules.forEach(
                Validasi.map<dynamic>([
                  MapRules.hasFields({
                    'id': Validasi.number<int>([NumberRules.moreThan(0)]),
                    'name': Validasi.string([StringRules.minLength(1)]),
                    'price': Validasi.number<double>([NumberRules.moreThan(0)]),
                    'in_stock': Validasi.any<bool>(),
                    'images': Validasi.list<String>([
                      IterableRules.minLength(1),
                    ]),
                    'variants': Validasi.list<Map<String, dynamic>>([
                      Nullable(),
                      IterableRules.forEach(
                        Validasi.map<dynamic>([
                          MapRules.hasFields({
                            'size': Validasi.string([Nullable()]),
                            'color': Validasi.string([Nullable()]),
                            'price_modifier': Validasi.number<double>([
                              Nullable(),
                            ]),
                          }),
                        ]),
                      ),
                    ]),
                  }),
                ]),
              ),
            ]),
          }),
        ]),
      ),
    ]),
  }),
]);

final catalog = {
  'categories': [
    {
      'id': 1,
      'name': 'Electronics',
      'slug': 'electronics',
      'products': [
        {
          'id': 101,
          'name': 'Wireless Headphones',
          'price': 99.99,
          'in_stock': true,
          'images': ['img1.jpg', 'img2.jpg'],
          'variants': [
            {'size': null, 'color': 'black', 'price_modifier': 0.0},
            {'size': null, 'color': 'white', 'price_modifier': 5.0},
          ],
        },
      ],
    },
  ],
};

print(catalogSchema.validate(catalog).isValid);

Social Media Examples

User Profile

dart
final profileSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'user': Validasi.map<dynamic>([
      MapRules.hasFields({
        'id': Validasi.number<int>([NumberRules.moreThan(0)]),
        'username': Validasi.string([
          Transform((value) => value?.trim().toLowerCase()),
          StringRules.minLength(3),
          StringRules.maxLength(20),
        ]),
        'email': Validasi.string([
          Transform((value) => value?.trim().toLowerCase()),
          InlineRule<String>((value) {
            return value.contains('@') ? null : 'Invalid email';
          }),
        ]),
        'bio': Validasi.string([
          Nullable(),
          StringRules.maxLength(500),
        ]),
        'avatar_url': Validasi.string([Nullable()]),
      }),
    ]),
    'stats': Validasi.map<dynamic>([
      MapRules.hasFields({
        'followers': Validasi.number<int>([NumberRules.moreThanEqual(0)]),
        'following': Validasi.number<int>([NumberRules.moreThanEqual(0)]),
        'posts': Validasi.number<int>([NumberRules.moreThanEqual(0)]),
      }),
    ]),
    'recent_posts': Validasi.list<Map<String, dynamic>>([
      IterableRules.forEach(
        Validasi.map<dynamic>([
          MapRules.hasFields({
            'id': Validasi.number<int>([NumberRules.moreThan(0)]),
            'content': Validasi.string([
              StringRules.minLength(1),
              StringRules.maxLength(280),
            ]),
            'likes': Validasi.number<int>([NumberRules.moreThanEqual(0)]),
            'comments': Validasi.number<int>([NumberRules.moreThanEqual(0)]),
            'created_at': Validasi.string([StringRules.minLength(1)]),
            'media': Validasi.list<Map<String, dynamic>>([
              Nullable(),
              IterableRules.forEach(
                Validasi.map<dynamic>([
                  MapRules.hasFields({
                    'type': Validasi.string([
                      StringRules.oneOf(['image', 'video', 'gif']),
                    ]),
                    'url': Validasi.string([StringRules.minLength(1)]),
                  }),
                ]),
              ),
            ]),
          }),
        ]),
      ),
    ]),
  }),
]);

final profile = {
  'user': {
    'id': 12345,
    'username': 'JohnDoe',
    'email': 'JOHN@EXAMPLE.COM',
    'bio': 'Software developer and coffee enthusiast',
    'avatar_url': 'https://example.com/avatar.jpg',
  },
  'stats': {
    'followers': 1234,
    'following': 567,
    'posts': 89,
  },
  'recent_posts': [
    {
      'id': 1,
      'content': 'Just shipped a new feature! 🚀',
      'likes': 42,
      'comments': 5,
      'created_at': '2024-01-15T10:00:00Z',
      'media': [
        {
          'type': 'image',
          'url': 'https://example.com/post1.jpg',
        },
      ],
    },
  ],
};

final result = profileSchema.validate(profile);
print('Valid: ${result.isValid}');
print('Username: ${result.data['user']['username']}'); // 'johndoe'

Comment Thread

dart
final commentSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'id': Validasi.number<int>([NumberRules.moreThan(0)]),
    'author': Validasi.string([StringRules.minLength(1)]),
    'content': Validasi.string([
      StringRules.minLength(1),
      StringRules.maxLength(1000),
    ]),
    'created_at': Validasi.string([StringRules.minLength(1)]),
    'likes': Validasi.number<int>([NumberRules.moreThanEqual(0)]),
    'replies': Validasi.list<Map<String, dynamic>>([
      Nullable(),
      IterableRules.forEach(
        Validasi.map<dynamic>([
          MapRules.hasFields({
            'id': Validasi.number<int>([NumberRules.moreThan(0)]),
            'author': Validasi.string([StringRules.minLength(1)]),
            'content': Validasi.string([
              StringRules.minLength(1),
              StringRules.maxLength(500),
            ]),
            'created_at': Validasi.string([StringRules.minLength(1)]),
            'likes': Validasi.number<int>([NumberRules.moreThanEqual(0)]),
          }),
        ]),
      ),
    ]),
  }),
]);

final thread = {
  'id': 1,
  'author': 'Alice',
  'content': 'Great article! Thanks for sharing.',
  'created_at': '2024-01-15T10:00:00Z',
  'likes': 15,
  'replies': [
    {
      'id': 2,
      'author': 'Bob',
      'content': 'I agree! Very helpful.',
      'created_at': '2024-01-15T10:30:00Z',
      'likes': 5,
    },
  ],
};

print(commentSchema.validate(thread).isValid);

API Response Validation

Paginated API Response

dart
final paginatedResponseSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'data': Validasi.list<Map<String, dynamic>>([
      IterableRules.forEach(
        Validasi.map<dynamic>([
          MapRules.hasFields({
            'id': Validasi.number<int>([NumberRules.moreThan(0)]),
            'title': Validasi.string([StringRules.minLength(1)]),
            'description': Validasi.string([Nullable()]),
            'created_at': Validasi.string([StringRules.minLength(1)]),
          }),
        ]),
      ),
    ]),
    'pagination': Validasi.map<dynamic>([
      MapRules.hasFields({
        'page': Validasi.number<int>([NumberRules.moreThan(0)]),
        'per_page': Validasi.number<int>([
          NumberRules.moreThan(0),
          NumberRules.lessThanEqual(100),
        ]),
        'total': Validasi.number<int>([NumberRules.moreThanEqual(0)]),
        'total_pages': Validasi.number<int>([NumberRules.moreThanEqual(0)]),
      }),
    ]),
    'links': Validasi.map<dynamic>([
      MapRules.hasFields({
        'first': Validasi.string([Nullable()]),
        'last': Validasi.string([Nullable()]),
        'prev': Validasi.string([Nullable()]),
        'next': Validasi.string([Nullable()]),
      }),
    ]),
  }),
]);

final apiResponse = {
  'data': [
    {
      'id': 1,
      'title': 'First Item',
      'description': 'Description here',
      'created_at': '2024-01-15T10:00:00Z',
    },
  ],
  'pagination': {
    'page': 1,
    'per_page': 20,
    'total': 100,
    'total_pages': 5,
  },
  'links': {
    'first': 'https://api.example.com/items?page=1',
    'last': 'https://api.example.com/items?page=5',
    'prev': null,
    'next': 'https://api.example.com/items?page=2',
  },
};

print(paginatedResponseSchema.validate(apiResponse).isValid);

Error Response

dart
final errorResponseSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'error': Validasi.map<dynamic>([
      MapRules.hasFields({
        'code': Validasi.string([StringRules.minLength(1)]),
        'message': Validasi.string([StringRules.minLength(1)]),
        'details': Validasi.list<Map<String, dynamic>>([
          Nullable(),
          IterableRules.forEach(
            Validasi.map<dynamic>([
              MapRules.hasFields({
                'field': Validasi.string([StringRules.minLength(1)]),
                'error': Validasi.string([StringRules.minLength(1)]),
              }),
            ]),
          ),
        ]),
      }),
    ]),
    'status': Validasi.number<int>([
      NumberRules.moreThanEqual(400),
      NumberRules.lessThan(600),
    ]),
  }),
]);

final errorResponse = {
  'error': {
    'code': 'VALIDATION_ERROR',
    'message': 'The request contains invalid data',
    'details': [
      {'field': 'email', 'error': 'Invalid email format'},
      {'field': 'age', 'error': 'Must be at least 18'},
    ],
  },
  'status': 422,
};

print(errorResponseSchema.validate(errorResponse).isValid);

Configuration Files

Application Config

dart
final appConfigSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'app': Validasi.map<dynamic>([
      MapRules.hasFields({
        'name': Validasi.string([StringRules.minLength(1)]),
        'version': Validasi.string([StringRules.minLength(1)]),
        'env': Validasi.string([
          StringRules.oneOf(['development', 'staging', 'production']),
        ]),
      }),
    ]),
    'server': Validasi.map<dynamic>([
      MapRules.hasFields({
        'host': Validasi.string([StringRules.minLength(1)]),
        'port': Validasi.number<int>([
          NumberRules.moreThan(0),
          NumberRules.lessThan(65536),
        ]),
        'ssl': Validasi.any<bool>(),
      }),
    ]),
    'database': Validasi.map<dynamic>([
      MapRules.hasFields({
        'host': Validasi.string([StringRules.minLength(1)]),
        'port': Validasi.number<int>([NumberRules.moreThan(0)]),
        'name': Validasi.string([StringRules.minLength(1)]),
        'user': Validasi.string([StringRules.minLength(1)]),
        'password': Validasi.string([StringRules.minLength(1)]),
        'pool_size': Validasi.number<int>([
          NumberRules.moreThan(0),
          NumberRules.lessThanEqual(100),
        ]),
      }),
    ]),
    'cache': Validasi.map<dynamic>([
      MapRules.hasFields({
        'driver': Validasi.string([
          StringRules.oneOf(['redis', 'memcached', 'memory']),
        ]),
        'ttl': Validasi.number<int>([NumberRules.moreThan(0)]),
      }),
    ]),
    'logging': Validasi.map<dynamic>([
      MapRules.hasFields({
        'level': Validasi.string([
          StringRules.oneOf(['debug', 'info', 'warning', 'error']),
        ]),
        'outputs': Validasi.list<String>([
          IterableRules.minLength(1),
          IterableRules.forEach(
            Validasi.string([
              StringRules.oneOf(['console', 'file', 'syslog']),
            ]),
          ),
        ]),
      }),
    ]),
  }),
]);

final config = {
  'app': {
    'name': 'MyApp',
    'version': '1.0.0',
    'env': 'production',
  },
  'server': {
    'host': '0.0.0.0',
    'port': 8080,
    'ssl': true,
  },
  'database': {
    'host': 'localhost',
    'port': 5432,
    'name': 'myapp_db',
    'user': 'dbuser',
    'password': 'secret',
    'pool_size': 20,
  },
  'cache': {
    'driver': 'redis',
    'ttl': 3600,
  },
  'logging': {
    'level': 'info',
    'outputs': ['console', 'file'],
  },
};

print(appConfigSchema.validate(config).isValid);

Forms and Surveys

Multi-Step Form

dart
final multiStepFormSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'personal_info': Validasi.map<dynamic>([
      MapRules.hasFields({
        'first_name': Validasi.string([
          Transform((value) => value?.trim()),
          StringRules.minLength(1),
        ]),
        'last_name': Validasi.string([
          Transform((value) => value?.trim()),
          StringRules.minLength(1),
        ]),
        'email': Validasi.string([
          Transform((value) => value?.trim().toLowerCase()),
          InlineRule<String>((value) {
            return value.contains('@') ? null : 'Invalid email';
          }),
        ]),
        'phone': Validasi.string([
          Nullable(),
          StringRules.minLength(10),
        ]),
      }),
    ]),
    'address': Validasi.map<dynamic>([
      MapRules.hasFields({
        'street': Validasi.string([StringRules.minLength(1)]),
        'city': Validasi.string([StringRules.minLength(1)]),
        'state': Validasi.string([StringRules.minLength(2)]),
        'zip': Validasi.string([StringRules.minLength(5)]),
        'country': Validasi.string([StringRules.minLength(2)]),
      }),
    ]),
    'preferences': Validasi.map<dynamic>([
      MapRules.hasFields({
        'newsletter': Validasi.any<bool>(),
        'notifications': Validasi.map<dynamic>([
          MapRules.hasFields({
            'email': Validasi.any<bool>(),
            'sms': Validasi.any<bool>(),
            'push': Validasi.any<bool>(),
          }),
        ]),
        'interests': Validasi.list<String>([
          IterableRules.minLength(1, message: 'Select at least one interest'),
          IterableRules.forEach(
            Validasi.string([StringRules.minLength(1)]),
          ),
        ]),
      }),
    ]),
  }),
]);

final formData = {
  'personal_info': {
    'first_name': 'John',
    'last_name': 'Doe',
    'email': 'JOHN@EXAMPLE.COM',
    'phone': '555-0123',
  },
  'address': {
    'street': '123 Main St',
    'city': 'Springfield',
    'state': 'IL',
    'zip': '62701',
    'country': 'USA',
  },
  'preferences': {
    'newsletter': true,
    'notifications': {
      'email': true,
      'sms': false,
      'push': true,
    },
    'interests': ['technology', 'sports', 'music'],
  },
};

final result = multiStepFormSchema.validate(formData);
print('Valid: ${result.isValid}');

Survey Response

dart
final surveySchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'survey_id': Validasi.number<int>([NumberRules.moreThan(0)]),
    'respondent': Validasi.map<dynamic>([
      MapRules.hasFields({
        'id': Validasi.number<int>([Nullable()]),
        'email': Validasi.string([Nullable()]),
      }),
    ]),
    'responses': Validasi.list<Map<String, dynamic>>([
      IterableRules.minLength(1, message: 'At least one response required'),
      IterableRules.forEach(
        Validasi.map<dynamic>([
          MapRules.hasFields({
            'question_id': Validasi.number<int>([NumberRules.moreThan(0)]),
            'question_type': Validasi.string([
              StringRules.oneOf(['text', 'choice', 'rating', 'boolean']),
            ]),
            'answer': Validasi.any<dynamic>([
              Required(message: 'Answer is required'),
            ]),
          }),
        ]),
      ),
    ]),
    'completed_at': Validasi.string([StringRules.minLength(1)]),
  }),
]);

final survey = {
  'survey_id': 123,
  'respondent': {
    'id': 456,
    'email': 'user@example.com',
  },
  'responses': [
    {
      'question_id': 1,
      'question_type': 'text',
      'answer': 'I really enjoyed the service',
    },
    {
      'question_id': 2,
      'question_type': 'rating',
      'answer': 5,
    },
    {
      'question_id': 3,
      'question_type': 'boolean',
      'answer': true,
    },
  ],
  'completed_at': '2024-01-15T10:00:00Z',
};

print(surveySchema.validate(survey).isValid);

Data Import/Export

CSV Import Schema

dart
final csvImportSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'filename': Validasi.string([StringRules.minLength(1)]),
    'rows': Validasi.list<Map<String, dynamic>>([
      IterableRules.minLength(1, message: 'CSV must have at least one row'),
      IterableRules.forEach(
        Validasi.map<dynamic>([
          MapRules.hasFields({
            'row_number': Validasi.number<int>([NumberRules.moreThan(0)]),
            'data': Validasi.map<dynamic>([
              MapRules.hasFields({
                'name': Validasi.string([
                  Transform((value) => value?.trim()),
                  StringRules.minLength(1, message: 'Name cannot be empty'),
                ]),
                'email': Validasi.string([
                  Transform((value) => value?.trim().toLowerCase()),
                  InlineRule<String>((value) {
                    return value.contains('@') ? null : 'Invalid email';
                  }),
                ]),
                'age': Validasi.number<int>([
                  Nullable(),
                  NumberRules.moreThanEqual(0),
                  NumberRules.lessThanEqual(150),
                ]),
              }),
            ]),
          }),
        ]),
      ),
    ]),
    'imported_at': Validasi.string([StringRules.minLength(1)]),
  }),
]);

Analytics Data

Event Tracking

dart
final analyticsSchema = Validasi.map<dynamic>([
  MapRules.hasFields({
    'session_id': Validasi.string([StringRules.minLength(1)]),
    'user': Validasi.map<dynamic>([
      MapRules.hasFields({
        'id': Validasi.number<int>([Nullable()]),
        'anonymous_id': Validasi.string([Nullable()]),
      }),
    ]),
    'events': Validasi.list<Map<String, dynamic>>([
      IterableRules.minLength(1),
      IterableRules.forEach(
        Validasi.map<dynamic>([
          MapRules.hasFields({
            'name': Validasi.string([StringRules.minLength(1)]),
            'timestamp': Validasi.string([StringRules.minLength(1)]),
            'properties': Validasi.map<dynamic>([Nullable()]),
            'page': Validasi.map<dynamic>([
              Nullable(),
              MapRules.hasFields({
                'url': Validasi.string([StringRules.minLength(1)]),
                'title': Validasi.string([Nullable()]),
                'referrer': Validasi.string([Nullable()]),
              }),
            ]),
          }),
        ]),
      ),
    ]),
  }),
]);

final analyticsData = {
  'session_id': 'sess_abc123',
  'user': {
    'id': 12345,
    'anonymous_id': null,
  },
  'events': [
    {
      'name': 'page_view',
      'timestamp': '2024-01-15T10:00:00Z',
      'properties': {'source': 'organic'},
      'page': {
        'url': 'https://example.com/home',
        'title': 'Home Page',
        'referrer': 'https://google.com',
      },
    },
    {
      'name': 'button_click',
      'timestamp': '2024-01-15T10:01:00Z',
      'properties': {'button_id': 'signup'},
      'page': null,
    },
  ],
};

print(analyticsSchema.validate(analyticsData).isValid);

Next Steps

Released under the MIT License.