API Reference
CNC-Pilot używa Supabase Client API do wszystkich operacji na danych.
Uwierzytelnianie
Supabase Client
Dwa klienty zależnie od kontekstu:
Server Components & API Routes:
import { createClient } from '@/lib/supabase-server'
// Async - używa cookies
const supabase = await createClient()
Client Components:
import { supabase } from '@/lib/supabase'
// Sync - używa localStorage
const { data } = await supabase.from('orders').select('*')
Orders API
Pobranie zamówień
Lista wszystkich zamówień firmy:
const { data: orders, error } = await supabase
.from('orders')
.select(`
*,
creator:users!created_by(id, full_name)
`)
.eq('company_id', user.company_id)
.order('created_at', { ascending: false })
Pojedyncze zamówienie:
const { data: order } = await supabase
.from('orders')
.select('*')
.eq('id', orderId)
.eq('company_id', user.company_id) // Security!
.single()
Filtrowanie po statusie:
.eq('status', 'in_progress')
Zakres dat:
.gte('deadline', '2025-01-01')
.lte('deadline', '2025-12-31')
Utworzenie zamówienia
const { data, error } = await supabase
.from('orders')
.insert({
order_number: 'ZAM-2025-001',
customer_name: 'Firma XYZ',
part_name: 'Wałek 50x200',
quantity: 10,
material: 'Stal S235',
deadline: '2025-12-31',
status: 'pending',
company_id: user.company_id, // REQUIRED!
created_by: user.id,
})
.select()
.single()
Aktualizacja zamówienia
const { error } = await supabase
.from('orders')
.update({
status: 'in_progress',
notes: 'Rozpoczęto produkcję',
})
.eq('id', orderId)
.eq('company_id', user.company_id) // Security!
Usunięcie zamówienia
const { error } = await supabase
.from('orders')
.delete()
.eq('id', orderId)
.eq('company_id', user.company_id) // Security!
Inventory API
Lista pozycji magazynowych
const { data: items } = await supabase
.from('inventory')
.select('*')
.eq('company_id', user.company_id)
.order('name')
Niski stan (low stock):
const { data: lowStock } = await supabase
.from('inventory')
.select('*')
.eq('company_id', user.company_id)
.filter('quantity', 'lte', supabase.raw('low_stock_threshold'))
Dodanie pozycji
const { data } = await supabase
.from('inventory')
.insert({
sku: 'STAL-S235-50',
name: 'Stal S235 pręt 50mm',
category: 'stal',
quantity: 100,
unit: 'kg',
low_stock_threshold: 20,
location: 'A-12',
company_id: user.company_id, // REQUIRED!
created_by: user.id,
})
Aktualizacja stanu
// Zwiększ (przyjęcie PZ)
const { error } = await supabase.rpc('adjust_inventory', {
item_id: itemId,
adjustment: 50, // +50 kg
})
// Zmniejsz (wydanie WZ)
const { error } = await supabase.rpc('adjust_inventory', {
item_id: itemId,
adjustment: -25, // -25 kg
})
Time Logs API
Aktywne timery
const { data: activeTimers } = await supabase
.from('time_logs')
.select(`
*,
order:orders(id, order_number, customer_name),
user:users(id, full_name)
`)
.eq('company_id', user.company_id)
.eq('status', 'running')
Start timera
const { data: timer } = await supabase
.from('time_logs')
.insert({
order_id: orderId,
user_id: user.id,
company_id: user.company_id, // REQUIRED!
start_time: new Date().toISOString(),
status: 'running',
hourly_rate: user.hourly_rate, // Snapshot
})
.select()
.single()
// Automatycznie zmień status zamówienia
await supabase
.from('orders')
.update({ status: 'in_progress' })
.eq('id', orderId)
.eq('company_id', user.company_id)
Stop timera
const { error } = await supabase
.from('time_logs')
.update({
end_time: new Date().toISOString(),
status: 'completed',
})
.eq('id', timerId)
.eq('user_id', user.id) // Tylko swój timer
Raport czasu
const { data: totalTime } = await supabase
.rpc('calculate_time_worked', {
start_date: '2025-01-01',
end_date: '2025-01-31',
worker_id: user.id,
})
Users API
Lista użytkowników firmy
const { data: users } = await supabase
.from('users')
.select('id, full_name, email, role')
.eq('company_id', user.company_id)
.order('full_name')
Aktualizacja roli
Wymaga uprawnień Admin/Owner
const { error } = await supabase
.from('users')
.update({ role: 'operator' })
.eq('id', userId)
.eq('company_id', user.company_id) // Security!
Dashboard Queries
Metryki
import { getDashboardSummary } from '@/lib/dashboard-queries'
const metrics = await getDashboardSummary(user.company_id)
// Returns:
// {
// totalOrders: 45,
// activeOrders: 12,
// completedThisWeek: 8,
// overdueOrders: 3,
// activeTimers: 2,
// lowStockItems: 5,
// }
Równoległe zapytania
const [orders, inventory, timers] = await Promise.all([
supabase.from('orders').select('*').eq('company_id', companyId),
supabase.from('inventory').select('*').eq('company_id', companyId),
supabase.from('time_logs').select('*').eq('company_id', companyId),
])
Real-time Subscriptions
Subscribe do zmian
const channel = supabase
.channel('orders-changes')
.on(
'postgres_changes',
{
event: '*', // INSERT, UPDATE, DELETE
schema: 'public',
table: 'orders',
filter: `company_id=eq.${user.company_id}`,
},
(payload) => {
console.log('Order changed:', payload)
// Odśwież listę
router.refresh()
}
)
.subscribe()
// Cleanup
return () => {
supabase.removeChannel(channel)
}
Error Handling
Pattern
const { data, error } = await supabase
.from('orders')
.select('*')
if (error) {
console.error('Database error:', error)
toast.error('Nie udało się pobrać zamówień')
return
}
// Use data
Powszechne błędy
- Row Level Security violation - Brak company_id w query
- Foreign key constraint - Próba usunięcia rekordu z powiązaniami
- Unique constraint - Duplikat (np. order_number już istnieje)
- Permission denied - Brak uprawnień (rola)
Rate Limiting
Supabase Free Tier limity:
- 500 MB database
- 5 GB bandwidth/miesiąc
- 50 MB file storage
- 500,000 Edge Function invocations/miesiąc
Pro Tier ($25/miesiąc):
- 8 GB database
- 250 GB bandwidth
- 100 GB storage
- 2M Edge Functions
Best Practices
1. Select tylko potrzebne kolumny
// ❌ Pobiera wszystko (wolne)
.select('*')
// ✅ Tylko potrzebne (szybkie)
.select('id, order_number, customer_name, status')
2. Używaj joins zamiast N+1
// ❌ N+1 queries
for (const order of orders) {
const user = await supabase.from('users').select('*').eq('id', order.created_by).single()
}
// ✅ 1 query z join
.select(`
*,
creator:users!created_by(full_name)
`)
3. Indeksuj często wyszukiwane kolumny
Zobacz Database Schema - sekcja Indeksy.
Następne kroki
- Database Schema - Pełny schemat tabel
- Authentication - Jak działa auth
- Multi-Tenancy - Separacja danych firm