Behöver ert företag hjälp med att implementera AI? Kontakta oss och få prisoffert här →
AI Skolan
januari 22, 2026

PostgreSQL-anmälningar, live-vinnarsida i webbläsaren

Rickard Andersson Partner, Nodenordic.se

Eventanmälningar ser enkla ut tills du står fem minuter från att gå upp på сцен och inser att listan är full av dubbletter, stavfel och paniken “vilket kalkylark är det som gäller?”.

Det här drabbar eventarrangörer hårdast, men marknadsförare som kör lead capture och community managers som hanterar registreringar känner av det också. Med den här Postgres-giveaway-automationen hamnar varje anmälan i en pålitlig databas, och din vinnarsida är redo att visas på projektorn.

Du får se hur flödet samlar in registreringar, avduplicerar dem med en upsert och sedan serverar en korrekt formaterad, webbläsarbaserad giveawaysida som maskerar personuppgifter för en rättvis dragning.

Så fungerar automationen

Hela n8n-flödet, från trigger till slutresultat:

n8n Workflow Template: PostgreSQL-anmälningar, live-vinnarsida i webbläsaren

Problemet: anmälningar till liveevent blir röriga snabbt

Registreringar är ofta utspridda över flera verktyg. Ett formulär här, ett kalkylark där, kanske ett DM i sista minuten med “lägg till min kompis också”. Sedan ska du köra en giveaway och det blir ett manuellt städjobb. Namn dubbleras för att någon registrerat sig två gånger. WhatsApp-nummer blir inkonsekventa eftersom folk formaterar dem olika. Och när det är dags att välja en vinnare så visar du antingen personuppgifter på projektorn, eller så stressar du med att maska dem för hand. Det är exakt där “enkel insamling av anmälningar” slutar vara enkel.

Friktionen växer snabbt. Här är var det faller isär.

  • Du lägger lätt runt en timme på att exportera, sortera och avduplicera precis innan eventet drar igång.
  • Dubbletter smyger sig in i dragningen, vilket kan skapa obekväma invändningar om att “det här är inte rättvist”.
  • Att projicera en rå deltagarlista riskerar att exponera e-post, WhatsApp-nummer eller andra personuppgifter.
  • När listan ligger i kalkylark gör folk “snabba fixar” som du inte kan granska i efterhand.

Lösningen: en avduplicerad databas + en live giveawaysida

Det här flödet ger dig två saker som alltid borde hänga ihop: en pålitlig registreringspipeline och en giveawaysupplevelse som är redo för scenen. Det startar när någon skickar in ditt anmälningsformulär i n8n (fält som fullständigt namn, e-post, WhatsApp, Discord-användarnamn och vad du nu samlar in). n8n mappar dessa råa inputvärden till en korrekt formaterad, konsekvent struktur och sparar dem sedan i PostgreSQL med en upsert så att dubbletter inte byggs upp. Separat serverar en publik “giveaway”-endpoint en färdig HTML-sida som du öppnar i webbläsaren. När du laddar den hämtar flödet de senaste deltagarposterna från PostgreSQL, maskerar känsliga uppgifter (som WhatsApp-nummer), kodar ID:n för integritet och renderar en enkel single-page-app som du kan projicera under eventet.

Flödet börjar med antingen en inskickad registrering eller en begäran om giveawaysidan. PostgreSQL blir källan till sanningen i mitten, eftersom den lagrar en enda avduplicerad post per person. Till sist svarar n8n med antingen en bekräftelseskärm (för deltagare) eller hela giveawaysidan (för arrangörer).

Det du får: automation vs. resultat

Exempel: så här ser det ut i praktiken

Säg att du driver en månatlig meetup med cirka 200 anmälningar. Tidigare är det vanligt att lägga 30 sekunder per rad på snabba kontroller (dubbletter, formatering, ta bort personfält för projektorn). Det blir ungefär 100 minuter pilligt arbete, och det händer oftast precis när du har som mest att göra. Med det här flödet är registreringen automatisk, avdupliceringen sker i samband med upserten och giveawaysidan är bara att öppna i webbläsaren. Din “förberedelse” blir att öppna en URL och klicka på en knapp.

