Ebizapple
Ta kontakt Logg inn i kundeportalen

+47 464 69 500 [email protected]

MakeDynamiske nettsider

Dynamisk nettsted: én mal, ett datasett og én port

En mal, ett datasett og en port som stopper det som ikke holder mål. Slik bygger vi nettsteder der hundre sider vedlikeholdes som én.

Daglig leder, Ebizapple Oppdatert 16 min lesetid
Diagram «klyngebygger»: Fem steg i Make — Start, Notion, Iterator, Samler tekst og Retur — som bygger cluster_html fra sider med status GROUNDED

Vi driver et dynamisk nettsted selv. Ingen kunde har bestilt det, ingen har betalt for det, og det er nettopp derfor vi kan skrive om innsiden av det — en reiseguide bygget rundt et kart, der hundrevis av steder deler én mal og ett datasett.

Vi kaller det kartprosjektet her, og oppgir ingen tall for hvor stort det er. Ikke fordi det er hemmelig — en oppmerksom leser finner det nok — men fordi det er under kontinuerlig utvikling. Et sidetall derfra er utdatert kort tid etter at det er skrevet, og en artikkel som lener seg på et slikt tall blir en reklame med kort holdbarhet i stedet for en forklaring. Vil du se det, spør oss, så viser vi det fram med det som står der den dagen.

Det som ikke endrer seg, er hvordan det er bygget. Ingen av sidene er laget for hånd som en side. De er rader som møter en mal. Legger vi til et sted, finnes siden, og den ligger allerede i kartet, i filtrene og i de interne lenkene til nabosidene.

Denne artikkelen er en gjennomgang av mekanikken: hvorfor filtrene er en datamodellbeslutning og ikke en designbeslutning, hvorfor de fleste automatiseringene ikke går på klokke, hvordan et scenario ser ut når du åpner det, og hva porten foran publisering faktisk stopper.


Diagram: S, 1, Start, scenario-service:StartSubscenario, tar imot slug og provins, N, 2, Notion, notion:makeApiCall
Fem moduler på rad i Make: start, Notion, iterator, tekstsamler og retur, med modulnummer

Kort vei til svaret: har dere en katalog, et register eller et kart som skal bli til mange sider, be om en gjennomgang av datamodellen — det er den som avgjør prisen senere. Vil du se hvordan vi løste det, les videre.

Slik vurderer vi dette

Alt i denne artikkelen er hentet fra vårt eget oppsett, lest 15. august 2026. Scenarioene er lest ut av Make sitt API, ikke talt opp i grensesnittet og ikke husket.

Det vi kan stå inne for:

  • De to scenarioene som beskrives, er gjengitt modul for modul slik de faktisk står — ikke forenklet til en illustrasjon
  • Der noe er et valg vi tok, står det at det var et valg
  • Der vi bevisst utelater et tall, står det hvorfor
  • Ingen tall er hentet fra hukommelsen

Om tallene: artikkelen er full av dem, men ikke ett eneste for størrelsen på kartprosjektet — av grunnen som står i ingressen.

Det vi ikke påstår: at dette er den eneste måten å bygge et dynamisk nettsted på, eller at oppsettet er ferdig. Store deler av datasettet er fortsatt tynt, og noen av scenarioene er byggeklosser vi laget for ett formål og siden gjenbrukte til et annet.

Vi oppgir heller ingen tall for hvor mye tid dette sparer. Vi har ikke bygget nettstedet manuelt først for å måle forskjellen, og et anslag uten en måling bak er bare en påstand med desimaler.

Innholdet endrer seg, og scenarioene endrer seg. Artikkelen gjennomgås derfor på nytt når innholdet eller scenarioene endrer seg. Sist gjennomgått 15. august 2026.

Skrevet av Svein Tore Olsen, daglig leder i Ebizapple, som holder til i Askim.


