bloomBUILD•SHIP•BLOOM
.md
REST API Layer

REST API Layer

bloom_rest delivers a Django REST Framework (DRF) style API architecture on top of BloomApiRouter and bloom_db.

BloomViewSet & Mounting

mountViewSet<T> binds a full suite of CRUD endpoints (list, retrieve, create, update, destroy) onto BloomApiRouter with pagination, permissions, throttling, and filtering in a single declaration:

lib/apps/blog/views.dart
dart
// lib/apps/blog/views.dart - DRF-style ViewSet
import 'package:bloom_cache/bloom_cache.dart';
import 'package:bloom_db/bloom_db.dart';
import 'package:bloom_framework/bloom_server.dart';
import 'package:bloom_rest/bloom_rest.dart';
import 'models.dart';

void registerBlogApi(BloomApiRouter router, DbExecutor db, BloomCache cache) {
  // 1. Configure Model Serializer with FieldSet visibility rules
  final serializer = BloomModelSerializer<Article>(
    meta: Article.meta,
    fields: BloomFieldSet.all().withReadOnly(['id', 'createdAt']),
  );

  // 2. Configure ViewSet options
  final options = BloomViewSetOptions<Article>(
    serializer: serializer,
    config: const BloomViewSetConfig(
      filterableFields: ['status'],
      orderableFields: ['createdAt', 'title', 'id'],
      defaultPageSize: 20,
    ),
    pagination: const PageNumberPagination(
      defaultPageSize: 20,
      maxPageSize: 100,
    ),
    // Composed permissions
    permission: const IsAuthenticated().and(const IsStaff()),
    // Cache-backed throttling
    throttle: BloomThrottle.fromRate(
      scope: 'articles_api',
      rate: '100/hour',
      cache: cache,
    ),
    filterBackends: [
      const BloomSearchFilter<Article>(['title', 'content']),
      const BloomOrderingFilter<Article>(['createdAt', 'title']),
    ],
  );

  // 3. Mount CRUD routes onto BloomApiRouter in a single call
  mountViewSet<Article>(
    router: router,
    basePath: '/api/articles',
    meta: Article.meta,
    fromRow: Article.fromRow,
    getDb: (req) => db,
    options: options,
  );
}

Serializers & FieldSets

BloomModelSerializer inspects ModelMeta to serialize and deserialize model instances. BloomFieldSet provides granular control over field exposure:

  • BloomFieldSet.all().withReadOnly(['id', 'createdAt']) — Exposes all fields, preventing clients from mutating identifier or timestamp columns.
  • BloomFieldSet.only(['id', 'title', 'content']) — Whitelists specific fields for serialization.
  • BloomFieldSet.exclude(['passwordHash']) — Strips sensitive fields from serialized responses.

Pluggable Pagination

PageNumberPagination

?page=1&page_size=20

Returns count, total_pages, page, and results.

LimitOffsetPagination

?limit=20&offset=40

Returns count, limit, offset, and results.

CursorPagination

?cursor=eyJpZ...

Keyset pagination with opaque base64 cursors, immune to pagination drift.

Composable Permissions

Permission classes can be composed with boolean logic (.and(), .or(), .negate()):

  • IsAuthenticated() — Requires an authenticated session token.
  • IsStaff() / IsSuperuser() — Requires administrative role claims.
  • AllowAny() — Public access without restriction.
  • IsReadOnly() — Permits safe methods (GET, HEAD, OPTIONS).

Throttling & Filter Backends

BloomThrottle.fromRate() enforces DRF rate strings like "100/hour" or "10/minute" backed by BloomCache. Filter backends provide out-of-the-box support for query string parameters:

  • BloomSearchFilter — Multi-field search (?search=term).
  • BloomOrderingFilter — Dynamic sort ordering (?ordering=-createdAt,title).
  • BloomFieldFilter — Exact and lookup filtering (?status=published&view_count__gte=10).