⌘ Permalink

Real-time page views with Next.js, Turso and Drizzle ORM

Ahmet ALMAZ

Ahmet ALMAZ
4 minutes / 699 words / --- views

If you are looking for an easy and efficient way to add simple real-time page views to your Next.js website then you’re reading the perfect how-to article on this subject. Now, I’ll teach you how to add real-time page views to your Next.js website using Turso and Drizzle ORM.

# What is Turso?

Introducing Turso, the SQLite edge database that will revolutionize your website. Built on the powerful libSQL framework, Turso offers a mind-blowing free-forever plan, providing a whopping 8 GB of total storage and the ability to have up to 3 databases in 3 different locations. Prepare to have your expectations shattered!

# What is Drizzle ORM?

Drizzle ORM, a cutting-edge object-relational mapping library designed specifically for Node.js and TypeScript applications. This powerhouse brings support for multiple databases, migrations, and query building. It’s like having a supercharged engine under the hood of your website!

# Setup Turso database

  1. Install the Turso CLI (For more options Click here):
Terminal window
brew install chiselstrike/tap/turso
  1. Sign up to Turso:
Terminal window
turso auth signup
  1. Create a new database
Terminal window
turso db create [db-name]
  1. Get the URL of your database (Starts with libsql://):
Terminal window
turso db show [db-name]
  1. Access your database shell:
Terminal window
turso db shell [db-name]
  1. Create a new table for views:
CREATE TABLE IF NOT EXISTS views (
slug TEXT PRIMARY KEY,
title TEXT,
count INTEGER
);
  1. Create an auth token
Terminal window
turso db tokens create [db-name] -e none
  1. Now inside .env file, add the following:
.env.local
DATABASE_URL=libsql://[db-url]
DATABASE_AUTH_TOKEN=[auth-token]

# Connect Next.js to Turso

In order to connect our site to the database, we need to use Drizzle ORM. All we have to do is install couple of packages and set them up.

  1. Install Drizzle ORM and libSQL client:
Terminal window
npm i drizzle-orm @libsql/client
  1. Create a file lib/turso.ts to initialize your Turso client
lib/turso.ts
import { createClient } from '@libsql/client'
import { drizzle } from 'drizzle-orm/libsql'
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
const connection = createClient({
url: process.env.DATABASE_URL || '',
authToken: process.env.DATABASE_AUTH_TOKEN,
})
export const db = drizzle(connection)
export const viewsTable = sqliteTable('views', {
slug: text('slug').primaryKey(),
count: integer('count').notNull().default(0),
})
  1. Now we create app/api/views/[slug]/route.ts and use it to increment and fetch page views
app/api/views/[slug]/route.ts
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { db, viewsTable } from '@/lib/turso'
interface Options {
params: {
slug: string
}
}
export const GET = async (request: NextRequest, { params }: Options) => {
const slug = z.string().parse(params.slug)
const data = await db.select().from(viewsTable).where(eq(viewsTable.slug, slug)).all()
const count = !data.length ? 0 : Number(data[0].count)
return NextResponse.json({ count })
}
export const POST = async (request: NextRequest, { params }: Options) => {
const slug = params.slug
const data = await db.select().from(viewsTable).where(eq(viewsTable.slug, slug)).all()
const count = !data.length ? 0 : Number(data[0].count)
await db
.insert(viewsTable)
.values({
slug,
count: 1,
})
.onConflictDoUpdate({
target: viewsTable.slug,
set: {
count: count + 1,
},
})
.returning()
.get()
return NextResponse.json({ count: count + 1 })
}
  1. Finally, we create ViewCounter.tsx component to display the page views which we can use in our blog posts
components/ViewCounter.tsx
'use client'
import { useEffect } from 'react'
import useSWR from 'swr'
import fetcher from '@/lib/fetcher'
import { PostView } from '@/lib/types'
export default function ViewsCounter({ slug, trackView }: { slug: string; trackView: boolean }) {
const { data } = useSWR<PostView>(`/api/views/${slug}`, fetcher)
const views = new Number(data?.count || 0)
useEffect(() => {
const registerView = () => {
fetch(`/api/views/${slug}`, {
method: 'POST',
})
}
if (trackView) {
registerView()
}
}, [slug])
return (
<p className="font-mono text-sm tracking-tighter">
{data ? `${views.toLocaleString()} views` : '--- views'}
</p>
)
}

# Conclusion

Now we have a working page views counter that is connected to our Turso database. You can use this method to add page views to any website that is built with Next.js.

Subscribe to the newsletter