Diagram «Én mal, ett datasett, mange sider»: Én mal, ett datasett, mange sider, Ingen av sidene er laget for hånd, STATISK
Arbeidet flytter seg fra å lage sider til å vedlikeholde data. Det er hele forskjellen.

Én mal, ett datasett, hundrevis av sider

Et statisk nettsted har én fil per side. Skal du ha tre hundre sider, lager du tre hundre sider, og skal du endre hvordan de ser ut, endrer du tre hundre ganger.

Et dynamisk nettsted snur det: du lager én mal og ett datasett. Malen beskriver hvordan en oppføring skal se ut. Datasettet inneholder radene. Sidene finnes fordi radene finnes.

Geografien i vårt eget tilfelle har to nivåer: kyststrekninger, og comarcaer — den spanske inndelingen under provinsnivå. Det er derfor en oppføring kan filtreres på begge nivåer uten at de er i konflikt. Poenget er ikke geografien; poenget er at to uavhengige nivåer må være to felter i datasettet, ikke ett. Slår du dem sammen til ett, oppdager du det den dagen noen vil filtrere på det smale nivået alene.

Fordelingen mellom kategoriene blir alltid skjev. Der noen har jobbet mye, ligger det gjerne ti ganger så mange oppføringer som i de tynneste — ikke fordi noe er galt, men fordi et datasett vokser ujevnt. Det er en av grunnene til at malen må tåle en kategori med en håndfull oppføringer like godt som en med femti. En mal som ser bra ut bare når listen er lang, er ikke en mal. Den er et designutkast.

Det samme gjelder feltene. En oppføring som mangler et bilde, skal ikke gi et hull i layouten. I praksis betyr det at malen må bygges rundt den fattigste raden, ikke rundt den rikeste. Vi bygger nettsteder etter det samme prinsippet i Oxygen — se hvordan vi setter opp komponenter før sider når innholdet skal komme fra et system.

Er ikke dette bare en database med et pent skall? Nei, og det er verdt å ta innvendingen på alvor, for den er halvveis riktig. Databasen er der. Men et dynamisk nettsted er ikke bare en visning av rader — det er malen, filtrene, de interne lenkene og vedlikeholdet som holder radene sanne over tid. Uten det siste har du en database med et pent skall, og det er nettopp den tilstanden folk pleier å ende i.


Filtrene er datamodellen, ikke pynt

Det som gjør et slikt nettsted til noe annet enn en lang liste, er kartet og filtrene. Hos oss kan brukeren snevre inn på stedstype og på område samtidig — strand, vik, kafé, turløype, historisk sted, museum, naturpark, utsiktspunkt, landsby og noen til, krysset med distriktsinndelingen.

Det ser ut som en designbeslutning. Det er en datamodellbeslutning, og det er den dyreste å gjøre om senere.

Grunnen er enkel: et filter kan bare filtrere på noe som ligger i datasettet som et eget felt. Har du skrevet «en fin liten strandby med museum» i et fritekstfelt, kan du ikke filtrere på det.

To spørsmål avgjør derfor hvordan modellen skal se ut, og de bør stilles før noen tegner en eneste skjerm:

  1. Kan en destinasjon ha flere stedstyper samtidig? Svaret styrer om feltet er en verdi eller en liste, og det styrer hvordan filteret må oppføre seg når to filtre står på samtidig.
  2. Hva skjer når kombinasjonen er tom? En bruker som velger «Museum» i en region uten museer, skal møte en forklaring — ikke et tomt kart som ser ut som en feil.

Dette er den delen av jobben som ser billigst ut og er dyrest å angre på. Det er også grunnen til at vi vurderer datakilden før vi priser noe: nivågraderingen fra N1 til N5 handler om nøyaktig dette — hvor mye struktur som allerede finnes, og hvor mye som må lages.

Skal dere bygge noe med filtre? Send oss feltene dere har i dag, så sier vi hva som mangler før dere låser modellen.


Scenarioene — og hvorfor de fleste er on-demand

