npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

postbasejs

v0.3.6

Published

The official JavaScript client for Postbase — self-hosted backend as a service

Downloads

679

Readme

postbasejs

The official JavaScript/TypeScript client for Postbase — a self-hosted, open-source backend as a service.

npm version license

getpostbase.com · Documentation · GitHub


What is Postbase?

Postbase is a self-hosted backend platform built on PostgreSQL. It gives you a database with a REST query API, authentication (password, magic link, OAuth), file storage, and row-level security — all running on your own infrastructure.

postbasejs is the client SDK for interacting with your Postbase instance from JavaScript or TypeScript apps.


Screenshots


Installation

npm install postbasejs
# or
pnpm add postbasejs
# or
yarn add postbasejs

Quick Start

import { createClient } from 'postbasejs'

const postbase = createClient(
  'https://your-postbase-instance.com',
  'pb_anon_your_api_key',
  { projectId: 'your-project-id' }
)

Your URL, anon key, and project ID can be found in the API Keys section of your Postbase dashboard.


Database

Query your PostgreSQL tables with a fluent, chainable API.

Select

// Fetch all posts
const { data, error } = await postbase.from('posts').select('*')

// Select specific columns
const { data } = await postbase.from('posts').select('id, title, created_at')

// With filters
const { data } = await postbase
  .from('posts')
  .select('*')
  .eq('status', 'published')
  .order('created_at', { ascending: false })
  .limit(10)

// Get total count
const { data, count } = await postbase
  .from('posts')
  .select('*', { count: 'exact' })

Filter operators

| Method | SQL equivalent | |---|---| | .eq(col, val) | col = val | | .neq(col, val) | col != val | | .gt(col, val) | col > val | | .gte(col, val) | col >= val | | .lt(col, val) | col < val | | .lte(col, val) | col <= val | | .like(col, pattern) | col LIKE pattern | | .ilike(col, pattern) | col ILIKE pattern | | .in(col, values) | col IN (values) | | .is(col, null) | col IS NULL | | .contains(col, val) | col @> val | | .overlaps(col, val) | col && val | | .textSearch(col, query) | full-text search | | .or(filters) | col = val OR col = val | | .not(col, op, val) | NOT col op val |

Insert

const { data, error } = await postbase
  .from('posts')
  .insert({ title: 'Hello World', status: 'draft' })
  .select()
  .single()

Update

const { data, error } = await postbase
  .from('posts')
  .update({ status: 'published' })
  .eq('id', 'post-id')
  .select()
  .single()

Upsert

const { data, error } = await postbase
  .from('profiles')
  .upsert({ id: 'user-id', username: 'alice' }, { onConflict: 'id' })
  .select()

Delete

const { error } = await postbase
  .from('posts')
  .delete()
  .eq('id', 'post-id')

Single row helpers

// Errors if not exactly one row
const { data, error } = await postbase.from('posts').select('*').eq('id', id).single()

// Returns null if not found (no error)
const { data } = await postbase.from('posts').select('*').eq('id', id).maybeSingle()

Pagination

// Limit + offset
const { data } = await postbase.from('posts').select('*').limit(20).offset(40)

// Range (inclusive)
const { data } = await postbase.from('posts').select('*').range(0, 19)

Authentication

Sign up

const { data, error } = await postbase.auth.signUp({
  email: '[email protected]',
  password: 'supersecret',
})
// data.user, data.session

Sign in with password

const { data, error } = await postbase.auth.signInWithPassword({
  email: '[email protected]',
  password: 'supersecret',
})

OTP & Magic Link (passwordless)

Magic Link:

const { error } = await postbase.auth.signInWithOtp({
  email: '[email protected]',
  type: 'magic_link', // optional, defaults to 'magic_link'
  options: { redirectTo: 'https://yourapp.com/dashboard' },
})

6-digit OTP Code:

// 1. Request the code
const { error } = await postbase.auth.signInWithOtp({
  email: '[email protected]',
  type: 'otp',
})

// 2. Verify the code
const { data, error } = await postbase.auth.verifyOtp({
  email: '[email protected]',
  token: '123456', // 6-digit code from email
})
// data.user, data.session

OAuth (browser redirect)

await postbase.auth.signInWithOAuth({
  provider: 'google', // or 'github', 'discord', etc.
  options: { redirectTo: 'https://yourapp.com/callback' },
})

Get current user

const { data: { user }, error } = await postbase.auth.getUser()

Get current session

const { data: { session }, error } = await postbase.auth.getSession()
// session.accessToken, session.user, session.expiresAt

Sign out

await postbase.auth.signOut()

Update user

const { data, error } = await postbase.auth.updateUser({
  name: 'Alice',
  metadata: { plan: 'pro' },
})

Listen to auth state changes

const { data: { subscription } } = postbase.auth.onAuthStateChange((event, session) => {
  // event: 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED'
  console.log(event, session)
})

// Cleanup
subscription.unsubscribe()

Admin (service role key required)

const adminClient = createClient(url, 'pb_service_your_service_key', { projectId: 'your-project-id' })

// List users
const { data } = await adminClient.auth.admin.listUsers({ page: 1, perPage: 50 })

// Create user
const { data } = await adminClient.auth.admin.createUser({
  email: '[email protected]',
  password: 'password',
  email_confirm: true,
})

// Update user
await adminClient.auth.admin.updateUserById(userId, { email: '[email protected]' })

