← Tilbake til oversikten

Intro to Supabase og JavaScript

Lag en enkel nettside som lagrer og viser meldinger via Supabase.

Trinn 1 — Opprett database med én tabell

-- Opprett tabell (hvis den ikke finnes)
create table if not exists public.messages (
  id bigint generated always as identity primary key,
  content text not null,
  created_at timestamptz not null default now()
);

-- Aktiver radnivå-sikkerhet
alter table public.messages enable row level security;

-- Tillat at alle (anon) kan lese meldinger
create policy "Anon kan lese" 
on public.messages
for select
using (true);

-- Tillat at alle (anon) kan skrive nye meldinger
create policy "Anon kan skrive" 
on public.messages
for insert
with check (true);

-- (valgfritt) sørg for at public kan bruke tabellen
grant all on public.messages to anon;
 

Trinn 2 — Finn nøkler

Viktig: Hent API URL og Anon key fra Supabase → Settings → API.

Husk: API URL og Anon key skal byttes inn i koden under. Se etter const SUPABASE_ANON_KEY og const SUPABASE_URL. Lim inn mellom "...".

Trinn 3 — Lag elevside (index.html)

Kopier hele blokken under til en ny fil som heter index.html.

        
<!doctype html>
<html lang="no">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Supabase demo — Meldinger</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { background:#f8fafc; }
    .card-elev { border:1px solid #e9ecef; border-radius:1rem; }
    .muted { color:#6c757d; }
  </style>
</head>
<body>
  <nav class="navbar navbar-expand-md bg-white border-bottom">
    <div class="container">
      <a class="navbar-brand fw-semibold" href="#">Supabase demo</a>
    </div>
  </nav>

  <main class="container my-4">
    <header class="text-center mb-4">
      <h1 class="h4 fw-bold">Navn + kort melding</h1>
      <p class="text-muted mb-0">Skriver til databasen og viser under.</p>
    </header>

    <section class="row g-4">
      <div class="col-12 col-lg-5">
        <div class="card card-elev shadow-sm">
          <div class="card-body">
            <h2 class="h6 mb-3">Send inn</h2>

            <form id="form" class="needs-validation" novalidate>
              <div class="mb-3">
                <label class="form-label" for="name">Navn</label>
                <input id="name" class="form-control" minlength="2" maxlength="30" required placeholder="F.eks. Sara">
                <div class="invalid-feedback">Skriv inn navnet ditt (2–30 tegn).</div>
              </div>

              <div class="mb-3">
                <label class="form-label" for="content">Kort melding</label>
                <input id="content" class="form-control" maxlength="140" required placeholder="Hei verden! (maks 140 tegn)">
                <div class="invalid-feedback">Skriv en kort melding (maks 140 tegn).</div>
              </div>

              <button class="btn btn-primary w-100" type="submit">Lagre</button>
              <div id="status" class="form-text mt-2 muted">Klar</div>
            </form>
          </div>
        </div>
      </div>

      <div class="col-12 col-lg-7">
        <div class="card card-elev shadow-sm">
          <div class="card-body">
            <div class="d-flex align-items-center justify-content-between mb-2">
              <h2 class="h6 mb-0">Meldinger</h2>
              <span class="badge text-bg-light"><span id="count">0</span> stk</span>
            </div>
            <div id="msgs" class="d-grid gap-2"></div>
          </div>
        </div>
      </div>
    </section>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
  <script>
    const SUPABASE_URL = "https://DIN-PROSJEKT-ID.supabase.co";
    const SUPABASE_ANON_KEY = "ey...DIN_ANON_KEY...";
    const supabase = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);

    const form = document.getElementById('form');
    const nameInput = document.getElementById('name');
    const contentInput = document.getElementById('content');
    const msgsEl = document.getElementById('msgs');
    const statusEl = document.getElementById('status');
    const countEl = document.getElementById('count');

    function esc(s) {
      return (s || '').replace(/[<>&"']/g, c => ({
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#39;'
      }[c]));
    }

    function parseRow(r) {
      const raw = r.content || '';
      const i = raw.indexOf('::');
      if (i === -1) return { name: 'Anonym', message: raw, created_at: r.created_at };
      return { name: raw.slice(0,i), message: raw.slice(i+2), created_at: r.created_at };
    }

    async function loadMessages() {
      statusEl.textContent = 'Laster...';
      const { data, error } = await supabase.from('messages').select('*').order('created_at', { ascending:false }).limit(100);
      if (error) { statusEl.textContent = 'Feil: ' + error.message; return; }

      const rows = data || [];
      countEl.textContent = rows.length;

      msgsEl.innerHTML = rows.map(r => {
        const it = parseRow(r);
        const when = new Date(it.created_at).toLocaleString();
        return `<div class="border rounded p-2"><strong>${esc(it.name)}:</strong> ${esc(it.message)} <span class="ms-2 muted">${when}</span></div>`;
      }).join('');

      statusEl.textContent = 'Klar';
    }

    form.addEventListener('submit', async (e) => {
      e.preventDefault();
      form.classList.add('was-validated');
      if (!form.checkValidity()) return;
      const name = nameInput.value.trim();
      const msg = contentInput.value.trim();
      if (!name || !msg) return;

      const submitBtn = form.querySelector('button[type="submit"]');
      submitBtn.disabled = true;
      statusEl.textContent = 'Lagrer...';

      const { error } = await supabase.from('messages').insert({ content: name + '::' + msg });
      submitBtn.disabled = false;
      if (error) { statusEl.textContent = 'Feil ved lagring: ' + error.message; return; }

      form.reset();
      form.classList.remove('was-validated');
      statusEl.textContent = 'Lagret!';
      nameInput.focus();
      await loadMessages();
    });

    loadMessages();
  </script>
</body>
</html>