Det här behöver du

  • n8n-instans (prova n8n Cloud gratis)
  • Alternativ för egen drift om du föredrar det (Hostinger fungerar bra)
  • PostgreSQL för att lagra och avduplicera registreringar
  • Ett publikt eventformulär för att samla in deltagaruppgifter
  • Databasuppgifter (hämtas från din Postgres-host)

Kunskapsnivå: medel. Du kopplar Postgres-uppgifter och kan behöva justera en SQL-fråga eller fältmappning.

Vill du inte sätta upp detta själv? Prata med en automationsexpert (gratis 15-minuters konsultation).

Så fungerar det

Ett inskick av registreringen triggar flödet. När någon fyller i ditt deltagarformulär tar n8n emot inskicket direkt och plockar nyckelfälten (namn, e-post, WhatsApp, Discord-användarnamn och eventuella extra fält).

De råa fälten struktureras och standardiseras. Flödet mappar input till en konsekvent struktur så att databasen inte får tio varianter av samma telefonnummer, eller oavsiktliga mellanslag som skapar “falska dubbletter”.

PostgreSQL blir källan till sanningen. En upsert skriver deltagarposten till Postgres och uppdaterar en befintlig post om den unika identifieraren redan finns (vanligen e-post eller WhatsApp). Det är avdupliceringsmekanismen, och den jobbar tyst i bakgrunden.

En separat webhook serverar giveawaysidan vid begäran. När du öppnar giveaway-URL:en frågar n8n Postgres efter senaste deltagarlistan, maskerar känsliga uppgifter, kodar ID:n för integritet och svarar med en HTML-sida som du kan projicera och använda live.

Du kan enkelt ändra vilket unikt ID som används för avduplicering så att det matchar din process. Se hela implementationsguiden nedan för alternativ för anpassning.

Steg-för-steg-guide för implementering

Steg 1: Konfigurera webhook-triggern

Sätt upp endpointen för giveaways landningssida som serverar HTML-väljargränssnittet.

  1. Lägg till och öppna Giveaway Webhook Trigger.
  2. Ställ in Pathgiveaway-app.
  3. Ställ in Response ModeresponseNode så att en nedströms svarsnod kan rendera HTML:en.
  4. Koppla Giveaway Webhook Trigger till Retrieve Participant Records.

Tips: Kopiera webhook-URL:en för produktion från Giveaway Webhook Trigger efter aktivering för att dela giveawaysidan med värdar.

Steg 2: Koppla formulär-triggern

Konfigurera anmälningsformuläret för deltagare som matar databasen.

  1. Lägg till och öppna Registration Form Trigger.
  2. Ställ in Form Titlen8n Indonesia Community Meetup #2.
  3. I Options, bekräfta att Path är giveaway-form, Button Label är Submit och att Ignore Bots är aktiverat.
  4. Säkerställ att det dolda fältet Event använder =Meetup #2 och att obligatoriska fält är aktiverade för Nama Lengkap, Domisili (Kota), Bio (pekerjaan, specialty, dsb) och Whatsapp.
  5. Koppla Registration Form Trigger till Map Form Fields.

⚠️ Vanlig fallgrop: Om det dolda fältet Event saknas eller ändras kan giveaway-listan bli tom, eftersom filtreringen bygger på Meetup #2.

Steg 3: Sätt upp fältmappning och upsert till databasen

Normalisera formulärfält och skriv eller uppdatera deltagarposter i Postgres.

  1. Öppna Map Form Fields och bekräfta tilldelningar som nama_lengkap{{ $json['Nama Lengkap'] }} och id{{ $json.Event }}--{{ $json.Whatsapp }}.
  2. Verifiera ytterligare mappningar som email{{ $json.Email }} och created_at{{ $json.submittedAt }}.
  3. Öppna Upsert Participant Record och ställ in Operationupsert.
  4. Ställ in Schemapublic och Tablen8n_meetup_participants.
  5. Bekräfta att Matching Columns inkluderar id och att mappningsläget är autoMapInputData.
  6. Inloggningsuppgifter krävs: Koppla era postgres-inloggningsuppgifter i Upsert Participant Record.

Tips: Det sammansatta id:t (Event--Whatsapp) förhindrar att dubbla registreringar skriver över orelaterade event.