// Delete user
await adminClient.auth.admin.deleteUser(userId)

Storage

Upload a file

const { data, error } = await postbase
  .storage
  .from('avatars')
  .upload('user-123.png', file, { contentType: 'image/png' })
// data.path, data.fullPath

Get public URL

const { data: { publicUrl } } = postbase
  .storage
  .from('avatars')
  .getPublicUrl('user-123.png')

Download a file

const { data: blob, error } = await postbase
  .storage
  .from('avatars')
  .download('user-123.png')

Create a signed URL (temporary access)

const { data, error } = await postbase
  .storage
  .from('private-docs')
  .createSignedUrl('report.pdf', 3600) // expires in 1 hour
// data.signedUrl

List files

const { data, error } = await postbase
  .storage
  .from('avatars')
  .list('folder/', { limit: 100, sortBy: { column: 'name', order: 'asc' } })

Delete files

const { error } = await postbase
  .storage
  .from('avatars')
  .remove(['user-123.png', 'user-456.png'])

Move / Copy

await postbase.storage.from('docs').move('old-name.pdf', 'new-name.pdf')
await postbase.storage.from('docs').copy('template.pdf', 'copy.pdf')

Bucket management

// Create
await postbase.storage.createBucket('avatars', {
  public: true,
  fileSizeLimit: 5 * 1024 * 1024, // 5 MB
  allowedMimeTypes: ['image/png', 'image/jpeg'],
})

// List
const { data: buckets } = await postbase.storage.listBuckets()

// Update
await postbase.storage.updateBucket('avatars', { public: false })

// Delete
await postbase.storage.deleteBucket('avatars')

// Empty (delete all objects)
await postbase.storage.emptyBucket('avatars')

RPC (PostgreSQL functions)

Call a stored procedure or function in your project's schema:

const { data, error } = await postbase.rpc('get_nearby_posts', {
  lat: 37.7749,
  lng: -122.4194,
  radius: 10,
})

SSR (Server-Side Rendering)

For Next.js App Router, SvelteKit, Nuxt, or any SSR framework, import from postbasejs/ssr. This forwards the user's session cookie to Postbase so that RLS policies apply server-side.

# No extra install needed — it's included in postbasejs

Next.js App Router

Server Component:

import { createServerClient } from 'postbasejs/ssr'
import { cookies } from 'next/headers'

export default async function Page() {
  const cookieStore = await cookies()

  const postbase = createServerClient(
    process.env.NEXT_PUBLIC_POSTBASE_URL!,
    process.env.NEXT_PUBLIC_POSTBASE_ANON_KEY!,
    {
      projectId: process.env.NEXT_PUBLIC_POSTBASE_PROJECT_ID!,
      cookies: {
        getAll: () => cookieStore.getAll(),
        setAll: () => {}, // read-only in server components
      },
    }
  )

  const { data: posts } = await postbase.from('posts').select('*')
  return <ul>{posts?.map(p => <li key={p.id}>{p.title}</li>)}</ul>
}

Middleware (session refresh):

// middleware.ts
import { createServerClient } from 'postbasejs/ssr'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(req: NextRequest) {
  const res = NextResponse.next()

  const postbase = createServerClient(
    process.env.NEXT_PUBLIC_POSTBASE_URL!,
    process.env.NEXT_PUBLIC_POSTBASE_ANON_KEY!,
    {
      projectId: process.env.NEXT_PUBLIC_POSTBASE_PROJECT_ID!,
      cookies: {
        getAll: () => req.cookies.getAll(),
        setAll: (cookies) =>
          cookies.forEach(c => res.cookies.set(c.name, c.value, c.options as any)),
      },
    }
  )

  await postbase.auth.getSession() // refreshes token if needed
  return res
}

Client Component:

'use client'
import { createBrowserClient } from 'postbasejs/ssr'

const postbase = createBrowserClient(
  process.env.NEXT_PUBLIC_POSTBASE_URL!,
  process.env.NEXT_PUBLIC_POSTBASE_ANON_KEY!,
  { projectId: process.env.NEXT_PUBLIC_POSTBASE_PROJECT_ID! }
)

TypeScript

The SDK is fully typed. Pass your row type as a generic for full IntelliSense:

interface Post {
  id: string
  title: string
  status: 'draft' | 'published'
  created_at: string
}

const { data } = await postbase.from<Post>('posts').select('*').eq('status', 'published')
// data is Post[] | null

Row Level Security (RLS)

When a user is signed in, their session JWT is automatically forwarded with every query. Your RLS policies can reference the user via:

current_setting('postbase.user_id', true)  -- the authenticated user's ID
current_setting('postbase.role', true)     -- the user's role

Example policy — users can only read their own rows:

CREATE POLICY "own rows" ON posts
  FOR SELECT USING (
    user_id = current_setting('postbase.user_id', true)::uuid
  );

Environment Variables

We recommend storing your Postbase credentials in environment variables:

NEXT_PUBLIC_POSTBASE_URL=https://your-postbase-instance.com
NEXT_PUBLIC_POSTBASE_ANON_KEY=pb_anon_...
NEXT_PUBLIC_POSTBASE_PROJECT_ID=your-project-id

Use your service role key (pb_service_...) only in server-side code — it bypasses RLS.


License

MIT — see LICENSE.


Built with love by the Postbase team.