De fleste scenarioene bak kartprosjektet er on-demand. Det er den viktigste opplysningen i denne seksjonen, og den er verdt mer enn hvor mange de er.

Kjøremåten leses av feltet scheduling.type på hvert scenario, og den er uavhengig av om scenarioet står på. En del av de on-demand er inaktive til enhver tid — de er byggeklosser ingen kaller på for tiden, ikke jobber som har stoppet. Resten fordeler seg på noen som startes av en webhook utenfra, og noen som går på klokke.

Et on-demand-scenario er ikke en jobb som står og går. Det er en byggekloss som andre scenarioer kaller på som et verktøy. Det tar imot parametre, gjør én ting, og returnerer et svar. Det er nøyaktig samme idé som en funksjon i et program: du skriver den én gang, og kaller den fra ti steder.

Arkitekturen har altså få inngangspunkter og mange deler. De tidsstyrte og de webhook-drevne er inngangene. De on-demand er delene, og de kjører bare når noen kaller på dem.

Blir det ikke uoversiktlig med mange scenarioer? Man har ikke like mange jobber. Alternativet til mange små byggeklosser er noen få store scenarioer som gjør alt — og de er verre. Et stort scenario kan ikke gjenbrukes, det er vanskelig å teste isolert, og når det stopper, stopper alt på én gang. Deler du det opp, kan du kjøre én kloss alene og se om den fortsatt gjør jobben sin.

Prisen du betaler for oppdelingen er navngiving og oversikt. Et scenario som heter «test 3» er tapt i det øyeblikket du har hundre av dem — og vi har selv slike i listen. Det er en av grunnene til at automatisering er noe man vedlikeholder, ikke noe man leverer og går fra.


Slik leser du et Make-scenario visuelt

Nå til det som er vanskeligst å forklare med ord, og enklest å forstå når du ser det: hvordan lese et scenario i Make.

Dette er den reelle forskjellen fra et skript. Et skript er tekst, og tekst kan bare leses av noen som kan lese den. Et scenario er en tegning — og en tegning kan leses av kunden, av en kollega, og av deg selv om seks måneder.

Sirklene er moduler, og de leses fra venstre

Hver modul er en sirkel. De står på rad og leses fra venstre mot høyre, i den rekkefølgen de kjører. Første sirkel er starten, siste sirkel er slutten.

Under hver sirkel står to ting: hva modulen heter, og hvilken handling den utfører — for eksempel notion:makeApiCall eller wordpress:ActionCreatePost. Den nederste linjen er den viktige. Den sier hva modulen faktisk gjør, ikke bare hvilken app den snakker med.

Nummeret er modulens adresse

Øverst til venstre på hver sirkel står et lite nummer i en hvit ring. Det er modulens id, og det er ikke pynt.

Nummeret er adressen du bruker når du henter en verdi fra en tidligere modul. Ser du uttrykket {{2.body.results}} et sted i scenarioet, betyr det: «feltet body.results fra modul 2». Ingenting annet.

Det er derfor du kan lese et scenario uten å lese kode. Finner du et uttrykk som peker på modul 2, ser du på sirkel nummer 2 og vet hvor verdien kommer fra.

Linjene er ruter, og trakter er filtre

Linjen mellom to sirkler er en rute. Data flyter langs den.

Ligger det et filter på ruten, tegner Make en liten trakt midt på linjen, med filterets navn ved siden av. Det er hele varselet: det som går inn i traktens venstre side, kommer ikke nødvendigvis ut på høyre.

Et filter er altså ikke en modul. Det er en betingelse som henger på forbindelsen mellom to moduler, og den er synlig som en trakt selv om du ikke åpner noe.

Iterator splitter, aggregator samler

To modultyper hører alltid sammen, og de er verdt å kjenne igjen på formen i teksten under sirkelen:

  • En iterator tar én bundel med mange elementer og splitter den i mange kjøringer — én per element
  • En aggregator tar mange kjøringer og samler dem til én bundel igjen

