
Alright lads, this will be a quick one. I want to add the views count functionality on my personal portfolio website's blog section.
Expected behavior:
- Blogs Page : List of blogs -> Show count.
- Blog Page : Particular Article -> Show count and Increment count.
How to achieve:
- Use supabase to store count by slug
- Stored procedure to increment count
Tools that I'll need:
Setting up supabase table :
Create a tableviews
with schema like such:
- slug -> text -> primary key
- created_at -> timestamp -> now()
- count -> int2
Updatingcount
:
- Fetch count
- Increment one
- Fetch count again
Now we can reduce this to one db call using stored procedures:
createfunctionincrement(slug_texttext)returnsvoidas$$updateviewssetcount=count+1whereslug=slug_text;$$languagesqlvolatile;
In NextJs:
We'll define a route for ease:
- /api/view/{slug}
and then we'll use thePOST
request to register a view andGET
to increment the view count.
Our handler code will look like this:
views.ts
import{createClient,PostgrestError}from"@supabase/supabase-js";constsupabase=createClient(process.env.SUPABASE_URL,process.env.SUPABASE_KEY);interfaceSupabaseResult{data?:{count:number};error?:PostgrestError;}///constgetViews=async(slug:string):Promise<number>=>{const{data:views,error}:SupabaseResult=awaitsupabase.from("views").select(`count`).match({slug:slug}).single();if(error&&error.details.includes(`0 rows`)){const{data,error}:SupabaseResult=awaitsupabase.from(`views`).insert({slug:slug,count:1},{returning:`representation`}).single();returndata.count;}if(!views){return0;}returnviews.count;};///constregisterView=async(slug:string):Promise<void>=>{const{data,error}=awaitsupabase.rpc("increment",{slug_text:slug,});};export{getViews,registerView};
- /api/view/[slug].ts
// /api/view/[slug].ts// Next.js API route support: https://nextjs.org/docs/api-routes/introductionimport{getViews,registerView}from"lib/views";importtype{NextApiRequest,NextApiResponse}from"next";interfaceData{message?:string;status?:number;count?:number;}///exportdefaultasyncfunctionhandler(req:NextApiRequest,res:NextApiResponse<Data>):Promise<void>{constslug=req.query.slug.toString();///if(!slug){returnres.status(400).json({message:`invalid slug`});}if(req.method==`POST`){awaitregisterView(slug);}constcount=awaitgetViews(slug);returnres.status(200).json({count:count});}
ViewCounter Component
- view_counter.tsx
importfetcherfrom"lib/fetcher";import{Views}from"lib/types";import{useEffect}from"react";importuseSWRfrom"swr";interfaceProps{slug:string;}constViewCounter=({slug}:Props)=>{const{data}=useSWR<Views>(`/api/views/${slug}`,fetcher);useEffect(()=>{constregisterView=()=>fetch(`/api/views/${slug}`,{method:"POST",});registerView();},[slug]);return(<span>{`${(data?.count??0)>0?data.count.toLocaleString():"–––"} views`}</span>);};exportdefaultViewCounter;
The code of this project lives at :https://github.com/100lvlmaster/100lvlmaster.in
You can find me at:https://100lvlmaster.in
Top comments(2)

- Email
- LocationNavi Mumbai, India.
- Joined
yes
For further actions, you may consider blocking this person and/orreporting abuse