Under varje inlägg i anteckningsboken finns nu fyra knappar: gilla, ogilla, älska och applådera. Klicka och räknaren går upp. Klicka igen och den går ner. Gilla och ogilla utesluter varandra, som man kan vänta sig.
Knapparna är det minst intressanta. Det intressanta är vad som inte behövdes för att bygga dem: ingen server, ingen databas att drifta, inget nytt konto att hålla reda på. Backend är en fil, och databasen kom från en meny.
Varför inte lägga räknarna i Sanity?#
Första tanken var förstås att spara räknarna där inläggen redan ligger. Det finns två skäl att låta bli. Det första: webbläsaren läser Sanity anonymt i dag, och skrivning kräver en token. En token i en bundle är en token alla har, och då kan vem som helst ändra vad som helst — inte bara bumpa en siffra. Det andra är subtilare: en räknare som ligger på inlägget krockar med utkast. Har någon ett utkast öppet i Studio medan läsarna klickar, skriver publiceringen över räknaren med utkastets gamla värde.
Räknare är inte innehåll. De ska bo någon annanstans.
En Redis från menyn#
Vercel Marketplace har Upstash Redis som integration. Man väljer den, väljer region (vi tog EU) och kopplar den till projektet. Det är hela provisioneringen. Miljövariablerna — KV_REST_API_URL och KV_REST_API_TOKEN — dyker upp i projektet av sig själva, i alla miljöer.
Gratisnivån räcker till 500 000 kommandon i månaden. En reaktion kostar fyra: räkna upp, uppdatera topplistan, läsa tillbaka och ett för spärren mot missbruk. Varje sidvisning av ett inlägg kostar ett. Med 20 000 sidvisningar och 10 000 reaktioner i månaden landar vi på 60 000 — drygt en tiondel av gratisnivån. Om den här bloggen någonsin får det problemet skriver vi gärna ett inlägg om det.
Klienten pratar REST, inte ett öppet socket. Det är poängen i serverless: en funktion som startar kallt ska inte sitta och hålla anslutningar, och Upstash är byggt för precis det.
Hela backenden är en fil#
Filen ligger i katalogen api/ bredvid frontendkoden. Vercel ser katalogen och gör en funktion av varje fil i den. Så här ser kärnan ut, med valideringen bortklippt:
api/react.js
import { Redis } from '@upstash/redis';
import { Ratelimit } from '@upstash/ratelimit';
const redis = new Redis({ url: process.env.KV_REST_API_URL, token: process.env.KV_REST_API_TOKEN });
const limiter = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(20, '10 m') });
export async function POST(request) {
const { slug, kind, delta = 1 } = await request.json();
const ip = request.headers.get('x-forwarded-for') ?? 'unknown';
if (!(await limiter.limit(ip)).success) {
return Response.json({ error: 'slow down' }, { status: 429 });
}
const key = `reactions:${slug}`;
await redis.hincrby(key, kind, delta); // atomärt — inga tappade klickTre saker gör den värd att visa. HINCRBY är atomärt: två personer som klickar samtidigt ger två, inte ett. Det sorterade settet reactions:top håller totalen per inlägg, så att sorteringen ”Populärast” på bloggen kan börja betyda något på riktigt — fram till nu har den sorterat på lästid, för det var den enda siffran som fanns. Och @upstash/ratelimit ger ett glidande fönster på 20 reaktioner per IP-adress och tio minuter, på en rad. Mer än en ärlig läsare någonsin behöver, för lite för att ett skript ska kunna göra ett inlägg populärt på en eftermiddag.
Saknas miljövariablerna svarar funktionen 503, och sidan döljer knapparna. Det gjorde att koden gick att deploya innan databasen fanns — och att sidan överlever om integrationen någon gång tas bort.
Vad som inte sparas#
Räknarna är anonyma summor. När du klickar skickas inläggets adress, reaktionstypen och — som i alla HTTP-anrop — din IP-adress. IP-adressen används enbart i spärren mot missbruk och lever i tio minuter där; den sparas aldrig tillsammans med räknaren. Vad du själv har reagerat på ligger i din egen webbläsare, i localStorage, så att knappen visas som vald och går att ta tillbaka. Vi vet inte vem som klickat. Allt det står i cookies-policyn och integritetspolicyn, som uppdaterades samma dag som knapparna kom.
Går det att lura?#
Ja. Den som vill kan rensa sin webbläsare eller byta IP-adress och klicka igen. Spärren gör det tråkigt, inte omöjligt. Så är det med alla räknare utan inloggning, och alternativet — att kräva ett konto för att få applådera — är en sämre blogg. Vi tar hellre en siffra som kan vara lite fel än en funktion ingen använder.
Vårt korta svar#
Räknare är inte innehåll — lägg dem inte i CMS:et. En Redis från Vercel Marketplace, en funktion på en fil och HINCRBY räcker: gratis upp till 500 000 kommandon i månaden, atomärt, utan en server att drifta. Spara summor, inte människor.
Knapparna finns här nedanför. De är kopplade.