Steg 4: Bygg svaret för giveawaysidan

Hämta deltagare, bygg en sanerad namnlista och rendera HTML-gränssnittet för giveaway.

  1. Öppna Retrieve Participant Records och ställ in Operationselect och Return Alltrue.
  2. Ställ in Schemapublic, Tablen8n_meetup_participants och lägg till ett Where-filter där event är lika med Meetup #2.
  3. Inloggningsuppgifter krävs: Koppla era postgres-inloggningsuppgifter i Retrieve Participant Records.
  4. Öppna Assemble Attendee List och behåll JavaScript-koden som filtrerar på nama_lengkap och maskerar whatsapp innan den returnerar {names: newList}.
  5. Öppna Render Giveaway Page och ställ in Respond Withtext.
  6. Ställ in Response Body till den angivna HTML:en och säkerställ att det inbäddade uttrycket förblir {{ JSON.stringify($json?.names || []) }} så att deltagarnamn förifylls i gränssnittet.
  7. Koppla Retrieve Participant RecordsAssemble Attendee ListRender Giveaway Page.

⚠️ Vanlig fallgrop: Om ni tar bort uttrycket {{ JSON.stringify($json?.names || []) }} kommer giveaway-listan att laddas tom även om data finns.

Steg 5: Konfigurera bekräftelseskärmen

Visa ett bekräftelsemeddelande efter lyckad registrering.

  1. Öppna Confirmation Screen och ställ in Operationcompletion.
  2. Ställ in Completion TitleGlad You're Here!.
  3. Ställ in Completion MessageThank you for being part of our meetup. Don’t miss the giveaway at the end of the session!.
  4. Koppla Upsert Participant Record till Confirmation Screen.

Tips: Sticky note-lappen Flowpast Branding är informativ och kan behållas eller tas bort utan att påverka körningen.

Steg 6: Testa och aktivera ert workflow

Validera både registrerings- och giveaway-flödena innan ni aktiverar produktion.

  1. Klicka på Test Workflow och skicka in ett testexempel via Registration Form Trigger.
  2. Bekräfta att Upsert Participant Record skriver eller uppdaterar en rad i n8n_meetup_participants med de mappade fälten.
  3. Öppna test-URL:en för Giveaway Webhook Trigger och verifiera att Render Giveaway Page visar HTML:en med förifyllda deltagarnamn.
  4. När allt fungerar, växla workflowet till Active och använd webhook-URL:erna för produktion för formuläret och giveawaysidan.
🔒

Lås upp fullständig steg-för-steg-guide

Få den kompletta implementeringsguiden + nedladdningsbar mall

Vanliga fallgropar

  • PostgreSQL-inloggningar kan löpa ut eller kräva specifika behörigheter. Om det skapar fel, kontrollera först dina credential-inställningar i n8n och databas-användarens privilegier.
  • Om du hostar giveawaysidans endpoint publikt kan cache och proxyer visa en gammal version. Vid osäkerhet: gör en hård omladdning i webbläsaren och bekräfta att webhooken returnerar de senaste frågeresultaten.
  • Standardreglerna för maskning och formatering kanske inte passar din målgrupp. Justera Code-steget som bygger deltagarlistan tidigt, annars kommer du fortsätta “bara fixa det för just det här eventet”.

Vanliga frågor

Hur lång tid tar det att sätta upp den här Postgres-giveaway-automationen?

Cirka 45 minuter om din Postgres-databas är redo.

Behöver jag kunna koda för att automatisera Postgres-giveaway-automation?

Nej. Du kopplar mest konton och justerar några fält. Om du vill ha egna maskningsregler hjälper en liten justering i ett Code-steg, men det är valfritt.

Är n8n gratis att använda för det här Postgres-giveaway-automationsflödet?

Ja. n8n har ett gratis alternativ för egen drift och en gratis testperiod på n8n Cloud. Cloud-planer börjar på $20/månad för högre volymer. Du behöver också räkna med kostnader för hosting av PostgreSQL (ofta $5–$20/månad om du inte redan har en databas).

Var kan jag hosta n8n för att köra den här automationen?