Ser du en iterator uten en aggregator etterpå, fortsetter scenarioet å kjøre én gang per element helt til slutten. Det er noen ganger riktig, og noen ganger er det grunnen til at du får 100 innlegg i stedet for ett. Fellen er beskrevet nærmere i fem feller i Make.

Fargen på sirkelen er appens

Fargen velger du ikke. Den følger appen: Notion er svart, WordPress har sin, en innebygd Make-modul er hvit. Det er en detalj, men den er nyttig — du ser hvilke systemer et scenario rører ved, før du har lest ett ord.

Summen av dette er poenget: du kan se hva et scenario gjør uten å lese kode. Det er derfor kunden selv kan åpne kjøringsloggen og peke på hvilken sirkel det stoppet i, i stedet for å sende oss en skjermdump av en feilmelding uten sammenheng.


Klyngebyggeren som skriver de interne lenkene

Første eksempel er «KLYNGE-BYGGER». Det er fem moduler på rad, og det gjør én ting: bygger interne lenker mellom sider som hører til samme region.

# Modul Hva den gjør
1 scenario-service:StartSubscenario Tar imot slug og provins
2 notion:makeApiCall Henter sidene fra Notion-databasen
3 builtin:BasicFeeder Itererer over treffene, én kjøring hver
4 util:TextAggregator Bygger listen med lenker
5 scenario-service:ReturnData Returnerer cluster_html og region

Modul 1 gjør scenarioet til et verktøy. Det tar imot to parametre: slug — sidens egen, som skal ekskluderes — og provins, altså costa-regionen det skal bygges klynge for. Fordi parametrene kommer utenfra, kan det samme scenarioet brukes for hvilken som helst side i hvilken som helst region.

Modul 2 er et POST-kall mot Notion-databasen, med filteret Status = GROUNDED, sortert på Entity, med page_size: 100. Filteret er verdt å stoppe ved: bare sider som er grunnet, blir lenket til. En side som ikke har kommet gjennom kvalitetskontrollen, finnes ikke for klyngebyggeren.

Modul 3 og 4 er iterator-og-aggregator-paret fra forrige seksjon. Feederen itererer over {{2.body.results}} — der ser du modulnummeret i praksis — og gir én kjøring per side. Aggregatoren bygger så én <li><a href='…'> per treff, og hopper over de radene der provinsen ikke stemmer eller sluggen er sidens egen. Uten den siste sjekken ville hver side lenket til seg selv.

Modul 5 returnerer cluster_html og region til den som spurte.

Resultatet er en boks nederst på siden, med en overskrift som selv er en mal: den inneholder et krøllparentes-felt som fylles med regionnavnet i det øyeblikket boksen bygges. Overskriften er altså ikke skrevet én gang per region — den er skrevet én gang, punktum.

Poenget med å gjøre dette i et scenario framfor for hånd: lenkene er alltid ferske. Kommer det en ny destinasjon i en region, dukker den opp i klyngen til naboene neste gang boksen bygges. Ingen sitter og oppdaterer lenkelister.


Diagram «Scenario eller byggeskript»: Scenario eller byggeskript, Samme mønster, to måter å lage det på, SCENARIO, 01
Valget avhenger av hvem som endrer innholdet og hvor ofte — ikke av hva som er teknisk finest.

Den samme klyngelenkingen på ebizapple.no

Her er ironien, og vi tar den med fordi den sier noe ærlig om hvordan slike valg faktisk tas.

Klyngelenkingen på ebizapple.no — pilarsiden lenker ned til hver artikkel i klyngen, og hver artikkel lenker opp igjen til pilaren — er nøyaktig det samme mønsteret som klyngebyggeren lager: en side skal aldri stå alene.

Forskjellen er hvordan den er laget. På kartprosjektet gjør et scenario det, løpende. På denne siden gjør et byggeskript det, når nettstedet bygges.

Er det ene bedre enn det andre? Ikke prinsipielt. Valget følger av hvor ofte innholdet endrer seg og hvem som skal endre det:

Den siste raden er den vi oftest gir bort gratis. Automatisering av noe som skjer tre ganger i året koster mer enn den sparer.


Porten som ikke publiserer noe halvferdig

Andre eksempel er «GATE-then-PUBLISH». Tre moduler, og likevel det scenarioet som har spart oss for mest opprydding.

Diagram: S, 1, Start, scenario-service:StartSubscenario, tar imot artikkelen, W, 2, WordPress, wordpress:ActionCreatePost
Tre moduler i Make med en filtertrakt tegnet mellom modul 1 og 2, merket ti betingelser

Modulene er:

  1. scenario-service:StartSubscenario — tar imot title, slug, content, meta_description, aof_task_uid, canonical, status, evidence og p0_threshold
  2. wordpress:ActionCreatePost — oppretter innlegget
  3. scenario-service:ReturnData — returnerer resultatet

Det interessante ligger ikke i modulene. Det ligger på ruten mellom modul 1 og modul 2, der det henger et filter som heter «P0 hard gate (schema-tro)». På tegningen er det trakten.

Filteret har ti betingelser, og alle må være sanne for at bundelen slipper videre til publisering:

# Felt Betingelse
1 title finnes
2 title minst 3 tegn
3 title høyst 200 tegn
4 aof_task_uid finnes
5 slug matcher ^[a-z0-9]+(?:-[a-z0-9]+)*$
6 canonical begynner med https://
7 meta_description finnes
8 meta_description høyst 320 tegn
9 content minst 300 tegn
10 evidence er minst p0_threshold

De ni første er formkrav. De sier at en side ikke er en side før den har en tittel, en gyldig slug, en kanonisk adresse og nok innhold til å være verdt å indeksere. Betingelse 5 er den som fanger mest i praksis: en slug med en stor bokstav, en æ, eller to bindestreker etter hverandre stanses her, i stedet for å bli en stygg adresse ingen kan endre etterpå.

Den tiende er en annen type krav. evidence mot p0_threshold er belegg mot terskel: hvor godt underbygget innholdet er, målt mot hvor godt det må være. Er belegget under terskelen, publiseres det ikke, uansett hvor pen teksten er.

Kommer bundelen ikke gjennom, skjer det ingenting. Ingen kladd, ingen halvferdig side, ingen «publisert, men fiks den senere».

Og det er den andre halvdelen av poenget: porten sier heller ikke at det gikk bra. En automatisering som svarer «ferdig» på noe den ikke gjorde, er farligere enn en som stopper. Det er hele resonnementet fra når AI blir blokkert og ingen merker det, og porten her er den praktiske konsekvensen av det.

Kan vi ikke bare skrive sidene selv? Jo, og for tjue sider er det sannsynligvis riktig. Poenget med porten er ikke å erstatte en skribent. Det er at når først noe skal skje maskinelt — enten det er en importert katalog eller en generert beskrivelse — så må det finnes et sted der kravene er skrevet ned som betingelser, ikke som en avtale noen husker. Ti betingelser i et filter er en slik nedskriving.


Diagram «Rekkefølgen vi ville bygget i»: Rekkefølgen vi ville bygget i, Den samme uansett hva radene handler om
Rekkefølgen er der kostnaden ligger. Bytter du om på to av dem, betaler du for det senere.

Slik ville vi bygget et dynamisk nettsted i dag

Rekkefølgen er den samme uansett om det er destinasjoner, produkter eller ansatte. Den er også den samme som vi selv brøt et par ganger før vi lærte det.

Datamodellen først, malen etterpå. Hvilke felter finnes, hvilke er obligatoriske, og hvilke skal det filtreres på? Filtrene er felter. Det du ikke har skilt ut som et felt, kan ikke bli et filter senere uten at alle radene må vurderes på nytt.

Bygg malen mot den fattigste raden. Den raden med manglende bilde og to setningers beskrivelse er den som avgjør om malen holder.

