SDK JavaScript / TypeScript
Exemplos com fetch nativo e interfaces TypeScript.
Interfaces TypeScript
// types.ts
export type DeliveryStatus =
| "pending"
| "assigned"
| "in_progress"
| "completed"
| "cancelled";
export type PixKeyType = "cpf" | "phone" | "email" | "random";
export interface Delivery {
id: string;
title: string;
status: DeliveryStatus;
origin_address: string;
origin_lat: number;
origin_lng: number;
destination_address: string;
destination_lat: number;
destination_lng: number;
distance_km: number | null;
driver_earnings: string | null;
driver_id: string | null;
admin_id: string;
created_at: string;
updated_at: string;
assigned_at: string | null;
started_at: string | null;
completed_at: string | null;
cancelled_at: string | null;
}
export interface CreateDeliveryInput {
title: string;
origin_address: string;
origin_lat: number;
origin_lng: number;
destination_address: string;
destination_lat: number;
destination_lng: number;
}
export interface Driver {
id: string;
full_name: string;
email: string;
phone: string | null;
role: "driver";
asaas_account_id: string | null;
pix_key: string | null;
pix_key_type: PixKeyType | null;
is_active: boolean;
created_at: string;
updated_at: string;
}
export interface Wallet {
id: string;
driver_id: string;
balance: string;
updated_at: string;
}
export interface Transaction {
id: string;
wallet_id: string;
delivery_id: string | null;
type: "credit" | "debit";
amount: string;
description: string;
created_at: string;
}
export interface Withdrawal {
id: string;
wallet_id: string;
amount: string;
pix_key: string;
pix_key_type: PixKeyType;
status: "pending" | "completed" | "failed";
asaas_transfer_id: string | null;
failure_reason: string | null;
created_at: string;
completed_at: string | null;
}
export interface Pricing {
id: string;
base_fare: string;
min_km: number;
price_per_km: string;
updated_at: string;
}
Classe FastDelivClient (TypeScript)
// client.ts
import type {
Delivery,
CreateDeliveryInput,
Driver,
Wallet,
Transaction,
Withdrawal,
Pricing,
} from "./types";
const SUPABASE_URL = process.env.SUPABASE_URL!;
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY!;
const BASE_URL =
process.env.FAST_DELIV_URL ?? "https://fast-deliv-backend.vercel.app";
interface AuthData {
access_token: string;
refresh_token: string;
expires_in: number;
}
export class FastDelivClient {
private accessToken: string | null = null;
private refreshToken: string | null = null;
private expiresAt = 0;
constructor(
private email: string,
private password: string
) {}
// --- Auth ---
private async authenticate(): Promise<void> {
const resp = await fetch(
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
{
method: "POST",
headers: { apikey: SUPABASE_ANON_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ email: this.email, password: this.password }),
}
);
if (!resp.ok) throw new Error(`Auth failed: ${resp.status}`);
const data: AuthData = await resp.json();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token;
this.expiresAt = Date.now() + (data.expires_in - 60) * 1000;
}
private async refresh(): Promise<void> {
const resp = await fetch(
`${SUPABASE_URL}/auth/v1/token?grant_type=refresh_token`,
{
method: "POST",
headers: { apikey: SUPABASE_ANON_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token: this.refreshToken }),
}
);
if (!resp.ok) throw new Error(`Refresh failed: ${resp.status}`);
const data: AuthData = await resp.json();
this.accessToken = data.access_token;
this.expiresAt = Date.now() + (data.expires_in - 60) * 1000;
}
private async getHeaders(): Promise<Record<string, string>> {
if (!this.accessToken) await this.authenticate();
else if (Date.now() >= this.expiresAt) await this.refresh();
return { Authorization: `Bearer ${this.accessToken}` };
}
private async request<T>(
method: string,
path: string,
body?: unknown
): Promise<T> {
const headers = await this.getHeaders();
if (body) headers["Content-Type"] = "application/json";
const resp = await fetch(`${BASE_URL}${path}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
if (!resp.ok) {
const err = await resp.json().catch(() => ({ detail: resp.statusText }));
throw new Error(`HTTP ${resp.status}: ${err.detail ?? resp.statusText}`);
}
return resp.json() as T;
}
// --- Entregas ---
async listDeliveries(status?: string): Promise<Delivery[]> {
const qs = status ? `?status=${status}` : "";
return this.request<Delivery[]>("GET", `/api/v1/deliveries${qs}`);
}
async createDelivery(data: CreateDeliveryInput): Promise<Delivery> {
return this.request<Delivery>("POST", "/api/v1/deliveries", data);
}
async assignDelivery(id: string): Promise<Delivery> {
return this.request<Delivery>("PATCH", `/api/v1/deliveries/${id}/assign`);
}
async startDelivery(id: string): Promise<Delivery> {
return this.request<Delivery>("PATCH", `/api/v1/deliveries/${id}/start`);
}
async completeDelivery(id: string): Promise<Delivery> {
return this.request<Delivery>("PATCH", `/api/v1/deliveries/${id}/complete`);
}
async cancelDelivery(id: string): Promise<Delivery> {
return this.request<Delivery>("PATCH", `/api/v1/deliveries/${id}/cancel`);
}
// --- Motoristas ---
async listDrivers(): Promise<Driver[]> {
return this.request<Driver[]>("GET", "/api/v1/drivers");
}
// --- Carteira ---
async getWallet(): Promise<Wallet> {
return this.request<Wallet>("GET", "/api/v1/wallets/me");
}
async getTransactions(): Promise<Transaction[]> {
return this.request<Transaction[]>("GET", "/api/v1/wallets/me/transactions");
}
async withdraw(
amount: string,
pixKey: string,
pixKeyType: string
): Promise<Withdrawal> {
return this.request<Withdrawal>("POST", "/api/v1/wallets/withdraw", {
amount,
pix_key: pixKey,
pix_key_type: pixKeyType,
});
}
// --- Precos ---
async getPricing(): Promise<Pricing> {
return this.request<Pricing>("GET", "/api/v1/pricing");
}
async updatePricing(data: Partial<Omit<Pricing, "id" | "updated_at">>): Promise<Pricing> {
return this.request<Pricing>("PUT", "/api/v1/pricing", data);
}
}
Uso
const client = new FastDelivClient("admin@empresa.com", "senha123");
const deliveries = await client.listDeliveries("pending");
console.log(`${deliveries.length} entregas pendentes`);
const delivery = await client.createDelivery({
title: "Entrega Setor Sul",
origin_address: "Rua das Flores, 100, Goiania",
origin_lat: -16.6869,
origin_lng: -49.2648,
destination_address: "Av. T-63, 800, Goiania",
destination_lat: -16.7012,
destination_lng: -49.2789,
});
console.log(`Criada: ${delivery.id}`);
Supabase Realtime — localizacao em tempo real
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
);
interface DriverLocationUpdate {
driver_id: string;
lat: number;
lng: number;
heading: number | null;
speed_kmh: number | null;
updated_at: string;
}
function subscribeToDriverLocations(
onUpdate: (location: DriverLocationUpdate) => void
) {
return supabase
.channel("driver_locations")
.on(
"postgres_changes",
{ event: "UPDATE", schema: "public", table: "driver_locations" },
(payload) => {
onUpdate(payload.new as DriverLocationUpdate);
}
)
.subscribe();
}
// Usar com um mapa MapLibre, Leaflet, etc.
const channel = subscribeToDriverLocations((loc) => {
console.log(`Driver ${loc.driver_id}: ${loc.lat}, ${loc.lng}`);
// updateMarkerOnMap(loc.driver_id, loc.lat, loc.lng);
});
// Ao desmontar o componente
// await supabase.removeChannel(channel);