DEV Community

Cover image for Typesafe Supabase Flutter Queries
mmvergara
mmvergara

Posted on • Edited on

Typesafe Supabase Flutter Queries

Supabase Flutter Types? In web development, supabase provide you with an API to generate typescript types to make typesafe queries. But what about for flutter? for dart? That's what this is all about

Yes we can generate dart classes directly from you supabase schema in order to achieve Typesafe Queries Flutter Supabase

Supabase Schema Dart Class Generator using this tool you can generate dart class via WebApp or CLI

1. Assuming the following table schema



create table
  public.books (
    id bigint generated by default as identity,
    name character varying not null,
    description text null,
    price integer not null,
    created_at timestamp with time zone not null default now(),
    constraint books_pkey primary key (id)
  ) tablespace pg_default;


Enter fullscreen mode Exit fullscreen mode

2. Use the CLI or the Web App to generate dart classes



class Books {
  final BigInt id;
  final String name;
  final String? description;
  final int price;
  final DateTime created_at;

  const Books({
    required this.id,
    required this.name,
    this.description,
    required this.price,
    required this.created_at,
  });

  static String get table_name => 'books';
  static String get c_id => 'id';
  static String get c_name => 'name';
  static String get c_description => 'description';
  static String get c_price => 'price';
  static String get c_created_at => 'created_at';
  static Map<String, dynamic> insert({
    BigInt? id,
    required String name,
    String? description,
    required int price,
    DateTime? created_at,
  }) {
    return {
      if (id != null) 'id': id.toString(),
      'name': name.toString(),
      if (description != null) 'description': description.toString(),
      'price': price.toString(),
      if (created_at != null) 'created_at': created_at.toUtc().toString(),
    };
  }

  static Map<String, dynamic> update({
    BigInt? id,
    String? name,
    String? description,
    int? price,
    DateTime? created_at,
  }) {
    return {
      if (id != null) 'id': id.toString(),
      if (name != null) 'name': name.toString(),
      if (description != null) 'description': description.toString(),
      if (price != null) 'price': price.toString(),
      if (created_at != null) 'created_at': created_at.toUtc().toString(),
    };
  }

  factory Books.fromJson(Map<String, dynamic> json) {
    return Books(
      id: BigInt.parse(json['id'].toString()),
      name: json['name'] as String,
      description:
          json['description'] != null ? json['description'] as String : null,
      price: json['price'] as int,
      created_at: DateTime.parse(json['created_at'].toString()),
    );
  }
}


Enter fullscreen mode Exit fullscreen mode

3. Using the generated class

we now have a typesafe'ish to interact with the database.

Getting Table Name



  Books.table_name // "books"


Enter fullscreen mode Exit fullscreen mode

Fetch Data



// fetchedBooks is a typeof List<Books>
final books = await supabase
      .books
      .select("*")
      .withConverter((data) => data.map(Books.fromJson).toList());


Enter fullscreen mode Exit fullscreen mode

Insert Data

yes, we know which ones is required and which ones are optional



final data = Books.insert(
  name: 'Learn Flutter',
  description: 'Endless brackets and braces',
  price: 2,
);
await supabase.books.insert(data);


Enter fullscreen mode Exit fullscreen mode

Inset Many Data



final many_data = [
  Books.insert(
    name: 'Learn Minecraft',
    description: 'Endless blocks and bricks',
    price: 2,
  ),
  Books.insert(
    name: 'Description is optional',
    created_at: DateTime.now(),
    price: 2,
  ),
];
await supabase.books.insert(many_data);


Enter fullscreen mode Exit fullscreen mode

Update Data



final newData = Books.update(
  name: 'New Book Name',
);
await supabase.books.update(newData).eq(Books.c_id, 1);


Enter fullscreen mode Exit fullscreen mode

Delete Data



await supabase.books.delete().eq(Books.c_id, 1);


Enter fullscreen mode Exit fullscreen mode

How it works is that it uses the rest api to fetch for your schemas and then constructs the dart classes for you. Is it safe? yes first of all the project is open source and they api is used by other tools like this one that visualizes your database.

Is this only for flutter? no you can use it in a normal Dart Project.

Im trying to make it better for the community i would really appreciate some help and suggestions to improve it. especially the process of parsing the data to dart types, but either way the generated classes are tested for runtime for most supabase / postgres types

GitHub logo mmvergara / supadart

Typesafe queries in Supabase Flutter! Generate Flutter / Dart 🎯 classes from your Supabase schema.

Pub Version Pub Points GitHub Stars Runtime Test GitHub License

Supadart 🎯

Typesafe Supabase Flutter Queries
Generate Flutter / Dart 🎯 classes from your Supabase schema.

// allBooks is a typeof List<Books>
final allBooks = await supabase
      .books
      .select("*")
      .withConverter(Books.converter);
Enter fullscreen mode Exit fullscreen mode

Table of Contents πŸ“š

Features πŸš€

  • 🌐 Cli and Web App
  • πŸ› οΈ Typesafe Queries (Create, Read, Equality)
  • 🧱 Immutable Generated Classes
  • πŸ—‚οΈ Roundtrip Serialization fromJson to toJson and back
  • πŸ“Š Supports Column Selection Queries
  • πŸ”’ Supports all Supabase Major datatypes
  • πŸ—‚οΈ Supports Defined as array types
  • πŸ—‚οΈ Supports Enums

Conversion Table πŸ“Š

Supabase Identifier PostgreSQL Format JSON Type Dart Type Runtime Tested
# int2 smallint integer int type βœ… type[]βœ…
# int4 integer integer int type
…

Top comments (0)