Syncing to your own backend

Build a backend API and connect it to Pindrop with a custom adapter

Pindrop stores comments in localStorage by default. To sync them across devices and users, point it at your own API using an adapter.

You need two things: a backend that stores and returns comments, and an adapter that connects Pindrop to it.

What your API needs

Two endpoints are enough:

MethodPathDescription
GET/api/commentsReturn all comments as a JSON array
POST/api/commentsReplace the stored comments with the request body

Pindrop passes the full comment array on every save, so you don't need separate create/update/delete endpoints.

Example: Next.js API route

typescript
// app/api/comments/route.ts
import { NextResponse } from 'next/server'
 
let comments: unknown[] = [] // replace with your database
 
export async function GET() {
  return NextResponse.json(comments)
}
 
export async function POST(req: Request) {
  comments = await req.json()
  return NextResponse.json({ ok: true })
}

Replace the in-memory comments array with a real database write — Postgres, SQLite, MongoDB, whatever you use.

Wire the adapter

typescript
import { Pindrop } from 'pindrop.js'
 
Pindrop.init({
  storageKey: 'my-app',
  adapter: {
    load: async () => {
      const res = await fetch('/api/comments')
      return res.json()
    },
    save: async (comments) => {
      await fetch('/api/comments', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(comments),
      })
    },
  },
})

That's all the adapter needs. Pindrop handles unread state, merging, and UI — you just store and return the array.

Live sync for multiple users

If multiple users share the same page, you can poll your API to pick up comments left by others without a page reload:

typescript
const pindrop = Pindrop.init({ storageKey: 'my-app', adapter })
 
setInterval(async () => {
  const remote = await fetch('/api/comments').then(r => r.json())
  pindrop.applyRemoteComments(remote)
}, 5000)

applyRemoteComments merges remote comments into the local store — new ones appear as unread, existing ones preserve the current user's read state.

Scoping comments to a URL

If your backend serves multiple pages, store comments per URL:

typescript
const url = encodeURIComponent(location.pathname)
 
const adapter = {
  load: async () => {
    const res = await fetch(`/api/comments?url=${url}`)
    return res.json()
  },
  save: async (comments) => {
    await fetch(`/api/comments?url=${url}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(comments),
    })
  },
}

Then filter by url in your API handler.

Last updated March 29, 2026