
Bestäm tidsluckor och resurser först. Beställ sedan en regel i databasen som bara tillåter en aktiv bokning per resurs och tid, och läs SQL:en innan du godkänner den. Prova till sist med två användare och med två identiska rader i SQL-editorn: den andra ska alltid nekas.
Steg 1: bestäm vad som får bokas
Skriv ned tre saker innan du öppnar chatten. Resurs: det som bokas, till exempel en frisörstol, ett mötesrum eller en konsult. Tidsluckor: fasta luckor, till exempel 30 minuter mellan 09.00 och 17.00 på vardagar, eller valfri längd. Tillstånd: en bokning är antingen bokad eller avbokad. Exemplet i guiden använder fasta luckor på 30 minuter; byt till dina egna värden.
Bestäm också facit i förväg. Två användare får aldrig ha en aktiv bokning på samma resurs och starttid. En avbokad tid ska bli ledig igen. Bekräftelsen visas först när databasen har sparat bokningen. Vill du veta var appens data ligger, börja med guiden om data och inloggning.
Valet mellan fasta luckor och valfri längd styr regeln i nästa steg. Fasta luckor räcker för de flesta frisörer, kurser och provningar och ger den enklaste regeln. Valfri längd behövs när en bokning kan vara 45 minuter och nästa 90.
Steg 2: beställ tabellen och spärren i databasen
Den vanliga fällan är att appen visar bara lediga tider och kontrollerar att tiden är ledig strax före sparningen. Båda användarna kan läsa ”ledig” innan någon av dem har sparat. Spärren måste därför ligga i databasen, där varje ny rad prövas mot regeln. PostgreSQL-dokumentationen varnar dessutom för att CHECK-villkor som läser andra rader kan se ut att fungera i enkla prov utan att garantera regeln, och pekar på UNIQUE och EXCLUDE för den här sortens krav. Bakgrunden för utvecklare finns i artikeln om samtidighetsbuggar i AI-kod.
För fasta luckor kan du skriva så här i chatten:
Skapa en tabell bokningar med id som primärnyckel, resurs_id, starttid (timestamptz),
användare, status ('bokad' eller 'avbokad') och skapad.
resurs_id, starttid och status får inte vara tomma.
Databasen ska avvisa andra statusvärden än 'bokad' och 'avbokad'.
Använd bara fördefinierade, icke överlappande 30-minutersluckor per resurs.
Databasen ska avvisa starttider som inte hör till dessa luckor,
även om någon anropar API:t direkt.
Lägg till ett unikt index i databasen så att det bara kan finnas
en rad med status 'bokad' per resurs_id och starttid.
Spärren ska ligga i databasen, inte bara i gränssnittet.
Visa SQL:en innan du kör den.Kräv att fälten inte får vara tomma. Enligt PostgreSQL räknas två tomma värden som olika, så en rad utan starttid skulle annars slinka förbi spärren. Ordet ”status 'bokad'” gör spärren partiell: avbokade rader får ligga kvar som historik utan att låsa tiden.
Lovable sparar ändringen som en migrering under drizzle/migrations/; äldre projekt kan ha supabase/migrations/. Inställningen Execute backward incompatible database migrations → Ask each time gäller ändringar som kan bryta bakåtkompatibiliteten. Den lovar inte en godkännandefråga för varje migrering. Be därför uttryckligen om SQL:en före körning och läs migreringsfilen.
Steg 3: läs SQL:en innan du godkänner
Du behöver inte kunna SQL för att känna igen rätt mönster. För fasta luckor ska förslaget innehålla något som liknar det här, med dina namn:
create unique index bokningar_en_aktiv_per_tid
on bokningar (resurs_id, starttid)
where status = 'bokad';
Indexet hindrar bara samma starttid. Det räcker för fasta luckor om databasen också begränsar starttiderna till det bestämda rutnätet och varje bokning upptar exakt en lucka. Annars kan 10.00 och 10.15 godkännas trots att två halvtimmar överlappar. Be Lovable visa även databasregeln som kopplar bokningen till en giltig lucka, till exempel en främmande nyckel till en tabell med tillåtna tider.
Har du valt valfri längd räcker inte en unik starttid, eftersom 10.00–11.00 och 10.30–11.30 har olika starttider men ändå krockar. Då behövs en exclusion constraint som stoppar överlappande intervall på samma resurs. Den kräver tillägget btree_gist. Sedan 13 oktober 2026 nekar Lovable tillägg utanför en fast lista, enligt Lovables ändringslogg. När guiden kontrollerades 16 oktober 2026 fanns btree_gist på listan i Lovables databasdokumentation. Kontrollera listan igen innan du beställer.
create extension if not exists btree_gist;
alter table bokningar
add constraint bokningar_ingen_overlappning
exclude using gist (resurs_id with =, tid with &&)
where (status = 'bokad');
Här ska tid vara ett tstzrange-intervall i stället för starttid. Be Lovable skapa hela tabelländringen: resurs och intervall får inte vara tomma, och intervallet ska ha ändlig start och sluttid, positiv längd och gränserna [). Då räknas starten in och slutet inte, så 10.00–10.30 och 10.30–11.00 krockar inte. SQL-blocket visar själva överlappsspärren, inte hela tabellens definition. Saknas ordet unique eller exclude i förslaget, eller ligger kontrollen bara i appens kod, är beställningen inte uppfylld. Be om en ny migrering i stället för att godkänna.
En fallgrop: finns det redan dubbletter i tabellen går spärren inte att lägga till. Ta bort eller avboka testdubbletterna först, och spara aldrig tider som lokal klocktid utan tidszon. Lagra timestamptz och visa tiderna i svensk tid, annars kan sommartidsskiftet flytta luckor en timme.
Steg 4: lediga tider, bokning, avbokning och bekräftelse
När spärren finns beställer du resten av flödet i en prompt per del. Lediga tider: visa öppettidernas luckor minus rader med status bokad. Bokning: försök spara direkt och låt databasen avgöra. Bekräftelse: visa ”Bokad” först när sparningen lyckats. Avbokning: sätt status till avbokad i stället för att radera raden, så att tiden blir ledig men historiken finns kvar.
Be också om ett begripligt fel när spärren slår till. PostgreSQL ger felkoden 23505 för ett unikt index och 23P01 för en exclusion constraint. Beställ: ”Om sparningen ger 23505 eller 23P01, visa ’Tiden hann bli bokad av någon annan. Välj en ny tid.’ och läs in lediga tider på nytt.” Ett generiskt felmeddelande gör att användaren tror att appen är trasig.
Bokningar innehåller ofta namn och telefonnummer. Kontrollera att en kund bara kan läsa och avboka sina egna bokningar, med samma tvåkontoprov som i guiden om behörigheter i Lovable. Spärren mot dubbelbokning gäller vilka rader som får finnas, inte vem som får se dem.
Steg 5: gör tvåanvändarprovet
Lovables dokumentation anger att utkast använder samma databas som projektet och att direkta ändringar gäller samma databas som den publicerade appen. Skapa därför en egen testresurs, till exempel ”Testrum”, och ta bort dina testbokningar efteråt.
Prov A, två användare: logga in som användare A i ett vanligt fönster och som B i ett privat fönster. Öppna samma lediga tid för Testrum i båda och klicka Boka så nära samtidigt som du kan. Facit: en av er får ”Bokad”, den andra får meddelandet om att tiden hann bli bokad. Ladda om båda fönstren; tiden ska visas som upptagen för båda.
Två klick blir inte alltid samtidiga i databasen, så prov A ensamt bevisar inte spärren. Prov B, direkt i databasen: öppna More → Cloud och SQL-editorn. Be Lovable skriva två insert-satser med olika boknings-id men samma resurs och tid för en annan ledig testlucka. Båda ska ha status bokad. Kör dem en i taget. Facit: den första lyckas och den andra nekas av just bokningsspärren, inte av primärnyckeln eller något annat villkor. Prova också att spara en starttid utanför de fasta luckorna; den ska nekas. Har du valt valfri längd provar du i stället två överlappande intervall med olika starttider; det andra ska nekas av exclusion-villkoret.
Prov C, avbokning: avboka den bokning som faktiskt lyckades i prov A och låt den andra användaren boka tiden. Facit: den andra användaren lyckas, oavsett om A eller B vann första försöket. För fasta luckor kör du sist den här kontrollen, som ska ge noll rader:
select resurs_id, starttid, count(*)
from bokningar
where status = 'bokad'
group by resurs_id, starttid
having count(*) > 1;
För valfri längd med kolumnen tid använder du i stället denna kontroll av överlappande aktiva bokningar:
select a.id, b.id
from bokningar a
join bokningar b on a.resurs_id = b.resurs_id
and a.id < b.id and a.tid && b.tid
where a.status = 'bokad' and b.status = 'bokad';
Skriv ned datum, vilken spärr du såg i SQL:en och utfallet av prov A, B och C. Om två krockande aktiva bokningar gick att spara ber du Lovable rätta databasregeln för ditt val av tidsluckor och visa SQL:en. Kontrollera att felet inte bara döljs i gränssnittet. Rensningen med DELETE eller UPDATE får en bekräftelsefråga i SQL-editorn; läs den innan du godkänner.
Du kan visa en granskad spärr i databasen, ett flöde där avbokade tider blir lediga, ett tydligt meddelande när tiden hinner tas, och daterade anteckningar där prov B nekade den andra identiska bokningen och dubblettkontrollen gav noll rader.
Produktuppgifterna kontrollerades 16 oktober 2026 mot Lovable Cloud, Lovables databasdokumentation och Lovables ändringslogg (posten 13 okt 2026). Databasreglerna bygger på PostgreSQL-dokumentationen om villkor, partiella index, intervalltyper, btree_gist och felkoder. Proven är dina; redaktionen har inte kört dem.