Två alternativ: n8n Cloud (hanterat, enklast setup) eller egen drift på en VPS. För egen drift är Hostinger VPS prisvärd och hanterar n8n bra. Egen drift ger obegränsade körningar men kräver grundläggande serverhantering.

Kan jag anpassa det här Postgres-giveaway-automationsflödet för flera event eller flera vinnare?

Ja, men planera databasfälten först. Du kan lägga till ett event_id i steget “Map Form Fields” så att varje anmälan kopplas till rätt event, och sedan filtrera SQL-frågan i “Retrieve Participant Records” så att den bara hämtar deltagarna för det eventet. För flera vinnare justerar du logiken på giveawaysidan i Code-steget “Assemble Attendee List” (till exempel genom att välja flera unika ID:n). Många team anpassar också maskningsreglerna där för att dölja e-post helt och bara visa delar av namn på scen.

Varför misslyckas min Postgres-anslutning i det här flödet?

Oftast handlar det om databasanvändarens behörigheter eller fel host/port. Bekräfta uppgifterna i n8n och verifiera sedan att din Postgres-användare kan köra upserten och select-frågan som används för att hämta deltagare. Om du kör egen drift, kontrollera också brandväggsregler och att Postgres tillåter anslutningar från din n8n-server.

Hur många registreringar klarar den här Postgres-giveaway-automationen?

För de flesta event: “betydligt fler än du behöver”. Några hundra eller några tusen registreringar fungerar fint på en enkel VPS, eftersom Postgres hanterar den volymen utan problem. I n8n Cloud är din gräns främst din månatliga körningskvot, eftersom varje registrering är en körning och varje sidladdning av giveawaysidan är en till. Om du kör egen drift finns ingen körningsgräns, så den praktiska gränsen är din serverstorlek och hur tung giveawaysidan blir när du bäddar in en väldigt stor lista.

Är den här Postgres-giveaway-automationen bättre än att använda Zapier eller Make?

Ofta, ja, eftersom det här flödet inte bara är “flytta formulärdata till en tabell”. Du gör en upsert (avduplicering) och serverar sedan en livewebbsida från en webhook, och där är n8n mer flexibelt. Du kan köra det med egen drift, vilket är viktigt när du har många anmälningar och inte vill att varje körning ska kännas som en avgift. Zapier och Make kan fortfarande funka för registreringsdelen, men giveawaysidans upplevelse är oftast där team stöter på begränsningar eller börjar sy ihop extra verktyg. Prata med en automationsexpert om du vill ha hjälp att välja en stack som passar din eventstorlek.

När det här väl rullar håller sig din anmälningslista felfri och din giveaway blir rättvis. Du sätter upp det, och sedan kan du fokusera på eventet.

Kontakta oss

Hör av dig, så diskuterar vi hur just din verksamhet kan dra nytta av alla fantastiska möjligheter som AI skapar.

×

Använd mall

Få direkt tillgång till denna n8n-arbetsflödes JSON-fil

\n

Select how many names to pick:

\n \n
\n \n
\n
\n
\n
\n \n
\n
\n\n \n\n \n\n\n" }, "typeVersion": 1.4 }, { "id": "cf039ad9-01ee-4484-ae3d-d0225227da32", "name": "Registration Form Trigger", "type": "n8n-nodes-base.formTrigger", "position": [ 775, 140 ], "webhookId": "48c80b3d-22a0-4add-9d7f-384bff7791b1", "parameters": { "options": { "path": "giveaway-form", "customCss": ":root {\n\t--font-family: 'Inter', sans-serif;\n\t--font-weight-normal: 400;\n\t--font-weight-bold: 700;\n\t--font-size-body: 14px;\n\t--font-size-label: 16px;\n\t--font-size-header: 24px;\n\t--font-size-input: 16px;\n\t--font-size-error: 12px;\n\n\t/* Purple Themed Colors */\n\t--color-background: #f5f3ff; /* soft lavender */\n\t--color-card-bg: #ffffff;\n\t--color-card-border: #e0d7f8;\n\t--color-card-shadow: rgba(103, 80, 164, 0.12);\n\t--color-header: #3c1e74;\n\t--color-label: #4c1d95;\n\t--color-input-text: #1f1b2e;\n\t--color-input-border: #c4b5fd;\n\t--color-focus-border: #7c3aed;\n\t--color-submit-btn-bg: #7c3aed;\n\t--color-submit-btn-bg-hover: #6d28d9;\n\t--color-submit-btn-text: #ffffff;\n\t--color-error: #dc2626;\n\t--color-required: #dc2626;\n\t--color-test-notice-text: #7e22ce;\n\t--color-test-notice-bg: #f3e8ff;\n\t--color-test-notice-border: #e9d5ff;\n\n\t--border-radius-card: 12px;\n\t--border-radius-input: 8px;\n\t--submit-btn-height: 52px;\n\n\t--padding-card: 32px;\n\t--padding-form-input: 14px;\n\t--box-shadow-card: 0 8px 24px rgba(103, 80, 164, 0.15);\n}\n\n/* Body Background */\nbody {\n\tbackground-color: var(--color-background);\n\tfont-family: var(--font-family);\n\tmargin: 0;\n\tpadding: 0;\n\tcolor: var(--color-input-text);\n}\n\n/* Form Container Card */\n.card {\n\tmax-width: 480px;\n\tmargin: 40px auto;\n\tbackground-color: var(--color-card-bg);\n\tborder-radius: var(--border-radius-card);\n\tbox-shadow: var(--box-shadow-card);\n\tpadding: var(--padding-card);\n\tborder: 1px solid var(--color-card-border);\n}\n\n/* Header */\n.card h1 {\n\tfont-size: var(--font-size-header);\n\tfont-weight: var(--font-weight-bold);\n\tcolor: var(--color-header);\n\tmargin-bottom: 24px;\n\ttext-align: center;\n}\n\n/* Test Notice */\n.test-notice {\n\tbackground-color: var(--color-test-notice-bg);\n\tcolor: var(--color-test-notice-text);\n\tborder: 1px solid var(--color-test-notice-border);\n\tborder-radius: 6px;\n\tpadding: 12px 16px;\n\tfont-size: 14px;\n\tmargin-bottom: 24px;\n}\n\n/* Label */\nlabel {\n\tdisplay: block;\n\tfont-size: var(--font-size-label);\n\tfont-weight: var(--font-weight-bold);\n\tcolor: var(--color-label);\n\tmargin-bottom: 8px;\n}\n\n/* Input Field */\ninput[type=\"text\"], input[type=\"email\"] {\n\twidth: 100%;\n\tpadding: var(--padding-form-input);\n\tborder: 1px solid var(--color-input-border);\n\tborder-radius: var(--border-radius-input);\n\tfont-size: var(--font-size-input);\n\tcolor: var(--color-input-text);\n\tbox-sizing: border-box;\n\ttransition: border-color 0.2s ease;\n}\n\ninput[type=\"text\"]:focus, input[type=\"email\"]:focus {\n\tborder-color: var(--color-focus-border);\n\toutline: none;\n}\n\n/* Error Message */\n.error {\n\tcolor: var(--color-error);\n\tfont-size: var(--font-size-error);\n\tmargin-top: 6px;\n}\n\n/* Submit Button */\nbutton[type=\"submit\"] {\n\twidth: 100%;\n\theight: var(--submit-btn-height);\n\tbackground-color: var(--color-submit-btn-bg);\n\tcolor: var(--color-submit-btn-text);\n\tfont-weight: var(--font-weight-bold);\n\tfont-size: 16px;\n\tborder: none;\n\tborder-radius: 10px;\n\tcursor: pointer;\n\ttransition: background-color 0.2s ease;\n}\n\nbutton[type=\"submit\"]:hover {\n\tbackground-color: var(--color-submit-btn-bg-hover);\n}\n\n/* Required Asterisk */\nlabel .required {\n\tcolor: var(--color-required);\n\tmargin-left: 4px;\n}\n", "ignoreBots": true, "buttonLabel": "Submit", "appendAttribution": false }, "formTitle": "n8n Indonesia Community Meetup #2", "formFields": { "values": [ { "fieldName": "Event", "fieldType": "hiddenField", "fieldValue": "=Meetup #2" }, { "fieldLabel": "Nama Lengkap", "requiredField": true }, { "fieldLabel": "Domisili (Kota)", "requiredField": true }, { "fieldType": "textarea", "fieldLabel": "Bio (pekerjaan, specialty, dsb)", "requiredField": true }, { "fieldLabel": "Whatsapp", "requiredField": true }, { "fieldLabel": "Discord Username (yang join di discord channel)" }, { "fieldLabel": "Threads Username" }, { "fieldLabel": "Instagram Username" }, { "fieldType": "email", "fieldLabel": "Email" } ] } }, "typeVersion": 2.2 }, { "id": "d1387478-3489-4d10-b2a0-910eda73469c", "name": "Upsert Participant Record", "type": "n8n-nodes-base.postgres", "position": [ 210, 125 ], "parameters": { "table": { "__rl": true, "mode": "list", "value": "n8n_meetup_participants", "cachedResultName": "n8n_meetup_participants" }, "schema": { "__rl": true, "mode": "list", "value": "public" }, "columns": { "value": [], "schema": [ { "id": "id", "type": "string", "display": true, "removed": false, "required": true, "displayName": "id", "defaultMatch": true, "canBeUsedToMatch": true }, { "id": "nama_lengkap", "type": "string", "display": true, "required": true, "displayName": "nama_lengkap", "defaultMatch": false, "canBeUsedToMatch": false }, { "id": "discord_username", "type": "string", "display": true, "required": false, "displayName": "discord_username", "defaultMatch": false, "canBeUsedToMatch": false }, { "id": "domisili_kota", "type": "string", "display": true, "required": false, "displayName": "domisili_kota", "defaultMatch": false, "canBeUsedToMatch": false }, { "id": "bio", "type": "string", "display": true, "required": false, "displayName": "bio", "defaultMatch": false, "canBeUsedToMatch": false }, { "id": "email", "type": "string", "display": true, "required": false, "displayName": "email", "defaultMatch": false, "canBeUsedToMatch": false }, { "id": "whatsapp", "type": "string", "display": true, "required": false, "displayName": "whatsapp", "defaultMatch": false, "canBeUsedToMatch": false }, { "id": "threads_username", "type": "string", "display": true, "required": false, "displayName": "threads_username", "defaultMatch": false, "canBeUsedToMatch": false }, { "id": "instagram_username", "type": "string", "display": true, "required": false, "displayName": "instagram_username", "defaultMatch": false, "canBeUsedToMatch": false }, { "id": "created_at", "type": "dateTime", "display": true, "required": false, "displayName": "created_at", "defaultMatch": false, "canBeUsedToMatch": false }, { "id": "event", "type": "string", "display": true, "required": false, "displayName": "event", "defaultMatch": false, "canBeUsedToMatch": false } ], "mappingMode": "autoMapInputData", "matchingColumns": [ "id" ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": [], "operation": "upsert" }, "credentials": { "postgres": { "id": "credential-id", "name": "" } }, "typeVersion": 2.6 } ], "active": true, "pinData": [], "settings": { "executionOrder": "v1" }, "versionId": "", "connections": { "Giveaway Webhook Trigger": { "main": [ [ { "node": "Retrieve Participant Records", "type": "main", "index": 0 } ] ] }, "Registration Form Trigger": { "main": [ [ { "node": "Map Form Fields", "type": "main", "index": 0 } ] ] }, "Retrieve Participant Records": { "main": [ [ { "node": "Assemble Attendee List", "type": "main", "index": 0 } ] ] }, "Assemble Attendee List": { "main": [ [ { "node": "Render Giveaway Page", "type": "main", "index": 0 } ] ] }, "Map Form Fields": { "main": [ [ { "node": "Upsert Participant Record", "type": "main", "index": 0 } ] ] }, "Upsert Participant Record": { "main": [ [ { "node": "Confirmation Screen", "type": "main", "index": 0 } ] ] } } }
Få prisoffert redan idag!
Få prisoffert redan idag!

Berätta vad ni behöver hjälp med så hör vi av oss inom en arbetsdag!

Få prisoffert redan idag!
Få prisoffert redan idag!

Berätta vad ni behöver hjälp med så hör vi av oss inom en arbetsdag!

Launch login modal Launch register modal