Skaff dataene før du lover en dato. Å hente inn noen hundre rader er en helt annen jobb avhengig av hvor de kommer fra. Vi bruker nivågradering før vi priser datainnhenting, og for katalogjobber spesielt er historien om produktkatalogen vi prøvde å hente en ærligere beskrivelse av hva som møter deg enn noen kravspesifikasjon.

Del automatiseringen i byggeklosser. Ett scenario som gjør én ting og returnerer et svar, kalt fra et scenario som styrer rekkefølgen. Det er derfor de fleste av våre er on-demand.

Sett porten på plass før den første publiseringen, ikke etterpå. Det er lett å legge til en port når ingenting er publisert ennå. Det er en oppryddingsjobb å legge den til når flere hundre sider allerede ligger ute.

Bestem hvem som eier innholdet etterpå. Et dynamisk nettsted flytter arbeidet fra å lage sider til å vedlikeholde data. Det er en bedre jobb, men det er fortsatt en jobb, og den forsvinner ikke fordi noe er automatisert. Se hva som inngår når vi bygger nettsteder på WordPress, og hva et slikt oppsett koster i prisoversikten vår.


Spørsmål om dynamiske nettsteder

Hvor mange scenarioer kreves for et slikt nettsted?

Flere titalls, men tallet er mindre interessant enn fordelingen. Rundt tre firedeler er on-demand — altså byggeklosser som andre scenarioer kaller på, ikke jobber som står og går. Inngangene er få: en håndfull som starter på en webhook, og en håndfull som går på klokke. Det er oppdelingen som gjør at én del kan byttes uten at resten stopper.

Hva er forskjellen på et dynamisk nettsted og vanlige sider?

Vanlige sider lages én om gangen. Et dynamisk nettsted har én mal og ett datasett, og hver rad blir en side. Endrer du malen, endrer du alle sidene samtidig — og legger du til en rad, finnes siden med en gang, med kart, filtre og interne lenker på plass.

Hvordan ser et scenario ut i Make?

Som en rad sirkler koblet med linjer, lest fra venstre mot høyre. Hver sirkel er en modul med et nummer øverst til venstre, og nummeret er adressen du bruker i uttrykk som {{2.body.results}}. Ligger det et filter mellom to moduler, tegnes det som en trakt på linjen.

Hva skjer når publiseringsporten avviser en side?

Ingenting. Bundelen slipper ikke forbi filteret, WordPress-modulen kjører ikke, og det opprettes verken innlegg eller kladd. Porten rapporterer heller ikke at det gikk bra — det er en villet egenskap, ikke en mangel.

Må man bruke Make for å bygge noe slikt?

Nei. Det samme kan gjøres med skript. Forskjellen er lesbarheten: et scenario kan åpnes av noen som ikke programmerer, og de kan se hvilken modul kjøringen stoppet i. Det er verdt mye i et oppsett som skal leve videre hos noen andre enn den som bygde det.

Hvordan holdes innholdet automatisk oppdatert?

Ved at innholdet ikke bor i sidene, men i datasettet. De tidsstyrte og webhook-drevne scenarioene oppdaterer datasettet, og sidene følger etter fordi de er bygget av det. Interne lenker mellom sider bygges på samme måte, av klyngebyggeren, i stedet for å vedlikeholdes for hånd.


Videre lesing om dynamiske nettsider

Sitter dere med et register, en katalog eller et kart som burde vært et nettsted? Vi ser på datamodellen først og sier hva den tåler — ta kontakt for en gjennomgang.

Mer om nettsider og wordpress

Hvorfor vi ikke gir deg en ytelsesgaranti

Mange byråer garanterer «PageSpeed 90+». Vi gjør det ikke. Tallet avhenger av fire ting ingen leverandør styrer etter lansering. Her er hva vi lover i stedet.

middels Les

Denne artikkelen hører til nettsider og wordpress. Skal noen gjøre jobben for deg, er det dette vi tilbyr: WordPress-nettsider bygget på komponenter.