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

next-api-bridge

v0.1.5

Published

Cookie-aware API bridge for Next.js App Router with Server Actions. Handles auth, cookies, and session flow entirely on the server for external backends.

Readme

next-api-bridge

Use Next.js as a real server — not just a React wrapper.

next-api-bridge is a cookie-aware API gateway for Next.js App Router. It sits between your UI and your external backend, handling cookies, headers, auth, and session flow entirely on the server.


The problem

Most Next.js apps with an external backend fall into this pattern:

Client → useEffect → fetch → Backend API

This means tokens in localStorage, broken HttpOnly cookies, duplicated API logic, and a client component that knows too much about your auth.

The solution

Client (UI only)
    ↓
Server Action (Next.js)
    ↓
next-api-bridge
    ↓
External Backend (NestJS · Laravel · Django · Express…)

next-api-bridge workflow

Your client triggers actions. Your server owns the session. Your backend never sees the browser directly.


Install

npm install next-api-bridge

Optional — for toast helpers:

npm install sonner

Requires: Next.js 13+ (App Router), Node.js server environment. Server Actions, Route Handlers, or Server Components only — not for browser fetch.


Quick start

1. Create your API client

// src/server/api.ts
import { createNextApiBridge } from 'next-api-bridge';

export const api = createNextApiBridge({
  baseUrl: process.env.API_URL!,
});

2. Call it from a Server Action

// server/auth/action.ts
'use server';

import { redirect } from 'next/navigation';
import { api } from '@/server/api';
import { getCleanFormData, validateRedirectPath } from 'next-api-bridge/form';

export async function signIn(_prev: unknown, data: FormData) {
  const body = getCleanFormData(data, { delete: ['redirectPath'] });
  const response = await api.post('/auth/login', body);

  if (response.success) redirect(validateRedirectPath(data.get('redirectPath') as string));

  return { formdata: body, ...response };
}

3. Use it in your form

// components/login-form.tsx
'use client';

import { useActionState } from 'react';
import { signIn } from '@/server/auth/action';

export default function LoginForm() {
  const [state, action, isPending] = useActionState(signIn, null);

  return (
    <form action={action}>
      <input name="email" type="email" />
      <input name="password" type="password" />
      <button disabled={isPending}>Sign in</button>
      {state?.message && <p>{state.message}</p>}
    </form>
  );
}

No useEffect. No useState for auth. No token management on the client.


Configuration

createNextApiBridge({
  baseUrl: string;           // Required. Your backend URL.
  cookiePrefix?: string;     // Default: 'nab_'. Namespaces backend cookies in Next.js.
  apiKey?: string;           // Optional API key.
  apiKeyHeader?: string;     // Header name for the API key.
  auth?: BearerAuthConfig;   // Bearer token from a cookie.
  verbose?: string;          // 'request,body,response' for debug logging.
});

Bearer token auth

Reads a token from a cookie and adds it as an Authorization header automatically:

export const api = createNextApiBridge({
  baseUrl: process.env.API_URL!,
  auth: {
    type: 'bearer',
    tokenCookie: 'accessToken', // reads nab_accessToken cookie
    header: 'Authorization',
    prefix: 'Bearer',
  },
});

API key auth

export const api = createNextApiBridge({
  baseUrl: process.env.API_URL!,
  apiKey: process.env.API_KEY,
  apiKeyHeader: 'X-API-Key',
});

API methods

api.get('/users/me');
api.post('/auth/login', body);
api.patch('/users/me', body);
api.put('/settings', body);
api.delete('/sessions/current');

All methods return:

{
  success: boolean;
  message: string;
  body: T | null;
  headers?: Headers;
}

Options

// Query params
api.get('/events', { query: { page: 1, search: 'conf' } });

// Path params
api.get('/events', { params: ['event-id'] });

// Cache control
api.get('/static-data', { cache: 'force-cache' });

// File upload
api.post('/upload', formData, { isMultipart: true });

Cookie behavior

When your backend responds with Set-Cookie: accessToken=abc123, next-api-bridge captures it and stores it in Next.js as nab_accessToken. On the next request, it strips the prefix and forwards Cookie: accessToken=abc123 to your backend transparently.

HttpOnly session cookies work out of the box — no workarounds needed.

Manual cookie management

await api.setCookie('sessionid', 'abc123', { httpOnly: true, maxAge: 3600 });
await api.getCookie('accessToken');
await api.deleteCookies(['accessToken', 'refreshToken']); // or pass nothing to delete all

Form helpers

getCleanFormData replaces Object.fromEntries() with something smarter — it strips empty fields, Next.js internals, and can coerce types:

import { getCleanFormData } from 'next-api-bridge/form';

const body = getCleanFormData(data, {
  delete: ['redirectPath'],
  jsonParse: ['deviceInfo'],
  boolean: ['isActive'],
  number: ['price', 'quantity'],
  date: ['startsAt'],
});

Utility helpers

import {
  validateRedirectPath,   // Returns '/' if path is invalid or external
  buildUrlWithParams,     // Builds '/path?key=value' strings
  reloadPage,             // Revalidates a page after mutation
} from 'next-api-bridge/form';

Toast notifications (optional, requires Sonner)

// In your root layout
import { Toaster } from 'sonner';
<Toaster />

// In a client component
import { showResponseToast, showResponseToastAndReload } from 'next-api-bridge/form';

showResponseToast({ state });
showResponseToastAndReload({ state, path: '/dashboard' });

Server Component data fetching

Server Components can fetch directly — no loading state, no useEffect:

// app/layout.tsx
export default async function RootLayout({ children }) {
  const user = await getUser();
  const { body } = await api.get('/memberships');

  return (
    <html><body>
      <AppProvider user={user} memberships={body?.members ?? []}>
        {children}
      </AppProvider>
    </body></html>
  );
}

When to use next-api-bridge vs direct fetch

| Route | Approach | |---|---| | Auth, forms, mutations | Use next-api-bridge via Server Actions | | Protected data reads | Use next-api-bridge in Server Components | | Fully public / static data | Direct fetch() is fine |


Exports

// Core
import { createNextApiBridge, NextApiBridgeClient } from 'next-api-bridge';
import type { ApiBridgeOptions, ApiBridgeResponse, BearerAuthConfig } from 'next-api-bridge';

// Helpers
import {
  getCleanFormData,
  reloadPage,
  validateRedirectPath,
  buildUrlWithParams,
  showResponseToast,
  showResponseToastAndReload,
} from 'next-api-bridge/form';

License

MIT