Skip to content

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

License

Notifications You must be signed in to change notification settings

aware-agent/supadart

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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

Table of Contents πŸ“š

Features πŸš€

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

Conversion Table πŸ“Š

Supabase Identifier PostgreSQL Format JSON Type Dart Type Runtime Tested
# int2 smallint integer int type βœ… type[]βœ…
# int4 integer integer int type βœ… type[]βœ…
# int8 bigint integer BigInt type βœ… type[]βœ…
# float4 real number double type βœ… type[]βœ…
# float8 double precision number double type βœ… type[]βœ…
# numeric numeric number num type βœ… type[]βœ…
{} json json object Map<String, dynamic> type βœ… type[]βœ…
{} jsonb jsonb object Map<String, dynamic> type βœ… type[]βœ…
T text text string String type βœ… type[]βœ…
T varchar character varying string String type βœ… type[]βœ…
T uuid uuid string String type βœ… type[]βœ…
πŸ—“οΈ date date string DateTime type βœ… type[]βœ…
πŸ—“οΈ time time without time zone string DateTime type βœ… type[]βœ…
πŸ—“οΈ timetz time with time zone string DateTime type βœ… type[]βœ…
πŸ—“οΈ timestamp timestamp without time zone string DateTime type βœ… type[]βœ…
πŸ—“οΈ timestamptz timestamp with time zone string DateTime type βœ… type[]βœ…
πŸ’‘ bool boolean boolean bool type βœ… type[]βœ…

Other Types

Generating Dart Classes

1. Pre-requisites

1.2 if you have serial types you need to add a [supadart:serial] to the column like this,

COMMENT ON COLUMN test_table.bigserialx IS '[supadart:serial]';
COMMENT ON COLUMN test_table.smallserialx IS 'you can still add comment [supadart:serial]';
COMMENT ON COLUMN test_table.serialx IS 'this part [supadart:serial] just needs to be included';
-- otherwise the insert method will always ask for a value

serial types in general are not available in supabase table editor afaik, so if you did not add them manually via sql editor you probably dont have them.

Why do we need this?

1.3 Install Internationalization package

# This is an official package from dart and is used for parsing dates
flutter pub add intl
# or
dart pub add intl

2. Generate Dart Classes

Using the Web App

Using the Dart CLI

Installation

# 🎯 Active from pub.dev
dart pub global activate supadart
# πŸš€ Run via
supadart
# or
dart pub global run supadart

Generate Classes

# If you have a .env file with SUPABASE_URL and SUPABASE_ANON_KEY in the root of your project
supadart

# If you have a .env file in a different location
supadart -e path/to/.env

# If you dont have a .env file specify the Supabase URL and ANON KEY
supadart -u <your-supabase-url> -k <your-supabase-anon-key>

Note: If you are not using Flutter, just normal Dart project, add -d option

Options

-h, --help         Show usage information
-e, --env-path     Path to the .env file -- (default: .env)
-u, --url          Supabase URL          -- (default: .env SUPABASE_URL)
-k, --key          Supabase ANON KEY     -- (default: .env SUPABASE_ANON_KEY)
-o, --output       Output file path      -- (default: "lib/generated_classes.dart" or "lib/models/" if --seperated is enabled)
-d, --dart         Enable if you are not using Flutter, just normal Dart project
-s, --seperated    Generate Seperate files for each classes
-v, --version      v1.3.2

Example Usage

Assuming the following table schema

we recommend using snake_casing for your table names, as it will be converted to PamelCasing in the generated classes.

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;

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

class Books implements SupadartClass<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 List<Books>? converter(List<Map<String, dynamic>> data) {
    return data.map((data) => Books.fromJson(data)).toList();
  }

  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: json['id'] != null
          ? BigInt.tryParse(json['id'].toString()) as BigInt
          : BigInt.from(0),
      name: json['name'] != null ? json['name'].toString() : '',
      description:
          json['description'] != null ? json['description'].toString() : '',
      price: json['price'] != null ? json['price'] as int : 0,
      created_at: json['created_at'] != null
          ? DateTime.tryParse(json['created_at'].toString()) as DateTime
          : DateTime.fromMillisecondsSinceEpoch(0),
    );
  }
}

2. Using the generated class

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

Getting Table Name

  Books.table_name // "books"

Fetch Data

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

Insert Data

// Yes we know which one's are optional or required.
final data = Books.insert(
  name: 'Learn Flutter',
  description: 'Endless brackets and braces',
  price: 2,
);
await supabase.books.insert(data);

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

Update Data

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

Delete Data

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

Fetch Single Data

// book is a typeof Books
final book = await supabase
      .books
      .select("*")
      .eq(Books.c_id, 1)
      .single()
      .withConverter(Books.converterSingle);

Contributors

@mmvergara
@bookshiyi

About

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

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Dart 83.9%
  • TypeScript 15.6%
  • Other 0.5%