Pular para o conteúdo
🐦 FLUTTER

SuperDB com Flutter.

SDK Dart ainda não disponível — mas você já pode usar a REST API diretamente hoje.

🚧

SDK Dart ainda não disponível. Enquanto isso, use a REST API diretamente — é simples e funciona hoje.

Precisa do SDK Dart? Conte pelo suporte →

Usando a REST API diretamente

O SuperDB expõe APIs REST padrão. Com o pacote http do Dart, você pode fazer signup, login e queries de banco de dados sem nenhum SDK específico.

Uma diferença que vale ler antes de escrever código: o login devolve dois tokens. O access_token é a sessão; o data_plane_token é o único que api.superdb.com.br aceita. O SDK esconde isso — chamando por HTTP, você escolhe.

Dependências necessárias

Adicione ao pubspec.yaml:

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  http: ^1.2.0
  flutter_secure_storage: ^9.0.0   # armazenar JWT de forma segura
  shared_preferences: ^2.2.0       # alternativa simples

Signup e login

A API de auth do SuperDB segue o padrão GoTrue. Os endpoints são:

  • Signup: POST /auth/v1/signup
  • Login: POST /auth/v1/signin
lib/services/auth_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class AuthService {
  static const String baseUrl = 'https://auth.superdb.com.br';
  static const String anonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
  // O slug do seu projeto. É ELE que diz ao servidor de qual tenant se trata —
  // a anon key não faz isso. Sem este header, toda chamada de auth devolve
  // 404 tenant_not_found, que é fácil confundir com URL errada.
  static const String projeto = 'meu-projeto';
  final _storage = const FlutterSecureStorage();

  Map<String, String> get _headers => {
    'Content-Type': 'application/json',
    'X-SuperDB-Project': projeto,
    'apikey': anonKey,
  };

  Future<Map<String, dynamic>?> signUp(String email, String password) async {
    final response = await http.post(
      Uri.parse('$baseUrl/auth/v1/signup'),
      headers: _headers,
      body: jsonEncode({'email': email, 'password': password}),
    );
    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      // São DOIS tokens. O access_token é a sessão (auth.superdb.com.br);
      // o data_plane_token é o único que api.superdb.com.br aceita.
      await _storage.write(key: 'access_token', value: data['access_token']);
      await _storage.write(key: 'data_plane_token', value: data['data_plane_token']);
      await _storage.write(key: 'refresh_token', value: data['refresh_token']);
      return data;
    }
    throw Exception('Signup falhou: ${response.body}');
  }

  Future<Map<String, dynamic>?> signIn(String email, String password) async {
    final response = await http.post(
      Uri.parse('$baseUrl/auth/v1/signin'),
      headers: _headers,
      body: jsonEncode({'email': email, 'password': password}),
    );
    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      // São DOIS tokens. O access_token é a sessão (auth.superdb.com.br);
      // o data_plane_token é o único que api.superdb.com.br aceita.
      await _storage.write(key: 'access_token', value: data['access_token']);
      await _storage.write(key: 'data_plane_token', value: data['data_plane_token']);
      await _storage.write(key: 'refresh_token', value: data['refresh_token']);
      return data;
    }
    throw Exception('Login falhou: ${response.body}');
  }

  /// Sessão — use em auth.superdb.com.br (logout, trocar senha, etc).
  Future<String?> getToken() async {
    return await _storage.read(key: 'access_token');
  }

  /// Dados — é ESTE que vai no Authorization de api.superdb.com.br.
  /// Vale 1 hora; renove em /auth/v1/token/refresh com o refresh_token.
  Future<String?> getDataPlaneToken() async {
    return await _storage.read(key: 'data_plane_token');
  }

  Future<void> signOut() async {
    final token = await getToken();
    if (token != null) {
      await http.post(
        Uri.parse('$baseUrl/auth/v1/signout'),
        headers: { ..._headers, 'Authorization': 'Bearer $token' },
      );
    }
    await _storage.deleteAll();
  }
}

Queries no banco de dados

O banco de dados é acessível via PostgREST. Use o JWT do usuário no header Authorization — o RLS filtra automaticamente os dados conforme as policies configuradas.

lib/services/database_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;

class DatabaseService {
  // Dados: api.superdb.com.br (NÃO auth.superdb.com.br)
  // Path: raiz da tabela, sem prefixo /rest/v1/
  static const String dataUrl = 'https://api.superdb.com.br';

  // Passe SEMPRE o data_plane_token (auth.getDataPlaneToken()).
  // Mandar o access_token aqui devolve 401.
  Future<List<dynamic>> fetchPosts(String dataPlaneToken) async {
    // Correto: api.superdb.com.br/posts (sem /rest/v1/)
    // O tenant sai do próprio token — não precisa de Accept-Profile.
    final response = await http.get(
      Uri.parse('$dataUrl/posts?order=created_at.desc&limit=20'),
      headers: {
        'Authorization': 'Bearer $dataPlaneToken',
        'Content-Type': 'application/json',
      },
    );
    if (response.statusCode == 200) {
      return jsonDecode(response.body) as List<dynamic>;
    }
    throw Exception('Query falhou: ${response.statusCode}');
  }

  Future<void> insertPost(String dataPlaneToken, String title, String body) async {
    final response = await http.post(
      Uri.parse('$dataUrl/posts'),
      headers: {
        'Authorization': 'Bearer $dataPlaneToken',
        'Content-Type': 'application/json',
        'Prefer': 'return=minimal',
      },
      body: jsonEncode({'title': title, 'body': body}),
    );
    if (response.statusCode != 201) {
      throw Exception('Insert falhou: ${response.body}');
    }
  }
}

Quando o SDK Dart estiver disponível

O SDK nativo simplificará esse código consideravelmente — sem headers manuais, com tipagem Dart completa e refresh automático de tokens. Enquanto ele não sai:

Essa página ajudou?