Lär dig hur UI-, sessions- och data-tillstånd rör sig mellan frontend och backend i AI-appar, med praktiska mönster för synkronisering, persistens, caching och säkerhet.

"Tillstånd" är allt din app behöver komma ihåg för att bete sig korrekt från ett ögonblick till nästa.
Om en användare klickar på Skicka i ett chattgränssnitt ska appen inte glömma vad hen skrev, vad assistenten redan svarat, om en förfrågan fortfarande körs eller vilka inställningar (ton, modell, verktyg) är aktiva. Allt det är tillstånd.
Ett användbart sätt att tänka på tillstånd är: den aktuella sanningen i appen — värden som påverkar vad användaren ser och vad systemet gör härnäst. Det inkluderar uppenbara saker som formulärfält, men också "osynliga" fakta som:
Traditionella appar läser ofta data, visar den och sparar uppdateringar. AI-appar lägger till extra steg och mellanliggande output:
Den här extra rörelsen är anledningen till att tillståndshantering ofta är den dolda komplexiteten i AI-applikationer.
I avsnitten nedan delar vi upp tillstånd i praktiska kategorier (UI-tillstånd, sessionstillstånd, persisterade data och modell/runtime-tillstånd) och visar var varje del bör bo (frontend vs backend). Vi tar också upp synkronisering, cache, långkörande jobb, strömmande uppdateringar och säkerhet — eftersom tillstånd bara är användbart om det är korrekt och skyddat.
Föreställ dig en chattapp där en användare ber: "Sammanfatta förra månadens fakturor och markera avvikelser." Backend kan (1) hämta fakturor, (2) köra ett analysverktyg, (3) strömma en sammanfattning tillbaka till UI:t och (4) spara slutrapporten.
För att det ska kännas sömlöst måste appen hålla reda på meddelanden, verktygsresultat, progress och det sparade resultatet — utan att blanda ihop konversationer eller läcka data mellan användare.
När folk säger "tillstånd" i en AI-app blandar de ofta ihop väldigt olika saker. Att dela upp tillstånd i fyra lager — UI, session, data och modell/runtime — gör det lättare att bestämma var något ska leva, vem som kan ändra det och hur det ska lagras.
UI-tillstånd är det levande, ögonblickliga tillståndet i webbläsaren eller mobilappen: textfält, toggle-knappar, valda objekt, vilken flik som är öppen och om en knapp är inaktiverad.
AI-appar lägger till några UI-specifika detaljer:
UI-tillstånd ska vara enkelt att återställa och säkert att förlora. Om användaren uppdaterar sidan kan du tappa det — och det är ofta OK.
Sessionstillstånd binder en användare till en pågående interaktion: användaridentitet, en konversations-ID och en konsekvent vy av meddelandehistoriken.
I AI-appar innehåller detta ofta:
Det här lagret sträcker sig ofta över frontend och backend: frontend håller lätta identifierare, medan backend är auktoriteten för sessionskontinuitet och åtkomstkontroll.
Data-tillstånd är det du avsiktligt lagrar i en databas: projekt, dokument, embeddings, preferenser, revisionsloggar, faktureringshändelser och sparade konversationer.
Till skillnad från UI och session bör data-tillstånd vara:
Modell/runtime-tillstånd är den operativa setup som används för att producera svar: systemprompts, vilka verktyg som är aktiverade, temperatur/max tokens, säkerhetsinställningar, rate limits och tillfälliga cachear.
En del är konfiguration (stabila standarder); annat är flyktigt (kortlivade cachear eller per-förfrågan token-budgetar). Det mesta hör hemma på backend så det kan kontrolleras konsekvent och inte exponeras i onödan.
När dessa lager suddas ut får du klassiska fel: UI visar text som inte sparats, backend använder andra promptinställningar än frontend förväntar sig, eller konversationsminne läcker mellan användare. Klara gränser skapar tydligare sanningskällor — och gör det uppenbart vad som måste bestå, vad som kan återskapas och vad som måste skyddas.
Ett pålitligt sätt att minska buggar i AI-appar är att bestämma, för varje bit tillstånd, var den ska bo: i webbläsaren (frontend), på servern (backend) eller i båda. Detta påverkar pålitlighet, säkerhet och hur "överraskande" appen känns när användare uppdaterar, öppnar en ny flik eller tappar nätverk.
Frontend-tillstånd passar bäst för saker som ändras snabbt och inte behöver överleva en uppdatering. Att hålla det lokalt gör UI:t responsivt och undviker onödiga API-anrop.
Vanliga frontend-exempel:
Om du förlorar detta vid uppdatering är det vanligtvis acceptabelt.
Backend bör hålla allt som måste vara betrott, granskningsbart eller konsekvent upprätthållet. Detta inkluderar tillstånd som andra enheter/flikar behöver se, eller som måste förbli korrekt även om klienten manipuleras.
Vanliga backend-exempel:
Bra tumregel: om felaktigt tillstånd kan kosta pengar, läcka data eller bryta åtkomstkontroll, hör det hemma på backend.
Vissa tillstånd är naturligt delade:
Även när det är delat, välj en "sanningskälla." Typiskt är backend auktoritativ och frontend cachar en kopia för hastighet.
Håll tillstånd nära där det behövs, men persist det som måste överleva uppdateringar, enhetsbyten eller avbrott.
Undvik antipatternen att lagra känsligt eller auktoritativt tillstånd endast i webbläsaren (t.ex. en klient-side isAdmin-flagga, plan-nivå eller jobbstatus som sanning). UI:t kan visa dessa värden, men backend måste verifiera dem.
En AI-funktion känns som "en handling", men är egentligen en kedja av tillståndsövergångar delade mellan webbläsare och server. Att förstå livscykeln gör det enklare att undvika mismatch i UI, saknad kontext och duplicerade kostnader.
En användare klickar på Skicka. UI:t uppdaterar omedelbart lokalt tillstånd: det kan lägga till en "pending" meddelandebubbla, inaktivera skicka-knappen och fånga aktuella inputs (text, bilagor, valda verktyg).
Vid denna punkt bör frontend generera eller bifoga korrelationsidentifierare:
conversation_id: vilken tråd detta hör tillmessage_id: klientens ID för det nya användarmeddelandetrequest_id: unikt per försök (användbart för retries)Dessa IDs låter båda sidor referera till samma händelse även när svar kommer sent eller dubbelt.
Frontenden skickar en API-förfrågan med användarmeddelandet plus IDs. Servern validerar behörigheter, rate limits och payloadens form, och persisterar sedan användarmeddelandet (eller åtminstone en oföränderlig loggpost) nycklad på conversation_id och message_id.
Detta persistenssteg förhindrar "fantomhistorik" om användaren uppdaterar mitt i en förfrågan.
För att anropa modellen bygger servern upp kontext från sin sanningskälla:
conversation_idNyckelidén: lita inte på att klienten skickar full historik. Klienten kan vara föråldrad.
Servern kan kalla verktyg (sök, databasuppslag) före eller under modellgenereringen. Varje verktygsanrop producerar mellanliggande tillstånd som bör spåras mot request_id så att det kan granskas och återköras säkert.
Vid streaming skickar servern partiella tokens/händelser. UI:t uppdaterar successivt det väntande assistentmeddelandet, men ser det fortfarande som "pågående" tills en slutlig händelse markerar slutförande.
Retries, dubbelinskickningar och felordnade svar händer. Använd request_id för att deduplicera på servern och message_id för att försonas i UI:t (ignorera sena chunkar som inte matchar den aktiva förfrågan). Visa alltid ett tydligt "misslyckades"-tillstånd med en säker retry som inte skapar dubbletter.
En session är tråden som binder användarens åtgärder: vilken workspace de är i, vad de senast sökte efter, vilket utkast de redigerade och vilken konversation ett AI-svar ska fortsätta. Bra sessionstillstånd gör att appen känns kontinuerlig över sidor — och helst över enheter — utan att backend blir en dumpningsplats för allt användaren någonsin sagt.
Sikta på: (1) kontinuitet (en användare kan lämna och komma tillbaka), (2) korrekthet (AI:n använder rätt kontext för rätt konversation) och (3) isolering (en session får inte läcka till en annan). Om du stödjer flera enheter, behandla sessioner som användar- plus enhetsskopade: "samma konto" betyder inte alltid "samma öppna arbetsvy".
Du väljer vanligtvis ett av dessa sätt att identifiera sessionen:
HttpOnly, Secure, SameSite) och hantera CSRF."Minne" är bara tillstånd du väljer att skicka tillbaka in i modellen.
Ett praktiskt mönster är summary + window: förutsägbart och hjälper undvika överraskande modellbeteende.
Om AI:n använder verktyg (sök, databasfrågor, filläsningar), lagra varje verktygsanrop med: inputs, tidsstämplar, verktygs-version och returnerat output (eller en referens). Det här låter dig förklara "varför AI:n sa det", replaya körningar för debugging och upptäcka när resultat ändrats på grund av verktygets datasetändring.
Spara inte långtidsminne som standard. Behåll bara vad som behövs för kontinuitet (konversations-ID, sammanfattningar och verktygsspår), sätt retention-gränser och undvik att persistera rå användartext utan klart produktbehov och användarsamtycke.
Tillstånd blir riskabelt när samma "sak" kan redigeras på mer än ett ställe — ditt UI, en andra flik eller en bakgrundsprocess. Lösningen handlar mindre om smart kod och mer om tydligt ägande.
Bestäm vilket system som är auktoritativt för varje bit tillstånd. I de flesta AI-applikationer bör backend äga den kanoniska posten för allt som måste vara korrekt: konversationsinställningar, verktygsbehörigheter, meddelandehistorik, faktureringsgränser och jobbstatus. Frontend kan cachea och härleda tillstånd för hastighet, men bör anta att backend har rätt vid mismatch.
Praktisk regel: om du skulle bli upprörd över att förlora det vid uppdatering, hör det troligen hemma i backend.
Optimistiska uppdateringar får appen att kännas omedelbar: toggla en inställning, uppdatera UI direkt och bekräfta sedan med servern. Det fungerar bra för låg-stakes, återkännbara handlingar (t.ex. markera en konversation som favorit).
Det skapar förvirring när servern kan avvisa eller transformera ändringen (behörighetskontroller, kvotbegränsningar, validering eller serversidiga defaults). I sådana fall visa ett "sparar..."-tillstånd och uppdatera UI först efter bekräftelse.
Konflikter uppstår när två klienter uppdaterar samma post utifrån olika startversioner. Vanligt exempel: Flik A och Flik B ändrar modellens temperatur samtidigt.
Använd lättviktsversionering så backend kan upptäcka föråldrade skrivningar:
updated_at tidsstämplar (enkelt och mänskligt debuggbart)If-Match headers (HTTP-native)Om versionen inte matchar, returnera en konflikt (ofta HTTP 409) och skicka tillbaka det senaste serverobjektet.
Efter varje skrivning, låt API:t returnera det sparade objektet som persisterats (inklusive serverskapade defaults, normaliserade fält och ny version). Detta låter frontend ersätta sin cachade kopia omedelbart — en uppdatering av sanningskällan istället för att gissa vad som ändrats.
Caching är ett av de snabbaste sätten att få en AI-app att kännas omedelbar, men det skapar också en andra kopia av tillstånd. Cachar du fel sak — eller cachar den på fel ställe — får du ett UI som känns snabbt men förvirrande.
Klient-side caches bör fokusera på upplevelsen, inte auktoriteten. Bra kandidater är senaste konversationsförhandsvisningar (titlar, sista meddelandesnitt), UI-preferenser (tema, vald modell, sidofältsstatus) och optimistiska UI-tillstånd (meddelanden som "skickas").
Håll klientcachen liten och förgänglig: om den rensas ska appen fortfarande fungera genom att hämta om från servern.
Servercachar bör fokusera på dyr eller ofta upprepad arbete:
Här kan du också cachea härlett tillstånd som token-räkningar, moderationsbeslut eller dokumentparsningsresultat — allt deterministiskt och dyrt.
Tre praktiska regler:
user_id, modell, verktygsparametrar, dokumentversion).Om du inte kan förklara när en cachepost blir fel, cachea den inte.
Undvik att lägga API-nycklar, auth-tokens, råa prompts med känslig text eller användarspecifikt innehåll i delade lager som CDN-cachar. Om du måste cachea användardata, isolera per användare och kryptera i vila — eller håll det i din primära databas istället.
Caching bör bevisas, inte antas. Mät p95-latens före/efter, cache-hit rate och användar-synliga fel som "meddelande uppdaterat efter rendering." Ett snabbt svar som senare motsäger UI:t är ofta sämre än ett något långsammare, konsekvent svar.
Vissa AI-funktioner blir klara på en sekund. Andra tar minuter: ladda upp och parsa en PDF, embedding och indexering av en knowledge base eller köra ett flerstegs verktygsworkflow. För dessa överlever tillstånd inte bara i skärmen — det måste överleva uppdateringar, retries och tid.
Persist endast det som ger verkligt produktvärde.
Konversationshistorik är uppenbart: meddelanden, tidsstämplar, användaridentitet och ofta vilken modell/verktygsstack som användes. Det möjliggör "återuppta senare", revisionsspår och bättre support.
Användar- och workspace-inställningar bör ligga i databasen: föredragen modell, temperatur-standarder, feature toggles, systemprompts och UI-preferenser som följer användaren över enheter.
Filer och artefakter (uppladdningar, extraherad text, genererade rapporter) lagras ofta i objektlagring med databaspunkter som pekar på dem. Databasen håller metadata (ägare, storlek, content-type, processing state), medan blob-lagret håller bytesserna.
Om en förfrågan inte kan slutföras inom en normal HTTP-timeout, flytta arbetet till en kö.
Ett typiskt mönster:
POST /jobs med inputs (file id, conversation id, parametrar).job_id.Detta håller UI:t responsivt och gör retries säkrare.
Gör jobbstatus explicit och frågbar: queued → running → succeeded/failed (valfritt canceled). Spara dessa övergångar serverside med tidsstämplar och felinformation.
På frontend, återspegla status tydligt:
Queued/running: visa spinner och inaktivera dubbletthandlingar.Failed: visa ett kort felmeddelande och en Retry-knapp.Succeeded: ladda artefakten eller uppdatera konversationen.Exponera GET /jobs/{id} (polling) eller strömuppdateringar (SSE/WebSocket) så UI:t aldrig behöver gissa.
Nätverks-timeouter händer. Om frontenden retry:ar POST /jobs vill du inte ha två identiska jobb (och dubbla kostnader).
Kräv en Idempotency-Key per logisk handling. Backend lagrar nyckeln med resulterande job_id/response och returnerar samma resultat för upprepade förfrågningar.
Långkörande AI-appar samlar snabbt data. Definiera retention-regler tidigt:
Behandla cleanup som en del av tillståndshantering: det minskar risk, kostnad och förvirring.
Streaming gör tillstånd svårare eftersom "svaret" inte längre är ett enda blob. Du arbetar med partiella tokens (text som kommer ord för ord) och ibland partiellt verktygsarbete (en sökning startar och blir klar senare). Det betyder att UI:t och backend måste vara överens om vad som räknas som tillfälligt respektive slutligt tillstånd.
Ett rent mönster är att strömma en sekvens små händelser, var och en med en typ och en payload. Exempel:
token: inkrementell text (eller en liten chunk)tool_start: ett verktygsanrop har börjat (t.ex. "Söker...", med ett id)tool_result: verktygets output är klart (samma id)done: assistentmeddelandet är kompletterror: något gick fel (inkludera ett användarsäkert meddelande och en debug-id)Denna event-ström är lättare att versionera och debugga än rå textströmning, eftersom frontend kan rendera progress exakt (och visa verktygsstatus) utan att gissa.
På klienten behandla streaming som append-only: skapa ett "utkast" till assistentmeddelande och fortsätt lägga till medan token-händelser kommer. När du får done, gör en commit: markera meddelandet som slutligt, persist det (om du lagrar lokalt) och lås upp åtgärder som kopiera, betygsätta eller regenerera.
Detta undviker att skriva om historiken mitt i streamen och håller UI:t förutsägbart.
Streaming ökar risken för halvfärdigt arbete:
Om sidan laddas om mitt i en stream, rekonstruera från senaste stabila tillstånd: de senaste commitade meddelandena plus eventuella lagrade utkastmetadata (message id, ackumulerad text, verktygsstatus). Om du inte kan återuppta streamen, visa utkastet som avbrutet och låt användaren försöka igen i stället för att låtsas att det slutfördes.
Tillstånd är inte bara data du lagrar — det är användarens prompts, uppladdningar, preferenser, genererade output och metadata som binder ihop allt. I AI-appar kan detta vara ovanligt känsligt (personuppgifter, proprietära dokument, interna beslut), så säkerhet måste designas in i varje lager.
Allt som skulle låta en klient imitera din app måste stanna serverside: API-nycklar, privata connectors (Slack/Drive/DB-credentials) och interna systemprompts eller routinglogik. Frontend kan be om en åtgärd ("sammanfatta den här filen"), men backend bör besluta hur det körs och med vilka credentials.
Behandla varje tillståndsmutation som en privilegierad operation. När klienten försöker skapa ett meddelande, byta namn på en konversation eller bifoga en fil, bör backend verifiera:
Detta förhindrar ID-gissningsattacker där någon byter conversation_id och får åtkomst till en annans historik.
Anta att alla klient-levererade tillstånd är otillförlitlig input. Validera schema och begränsningar (typer, längder, tillåtna enums) och sanera för destinationen (SQL/NoSQL, loggar, HTML-rendering). Om du accepterar "tillståndsuppdateringar" (t.ex. inställningar, verktygsparametrar), whitelista tillåtna fält i stället för att slå samman godtycklig JSON.
För åtgärder som ändrar beständigt tillstånd — dela, exportera, radera, koppla connectors — spela in vem som gjorde vad och när. En lättvikts audit-logg hjälper vid incidenthantering, support och compliance.
Spara bara det du behöver för funktionen. Om du inte behöver fulla prompts för evigt, överväg retention-window eller redigering. Kryptera känsligt tillstånd i vila när det är lämpligt (tokens, connector-credentials, uppladdade dokument) och använd TLS i transit. Separera operationell metadata från content så du kan begränsa åtkomst strängare.
En användbar standard för AI-appar är enkel: backend är sanningskällan och frontend är en snabb, optimistisk cache. UI:t kan kännas omedelbart, men allt du skulle bli ledsen över att förlora (meddelanden, jobbstatus, verktygsresultat, faktureringshändelser) bör bekräftas och sparas serverside.
Om du bygger med en snabb iterations-workflow — där stora delar av produktytan genereras snabbt — blir tillståndsmodellen ännu viktigare. Plattformar som Koder.ai kan hjälpa team att leverera fulla webb-, backend- och mobilappar från chatt, men samma regel gäller: snabb iteration är säkrast när sanningskällor, IDs och statusövergångar designas i förväg.
Frontend (browser/mobile)
session_id, conversation_id och en ny request_id.Backend (API + workers)
Notera: ett praktiskt sätt att hålla detta konsekvent är att standardisera backend-stack tidigt. Till exempel använder Koder.ai-genererade backends ofta Go med PostgreSQL (och React i frontend), vilket gör det enkelt att centralisera "auktoritativt" tillstånd i SQL medan klientcachen är förgänglig.
Innan du bygger skärmar, definiera fälten du kommer lita på i varje lager:
IDs och ägarskap: user_id, org_id, conversation_id, message_id, request_id.Tidsstämplar och ordning: created_at, updated_at och en explicit sequence för meddelanden.Statusfält: queued | running | streaming | succeeded | failed | canceled (för jobb och verktygsanrop).Versionering: etag eller version för konflikt-säkra uppdateringar.Detta förhindrar klassiska buggar där UI:t "ser rätt ut" men inte kan försonas vid retries, uppdateringar eller samtidiga redigeringar.
Håll endpoints förutsägbara över funktioner:
GET /conversations (lista)GET /conversations/{id} (hämta)POST /conversations (skapa)POST /conversations/{id}/messages (lägg till)PATCH /jobs/{id} (uppdatera status)GET /streams/{request_id} eller POST .../stream (stream)Returnera samma envelopestil överallt (inklusive fel) så frontend kan uppdatera tillstånd enhetligt.
Logga och returnera ett request_id för varje AI-anrop. Spela in verktygsanrops inputs/outputs (med redigering), latens, retries och slutlig status. Gör det enkelt att svara på: "Vad såg modellen, vilka verktyg kördes och vilket tillstånd persisterade vi?"
request_id (och/eller en Idempotency-Key).queued till succeeded).version/etag eller serverside merge-regler.När du antar snabbare byggcykler (inklusive AI-assisterad generation), överväg att lägga in guardrails som automatiskt uppfyller dessa checklist-punkter — schema-validering, idempotens och eventad streaming — så att "snabbt" inte blir till tillståndsdrift. I praktiken är det här en end-to-end-plattform som Koder.ai kan vara användbar: den snabbar upp leverans samtidigt som du kan exportera koden och behålla konsekventa tillståndsmönster över web, backend och mobil.