===============
LIBRARY RULES
===============
From library maintainers:
- ARCHITECTURE OVERVIEW: Nextbase is a comprehensive SaaS boilerplate built with Next.js App Router, Supabase, Stripe, TypeScript. It provides authentication, payments, admin panel, real-time features, and workspace management out of the box
- AUTHENTICATION SYSTEM: Uses Supabase Auth with multiple methods - email/password, magic links, social providers (Google/GitHub/Twitter), and API keys via Unkey. All authentication uses server actions and proper session management
- PAYMENT INTEGRATION: Stripe is fully integrated with subscription management, webhooks, customer portal, and checkout sessions. All payment operations use server actions for security
- ADMIN PANEL (Pro/Ultimate only): Comprehensive admin interface for managing users, billing, blog posts, changelogs, feedback, and roadmaps. Role-based access control with application admin privileges
- DATABASE & MIGRATIONS: Uses Supabase PostgreSQL with Row Level Security (RLS) policies. Database schema changes are managed through Supabase CLI migrations with version control
- FOLDER STRUCTURE - docs-v3/admin-panel/: Contains 6 files managing admin panel features - blog.md (markdown blog system with images/tags), changelog.md (version history), feedback.md (user feedback management), make-user-app-admin.md (granting admin privileges), roadmap.md (product planning), setting-up-payments.md (payment configuration)
- FOLDER STRUCTURE - docs-v3/authentication/: Contains 5 files covering all auth methods - overview.md (supported methods summary), login-with-api-key.md (Unkey integration), login-with-magic-link.md (passwordless auth), login-with-password.md (traditional auth), login-with-social-providers.md (OAuth providers)
- FOLDER STRUCTURE - docs-v3/development-updates/: Contains 2 files for project maintenance - code-and-migration-updates.md (handling updates), version-control.md (Git workflows)
- FOLDER STRUCTURE - docs-v3/guides/: Contains 19 comprehensive tutorials covering - admin-panel.md, asynchronous-data-fetching.md (Server Components), authentication.md, creating-application-admin-page.md, creating-docs-page.md, creating-in-app-notifications.md (real-time notifications), creating-new-supabase-table.md (database management), creating-protected-page.md (route protection), creating-public-dynamic-page.md, creating-public-static-page.md, handling-realtime-data-with-supabase.md, managing-users.md, mutating-data-with-supabase.md, optimizing-data-operations-nextjs-supabase.md, removing-internationlization.md, row-level-security.md, stripe-subscriptions.md, writing-integration-tests.md (Playwright), writing-unit-tests.md (Vitest)
- FOLDER STRUCTURE - docs-v3/installation/: Contains 6 setup guides - dev-environment.md, first-steps.md (cloning and setup), login.md, setting-up-stripe.md (payment integration), setting-up-supabase-locally.md (local development), supabase-for-production.md (production deployment)
- FOLDER STRUCTURE - docs-v3/integrations/: Contains 2 third-party integrations - api-key-authentication-with-unkey.md (API key management), resend-and-react-email-integration.md (email system)
- FOLDER STRUCTURE - docs-v3/known-issues/: Contains 3 troubleshooting files - build-error-undici.md, fetch-failed-build-error-undici.md, supabase-get-session-warning.md (common development issues)
- FOLDER STRUCTURE - docs-v3/payments/: Contains 2 payment system files - stripe-configuration.md (setup and configuration), stripe-webhooks.md (event handling)
- FOLDER STRUCTURE - docs-v3/performance/: Contains 2 optimization guides - strategies-for-pages.md (rendering strategies), streaming-suspense-rendering.md (React Suspense)
- FOLDER STRUCTURE - docs-v3/security/: Contains 3 security guides - access-control-on-supabase-functions.md, protected-nextjs-areas.md (route protection), row-level-security.md (database security)
- FOLDER STRUCTURE - docs-v3/troubleshooting/: Contains 1 file - login-callback-wrong.md (authentication issues)
- FOLDER STRUCTURE - docs-v3/ui/: Contains 5 UI framework files - dark-mode.md (theme switching), nprogress.md (loading indicators), shadcn-ui.md (component system), sonner.md (toast notifications), tailwindcss.md (styling system)
- FOLDER STRUCTURE - features/: Contains 22 feature description files - admin-panel.md, authentication.md, built-in-dark-theme.md, built-with-shadcn-ui.md, changelog-manager.md, dalle-integration.md (AI image generation), feedback-manager.md, fumadocs-integration.md (documentation), nextbase-ultimate.md, open-ai-integration.md, payments.md, roadmap-manager.md, role-based-access.md, sentry-integration.md (error tracking), seo-optimized-blog.md, stripe-integration.md, supports-framer-pages.md, tailwindcss-integration.md, typescript-integrated.md, unkey-integration.md, user-impersonation.md, workspaces.md (multi-tenant architecture)
- SERVER COMPONENTS PATTERN: Always prefer Server Components for data fetching - they provide better performance, SEO, and security. Use 'use client' only when necessary for interactivity
- TWO-TIER ROUTE PROTECTION: Nextbase implements middleware protection (fast cookie-based check) and layout protection (thorough database validation). Always add new protected routes to middleware.ts protectedPaths array
- DATA FETCHING PATTERNS: Use server actions for mutations, React Query for client-side data, and proper Suspense boundaries for loading states. All async operations should be wrapped in try-catch blocks
- REAL-TIME FEATURES: Supabase Realtime is integrated for live notifications and data updates. Use proper channel subscriptions and cleanup to prevent memory leaks
- FORM HANDLING: All forms use server actions with Zod validation. Error handling provides field-level feedback using the useAction hook pattern
- TESTING STRATEGY: Playwright for E2E tests with authenticated/unauthenticated user scenarios. Vitest for unit tests. GitHub Actions for CI/CD automation
- TYPE SAFETY: Strict TypeScript configuration with auto-generated types from Supabase schema. All database operations are fully typed
- EMAIL SYSTEM: Resend integration with React Email templates for transactional emails. All email operations use server actions
- WORKSPACE ARCHITECTURE: Multi-tenant system with organization-based isolation. Users can belong to multiple workspaces with role-based permissions
- INTERNATIONALIZATION: Built-in i18n support that can be removed if not needed. Proper locale handling throughout the application
- PERFORMANCE OPTIMIZATION: Implements streaming, Suspense, parallel data fetching, and proper caching strategies for optimal performance
- PRODUCTION DEPLOYMENT: Comprehensive guides for Supabase production setup, environment variable management, and secure deployment practices
- ERROR HANDLING: Integrated Sentry for error tracking and monitoring. Proper error boundaries and user-friendly error messages
- ANALYTICS INTEGRATION: PostHog and Google Analytics integration for user behavior tracking and business intelligence
- AI INTEGRATIONS: OpenAI and DALL-E integration for AI-powered features in Ultimate version
- API SECURITY: All API routes implement proper authentication and authorization checks using Supabase RLS and custom middleware
### Launch Supabase Locally with pnpm
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/quick-setup.md
This command starts the Supabase services locally. Ensure Docker is installed and running on your system as it's a prerequisite for Supabase.
```pnpm
pnpm supabase start
```
--------------------------------
### Install Nextbase Dependencies with pnpm
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/quick-setup.md
This command installs all necessary project dependencies using pnpm, preparing the environment for development.
```pnpm
pnpm
```
--------------------------------
### Start Nextbase Application with pnpm
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/quick-setup.md
This command launches the Nextbase application in development mode, making it accessible at http://localhost:3000. It enables local development with Supabase and Next.js.
```pnpm
pnpm dev
```
--------------------------------
### Clone NextBase Private Repository
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/getting-started.md
This command is used to clone your purchased private NextBase repository from GitHub. It is strongly advised to clone the repository instead of forking it to ensure the privacy and integrity of your code.
```Bash
git clone https://github.com/imbhargav5/nextbase-pro.git
```
--------------------------------
### Terminal Output for Supabase Local Server Start
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/installation/setting-up-supabase-locally.md
This snippet shows the typical console output after executing `pnpm supabase start`, indicating a successful launch of the local Supabase development setup. It provides crucial access points such as the API URL, GraphQL URL, Database URL, Studio URL, Inbucket URL, JWT secret, and local development keys.
```shell
Status: Downloaded newer image for public.ecr.aws/supabase/studio
Started supabase local development setup.
API URL: http://localhost:54321
GraphQL URL: http://localhost:54321/graphql/v1
DB URL: postgresql://postgres:postgres@localhost:54322/postgres
Studio URL: http://localhost:54323
Inbucket URL: http://localhost:54324
JWT secret: super-secret-jwt-token-with-at-least-32-characters-long
anon key: anon-key-for-local-development
service_role key: service-role-key-for-local-development
```
--------------------------------
### Next.js Streaming Rendering with Server Components and Suspense
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/asynchronous-data-fetching.md
This example demonstrates how Next.js leverages Server Components and React Suspense for streaming rendering. By wrapping data-fetching components (Tags, BlogList) in Suspense, the page can render incrementally, improving user experience by displaying content as soon as it's available without waiting for all data.
```TypeScript
import { T } from '@/components/ui/Typography';
import { PublicBlogList } from '../PublicBlogList';
import { TagsNav } from '../TagsNav';
import { Suspense } from 'react';
import {
anonGetAllBlogTags,
anonGetPublishedBlogPosts,
} from '@/data/anon/internalBlog';
export const metadata = {
title: 'Blog List | Nextbase',
description: 'Collection of the latest blog posts from the team at Nextbase',
icons: {
icon: '/images/logo-black-main.ico',
},
};
async function Tags() {
const tags = await anonGetAllBlogTags();
return ;
}
async function BlogList() {
const blogPosts = await anonGetPublishedBlogPosts();
return ;
}
export default async function BlogListPage() {
return (
BlogAll blog posts
Here is a collection of the latest blog posts from the team at
Nextbase.
Loading tags...}>
Loading posts...}>
);
}
```
--------------------------------
### Example Supabase Migration File Content (PostgreSQL)
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/creating-new-supabase-table.md
Illustrates the content of a Supabase migration file, showing the PostgreSQL `CREATE TABLE` statement for a `todos` table. This file is executed to apply schema changes to the database.
```PostgreSQL
-- This is a migration file (e.g., 20240101000000_create_todos_table.sql)
CREATE TABLE todos (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
title TEXT,
is_completed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL
);
```
--------------------------------
### Initialize Stripe Client in TypeScript
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/stripe-subscriptions.md
This code demonstrates the initialization of the Stripe client in `src/utils/stripe.ts`. It imports the `Stripe` library and uses the `STRIPE_SECRET_KEY` to create a new Stripe instance, configured with a specific API version for interacting with Stripe services.
```TypeScript
import { Stripe } from 'stripe';
import { STRIPE_SECRET_KEY } from '../config';
export const stripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: '2020-08-27',
});
```
--------------------------------
### Example Vitest Unit Test for a Helper Function
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/writing-unit-tests.md
This example demonstrates how to write a basic unit test for a helper function in Vitest, located in `tests/helpers.test.ts`. It uses `expect` and `test` from Vitest to verify that the `toDateTime` function correctly returns a `Date` object.
```TypeScript
import { expect, test } from 'vitest';
import { toDateTime } from './helpers';
test('helpers: toDateTime should return a date object', () => {
const date = new Date();
const dateObject = toDateTime(date.getTime());
expect(dateObject).toBeInstanceOf(Date);
});
```
--------------------------------
### Playwright Test Configuration
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/writing-integration-tests.md
This configuration file (`playwright.config.js`) defines the setup for Playwright end-to-end tests. It specifies test timeouts, the test directory, retry logic, output directory for artifacts, and how to launch the development web server. It also configures different test projects for authenticated and unauthenticated users, leveraging a global setup script for authentication.
```TypeScript
import { PlaywrightTestConfig, devices } from '@playwright/test';
import path from 'path';
// Use process.env.PORT by default and fallback to port 3000
const PORT = process.env.PORT || 3000;
// Set webServer.url and use.baseURL with the location of the WebServer respecting the correct set port
const baseURL = `http://localhost:${PORT}`;
// Reference: https://playwright.dev/docs/v3/test-configuration
const config: PlaywrightTestConfig = {
// Timeout per test
timeout: 120 * 1000,
// Test directory
testDir: path.join(__dirname, 'e2e'),
// If a test fails, retry it additional 2 times
retries: 2,
// Artifacts folder where screenshots, videos, and traces are stored.
outputDir: 'test-results/',
// Run your local dev server before starting the tests:
// https://playwright.dev/docs/v3/test-advanced#launching-a-development-web-server-during-the-tests
webServer: {
command: 'cross-env NODE_ENV=test next dev',
url: baseURL,
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
},
use: {
// Use baseURL so to make navigations relative.
// More information: https://playwright.dev/docs/v3/api/class-testoptions#test-options-base-url
baseURL,
// Retry a test if its failing with enabled tracing. This allows you to analyse the DOM, console logs, network traffic etc.
// More information: https://playwright.dev/docs/v3/trace-viewer
trace: 'retry-with-trace',
actionTimeout: 60 * 1000,
// All available context options: https://playwright.dev/docs/v3/api/class-browser#browser-new-context
// contextOptions: {
// ignoreHTTPSErrors: true,
// },
},
projects: [
{
name: 'with-auth',
testMatch: 'auth/**/*.setup.ts',
},
{
name: 'Logged In Users (Desktop Chrome)',
testMatch: /.*user.spec.ts/,
retries: 0,
dependencies: ['with-auth'],
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json',
},
},
{
name: 'Logged Out Users (Desktop Chrome)',
testIgnore: /.*user.spec.ts/,
use: {
...devices['Desktop Chrome'],
},
}
],
globalSetup: './playwright/global-setup.ts',
};
export default config;
```
--------------------------------
### Parallel Data Fetching and Streaming with React Suspense in Next.js
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/performance/strategies-for-pages.md
This example illustrates using React's Suspense component to fetch multiple data sources in parallel and render child components as soon as data is available. This technique allows the page to start rendering without waiting for all data, improving perceived performance and enabling streaming rendering. The blog list page fetches posts and tags concurrently.
```TypeScript
import { T } from '@/components/ui/Typography';
import {
anonGetAllBlogTags,
anonGetPublishedBlogPosts,
} from '@/data/anon/internalBlog';
import { Suspense } from 'react';
import { PublicBlogList } from '../PublicBlogList';
import { TagsNav } from '../TagsNav';
export const metadata = {
title: 'Blog List | Nextbase',
description: 'Collection of the latest blog posts from the team at Nextbase',
icons: {
icon: '/images/logo-black-main.ico',
},
};
async function Tags() {
const tags = await anonGetAllBlogTags();
return ;
}
async function BlogList() {
const blogPosts = await anonGetPublishedBlogPosts();
return ;
}
export default async function BlogListPage() {
return (
BlogAll blog posts
Here is a collection of the latest blog posts from the team at
Nextbase.
Loading tags...}>
Loading posts...}>
);
}
```
--------------------------------
### Example Admin Panel Page Component
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/creating-application-admin-page.md
This snippet demonstrates a basic page component that can be placed within the Nextbase admin panel structure. It serves as a placeholder for your specific admin page content and will be rendered under the protected admin layout.
```TypeScript
export default async function YourPageName() {
// Your page content goes here
return
// Your JSX goes here
;
}
```
--------------------------------
### Automate Vitest Tests with GitHub Actions
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/writing-unit-tests.md
This GitHub Actions workflow automates the execution of Vitest tests on every push or pull request to the repository. It sets up the environment, installs project dependencies using pnpm, and then runs the `pnpm test` command to ensure code quality and prevent regressions.
```YAML
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm test
```
--------------------------------
### Configure Stripe API Keys in .env
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/stripe-subscriptions.md
This snippet shows how to store your Stripe public and secret API keys in the `.env` file. These keys are essential for authenticating requests to the Stripe API.
```dotenv
STRIPE_PUBLIC_KEY=your_stripe_public_key
STRIPE_SECRET_KEY=your_stripe_secret_key
```
--------------------------------
### Create Stripe Customer Portal Link in TypeScript
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/stripe-subscriptions.md
This asynchronous server action generates a URL for the Stripe customer portal. It fetches organization details, ensures a Stripe customer exists for the organization, and then creates a Stripe billing portal session, returning its URL. This allows users to manage their billing and subscription details directly via Stripe.
```TypeScript
export async function createCustomerPortalLinkAction(organizationId: string) {
'use server';
const user = await serverGetLoggedInUser();
const supabaseClient = createSupabaseUserServerActionClient();
const { data, error } = await supabaseClient
.from('organizations')
.select('id, title')
.eq('id', organizationId)
.single();
if (error) {
throw error;
}
if (!data) {
throw new Error('Organization not found');
}
const customer = await createOrRetrieveCustomer({
organizationId: organizationId,
organizationTitle: data.title,
email: user.email || '',
});
if (!customer) throw Error('Could not get customer');
const { url } = await stripe.billingPortal.sessions.create({
customer,
return_url: toSiteURL(`/organization/${organizationId}/settings/billing`),
});
return url;
}
```
--------------------------------
### Implement Stripe Webhook Handler in Next.js API
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/stripe-subscriptions.md
This TypeScript code defines a Next.js API route (`src/pages/api/stripe/webhook.ts`) that acts as a Stripe webhook handler. It processes incoming Stripe events such as `product.created`, `price.updated`, and `customer.subscription.deleted` to manage product, price, and subscription records in a database. The handler requires the raw request body for signature verification and relies on environment variables for the webhook secret. Proper setup in the Stripe dashboard is crucial for its operation.
```TypeScript
import { errors } from '@/utils/errors';
import { stripe } from '@/utils/stripe';
import {
manageSubscriptionStatusChange,
upsertPriceRecord,
upsertProductRecord,
} from '@/utils/supabase-admin';
import { NextApiRequest, NextApiResponse } from 'next';
import { Readable } from 'node:stream';
import Stripe from 'stripe';
// Stripe requires the raw body to construct the event.
export const config = {
api: {
bodyParser: false,
},
};
async function buffer(readable: Readable) {
const chunks: Array = [];
for await (const chunk of readable) {
chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
}
return Buffer.concat(chunks);
}
const relevantEvents = new Set([
'product.created',
'product.updated',
'price.created',
'price.updated',
'checkout.session.completed',
'customer.subscription.created',
'customer.subscription.updated',
'customer.subscription.deleted',
]);
/**
* Webhook handler which receives Stripe events and updates the database.
* Events handled are product.created, product.updated, price.created, price.updated,
* checkout.session.completed, customer.subscription.created, customer.subscription.updated.
*
* IMPORTANT! Make sure that when your webshite is deployed, the webhook secret is set in the environment variables and
* that the webhook is set up in the Stripe dashboard.
*/
const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'POST') {
const buf = await buffer(req);
const sig = req.headers['stripe-signature'];
const webhookSecret =
process.env.STRIPE_WEBHOOK_SECRET_LIVE ??
process.env.STRIPE_WEBHOOK_SECRET;
let event: Stripe.Event;
try {
if (!sig || !webhookSecret) return;
event = stripe.webhooks.constructEvent(buf, sig, webhookSecret);
} catch (error: unknown) {
errors.add(error);
if (error instanceof Error) {
return res.status(400).send(`Webhook error: ${error.message}`);
}
return res.status(400).send(`Webhook Error: ${String(error)}`);
}
if (relevantEvents.has(event.type)) {
try {
switch (event.type) {
case 'product.created':
case 'product.updated':
await upsertProductRecord(event.data.object as Stripe.Product);
break;
case 'price.created':
case 'price.updated':
await upsertPriceRecord(event.data.object as Stripe.Price);
break;
case 'customer.subscription.created':
case 'customer.subscription.updated':
case 'customer.subscription.deleted': {
const subscription = event.data.object as Stripe.Subscription;
await manageSubscriptionStatusChange(
subscription.id,
subscription.customer as string,
event.type === 'customer.subscription.created',
);
break;
}
case 'checkout.session.completed': {
const checkoutSession = event.data
.object as Stripe.Checkout.Session;
if (checkoutSession.mode === 'subscription') {
const subscriptionId = checkoutSession.subscription;
await manageSubscriptionStatusChange(
subscriptionId as string,
checkoutSession.customer as string,
true,
);
}
break;
}
default:
throw new Error('Unhandled relevant event!');
}
} catch (error) {
errors.add(error);
return res
.status(400)
.send('Webhook error: "Webhook handler failed. View logs."');
}
}
res.json({ received: true });
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
};
export default webhookHandler;
```
--------------------------------
### Start React Email Development Server Command
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/integrations/resend-and-react-email-integration.md
This command is used to execute the `email:dev` script defined in `package.json`, which launches the React Email development server. This server provides a local environment for previewing and developing email templates.
```Shell
pnpm email-dev
```
--------------------------------
### Using Password Authentication in a React Component
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/authentication/login-with-password.md
This client-side example demonstrates how to integrate the `signUpWithPasswordAction` server action into a React component using the `useAction` hook. It shows how to handle execution, success, and error states, and how to pass data to a UI component for form submission.
```TypeScript
const signUpWithPasswordMutation = useAction(signUpWithPasswordAction, {
onExecute: () => {},
onSuccess: () => {},
onError: ({ error }) => {}
});
// In your component JSX
{
passwordMutation.mutate(data);
}}
view="sign-in"
/>;
```
--------------------------------
### Store Stripe API Keys as Environment Variables
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/installation/setting-up-stripe.md
This snippet illustrates the format for storing your Stripe Secret and Publishable API keys as environment variables. These keys are crucial for authenticating requests to the Stripe API and must be kept secure. The example shows test keys, indicated by `sk_test_` for the secret key and `pk_test_` for the publishable key.
```Shell
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_51...
```
--------------------------------
### Create Stripe Checkout Session for Subscriptions in TypeScript
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/stripe-subscriptions.md
This asynchronous server action initiates a Stripe checkout session for a new subscription. It supports both standard and trial subscriptions (14 days). The function requires an organization ID and a Stripe price ID, handles customer retrieval, and configures payment methods, billing address collection, and success/cancel URLs for the checkout flow.
```TypeScript
export async function createCheckoutSessionAction({
organizationId,
priceId,
isTrial = false,
}: {
organizationId: string;
priceId: string;
isTrial?: boolean;
}) {
'use server';
const TRIAL_DAYS = 14;
const user = await serverGetLoggedInUser();
const organizationTitle = await getOrganizationTitle(organizationId);
const customer = await createOrRetrieveCustomer({
organizationId: organizationId,
organizationTitle: organizationTitle,
email: user.email || '',
});
if (!customer) throw Error('Could not get customer');
if (isTrial) {
const stripeSession = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
billing_address_collection: 'required',
customer,
line_items: [
{
price: priceId,
quantity: 1,
},
],
mode: 'subscription',
allow_promotion_codes: true,
subscription_data: {
trial_period_days: TRIAL_DAYS,
trial_settings: {
end_behavior: {
missing_payment_method: 'cancel',
},
},
metadata: {},
},
success_url: toSiteURL(
`/organization/${organizationId}/settings/billing`,
),
cancel_url: toSiteURL(`/organization/${organizationId}/settings/billing`),
});
return stripeSession.id;
} else {
const stripeSession = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
billing_address_collection: 'required',
customer,
line_items: [
{
price: priceId,
quantity: 1,
},
],
mode: 'subscription',
allow_promotion_codes: true,
subscription_data: {
trial_settings: {
end_behavior: {
missing_payment_method: 'cancel',
},
},
},
metadata: {},
success_url: toSiteURL(
`/organization/${organizationId}/settings/billing`,
),
cancel_url: toSiteURL(`/organization/${organizationId}/settings/billing`),
});
return stripeSession.id;
}
}
```
--------------------------------
### Type-Checking Supabase Queries with Generated Types
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/creating-new-supabase-table.md
This TypeScript example demonstrates how to integrate generated database types with the Supabase client. It sets up a type-safe Supabase admin client and performs a query to retrieve 'todos', showcasing how the `data` variable is automatically typed based on the database schema for enhanced development experience.
```TypeScript
// The Database type is generated from your database schema by supabase.
import { Database } from '@/lib/database.types';
import { createClient } from '@supabase/supabase-js';
// A simple wrapper around the Supabase client that uses the service role key
// Since Database is passed as a generic, the client will be typed correctly.
export const supabaseAdminClient = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY,
);
export async function getAllTodos() {
const { data, error } = await supabaseAdminClient.from('todos').select('*');
/**
* The `data` will be typed as `Database['public']['Tables']['todos']['Row'][]`.
* It will have the following shape:
* {
* id: number;
* title: string;
* is_completed: boolean;
* created_at: string;
* }[]
*/
if (error) {
throw error;
}
return data;
}
```
--------------------------------
### Playwright E2E Test Example: Create and Update Organization
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/writing-integration-tests.md
A comprehensive Playwright test case demonstrating user-authenticated interactions to create a new organization and then update its title. It covers navigation, form filling, button clicks, and URL/text assertions.
```TypeScript
test('create organization works correctly', async ({ page }) => {
// Start from the index page (the baseURL is set via the webServer in the playwright.config.ts)
await page.goto('/dashboard');
// click button with role combobox and data-name "organization-switcher"
await page.click(
'button[role="combobox"][data-name="organization-switcher"]',
);
// click button with text New Organization
await page.click('button:has-text("New Organization")');
// wait for form within a div role dialog to show up with data-testid "create-organization-form"
const form = await page.waitForSelector(
'div[role="dialog"] form[data-testid="create-organization-form"]',
);
// find input with name "name" and type "Lorem Ipsum"
const input = await form.waitForSelector('input[name="name"]');
if (!input) {
throw new Error('input not found');
}
await input.fill('Lorem Ipsum');
// click on button with text "Create Organization"
const submitButton = await form.waitForSelector(
'button:has-text("Create Organization")',
);
if (!submitButton) {
throw new Error('submitButton not found');
}
await submitButton.click();
// wait for url to change to /organization/
let organizationId;
await page.waitForURL((url) => {
const match = url
.toString()
.match(
//organization/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})/
);
if (match) {
organizationId = match[1];
return true;
}
return false;
});
if (!organizationId) {
throw new Error('organizationId creation failed');
}
// go to /organization//settings
const settingsPageURL = `/organization/${organizationId}/settings`;
await page.goto(settingsPageURL);
// wait for data-testid "edit-organization-title-form"
const editOrganizationTitleForm = await page.waitForSelector(
'form[data-testid="edit-organization-title-form"]',
);
// fill input with name "organization-title"
const titleInput = await editOrganizationTitleForm.waitForSelector(
'input[name="organization-title"]',
);
if (!titleInput) {
throw new Error('titleInput not found');
}
await titleInput.fill('Lorem Ipsum 2');
// click on button with text "Update"
const updateButton = await editOrganizationTitleForm.waitForSelector(
'button:has-text("Update")',
);
if (!updateButton) {
throw new Error('updateButton not found');
}
await updateButton.click();
// wait for text "Organization title updated!"
await page.waitForSelector('text=Organization title updated!');
});
```
--------------------------------
### Configure React Email Development Server Script in package.json
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/integrations/resend-and-react-email-integration.md
This snippet shows the `email:dev` script configuration within the `scripts` section of a `package.json` file. This script is used to start the React Email development server, enabling developers to preview email templates interactively in a browser.
```JSON
{
"scripts": {
// Other scripts...
"email:dev": "email dev"
}
}
```
--------------------------------
### Creating Protected Route Handlers with Unkey API Key Authentication
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/integrations/api-key-authentication-with-unkey.md
This example shows how to create a Nextbase route handler protected by Unkey's `withUnkeyAuth` middleware. The middleware wraps the asynchronous request handler, ensuring that only requests with valid API keys can access the enclosed logic and receive a successful response.
```javascript
// Example: Creating a route handler with API key authentication
import { withUnkeyAuth } from 'unkey';
export default withUnkeyAuth(async (req, res) => {
// Your protected route logic goes here
res
.status(200)
.json({ message: 'Authenticated route accessed successfully!' });
});
```
--------------------------------
### Client-Side Usage of Social Provider Login
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/authentication/login-with-social-providers.md
This example demonstrates how to integrate the `signInWithProvider` server action into a client-side React component. It uses a `useToastMutation` hook to manage the asynchronous login request, providing loading, success, and error messages. The `RenderProviders` component then uses this mutation to trigger social logins.
```TypeScript
const providerMutation = useToastMutation(
async (provider: AuthProvider) => {
return signInWithProvider(provider, next);
},
{
loadingMessage: 'Requesting login...',
successMessage: 'Redirecting...',
errorMessage: 'Failed to login',
},
);
// In your component JSX
;
```
--------------------------------
### Example: Next.js Undici `fetch failed` Build Error Log
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/known-issues/fetch-failed-build-error-undici.md
This code snippet displays a typical `TypeError: fetch failed` error message and stack trace encountered during Next.js application deployment, which often arises when `localhost` URLs are used in production environment configurations.
```JavaScript
error validating user TypeError: fetch failed
at Object.fetch (/var/task/node_modules/next/dist/compiled/undici/index.js:1:26684)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async validateUser (/var/task/.next/server/app/page.js:740:21)
at async HomePage (/var/task/.next/server/app/page.js:674:17) {
cause: TypeError: Cannot read properties of undefined (reading 'reason')
at makeAppropriateNetworkError (/var/task/node_modules/next/dist/compiled/undici/index.js:2:54604)
at schemeFetch (/var/task/node_modules/next/dist/compiled/undici/index.js:2:30000)
at /var/task/node_modules/next/dist/compiled/undici/index.js:2:28607
at mainFetch (/var/task/node_modules/next/dist/compiled/undici/index.js:2:29007)
at httpRedirectFetch (/var/task/node_modules/next/dist/compiled/undici/index.js:2:33708)
at httpFetch (/var/task/node_modules/next/dist/compiled/undici/index.js:2:32580)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async schemeFetch (/var/task/node_modules/next/dist/compiled/undici/index.js:2:30793)
at async /var/task/node_modules/next/dist/compiled/undici/index.js:2:28601
at async mainFetch (/var/task/node_modules/next/dist/compiled/undici/index.js:2:28426)
```
--------------------------------
### Call Next.js Server Action using useToastMutation Hook
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/mutating-data-with-supabase.md
This example shows how to use the `useToastMutation` hook, a wrapper around `react-query`'s `useMutation`, to call a server action. It automatically displays toast notifications for loading, success, and error states, simplifying UI feedback for data mutations.
```TypeScript
'use client'
import { useToastMutation } from '@/hooks/useToastMutation';
function MyForm(){
const { mutate } = useToastMutation(async (formData: FormData) => {
return await hello(formData);
}, {
onSuccess: (data) => {
const { name, email } = data;
// Do something with the data
},
loadingMessage: 'Calling function...',
successMessage: 'Successful!',
errorMessage: 'Something went wrong!',
});
return
}
```
--------------------------------
### Initialize Resend Client with API Key and SMTP Configuration
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/integrations/resend-and-react-email-integration.md
This snippet demonstrates how to initialize the Resend client in a Next.js application. It imports the `Resend` library and an example `supabase` configuration, then sets up the Resend client using an API key from environment variables and SMTP details from the `supabase` object. The initialized `resend` object is then exported for use throughout the application.
```TypeScript
import { Resend } from 'resend';
import { supabase } from './supabase'; // Example supabase configuration
const resend = new Resend({
apiKey: process.env.RESEND_API_KEY,
// Other Resend configurations...
smtp: {
host: supabase.smtpHost,
port: supabase.smtpPort,
secure: supabase.smtpSecure,
auth: {
user: supabase.smtpUser,
pass: supabase.smtpPassword,
}
}
});
export { resend };
```
--------------------------------
### Example React Email Templates Folder Structure
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/integrations/resend-and-react-email-integration.md
This snippet illustrates the typical directory structure for React Email components within a Nextbase project. The `src/emails` folder serves as a repository for various email templates, each represented by a `.tsx` file (e.g., `welcome.tsx`, `SignInEmail.tsx`), which are React components for rendering email content.
```Text
src
|-- emails
| |-- welcome.tsx
| |-- SignInEmail.tsx
| |-- TeamInvitation.tsx
| |-- ...
```
--------------------------------
### Client-Side Data Fetching using React Query in Next.js
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/asynchronous-data-fetching.md
This snippet illustrates the recommended approach for data fetching in client components using useQuery from @tanstack/react-query. It's typically used for specific client-side needs, such as checking user authentication status, rather than primary page data loading handled by server components.
```TypeScript
'use client';
import { supabaseUserClientComponentClient } from '@/supabase-clients/user/supabaseUserClientComponentClient';
import { useQuery } from '@tanstack/react-query';
export function LoginCTAButton() {
const { data: isLoggedIn, isFetching } = useQuery(
['isLoggedInHome'],
async () => {
const response = await supabaseUserClientComponentClient.auth.getUser();
return Boolean(response.data.user?.id);
},
{
// options
},
);
// if logged in , show dashboard button else show login button
// ...
}
```
--------------------------------
### Asynchronous Data Fetching in Next.js Server Components
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/performance/strategies-for-pages.md
This snippet demonstrates how Next.js server components can execute asynchronously to fetch data on the server before rendering. This approach reduces bundle sizes and leverages server CPU power for faster data fetching. The example shows an async function `PlannedCards` fetching roadmap feedback data.
```TypeScript
async function PlannedCards() {
const plannedCards = await anonGetPlannedRoadmapFeedbackList();
return (
);
}
```
--------------------------------
### Fetch Blog Post Data in Next.js Server Component
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/asynchronous-data-fetching.md
This Next.js page component (`src/app/(external-pages)/blog/[slug]/page.tsx`) demonstrates asynchronous data fetching within a server component. It uses `generateStaticParams` for static site generation, `generateMetadata` for SEO, and the default export function to fetch and render a blog post based on its slug. Data is fetched using `anonGetPublishedBlogPostBySlug`, which is a server action.
```TypeScript
import {
anonGetPublishedBlogPostBySlug,
anonGetPublishedBlogPosts,
} from '@/data/anon/internalBlog';
import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { z } from 'zod';
const paramsSchema = z.object({
slug: z.string(),
});
// Return a list of `params` to populate the [slug] dynamic segment
export async function generateStaticParams() {
const posts = await anonGetPublishedBlogPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
export async function generateMetadata({
params,
}: {
params: unknown;
}): Promise {
// read route params
const { slug } = paramsSchema.parse(params);
const post = await anonGetPublishedBlogPostBySlug(slug);
return {
title: `${post.title} | Blog | Nextbase Boilerplate`,
description: post.summary,
openGraph: {
title: `${post.title} | Blog | Nextbase Boilerplate`,
description: post.summary,
type: 'website',
images: post.cover_image ? [post.cover_image] : undefined,
},
twitter: {
images: post.cover_image ? [post.cover_image] : undefined,
title: `${post.title} | Blog | Nextbase Boilerplate`,
card: 'summary_large_image',
site: '@usenextbase',
description: post.summary,
},
};
}
export default async function BlogPostPage({ params }: { params: unknown }) {
try {
const { slug } = paramsSchema.parse(params);
const post = await anonGetPublishedBlogPostBySlug(slug);
return (
{post.cover_image ? (
) : null}
{post.title}
);
} catch (error) {
return notFound();
}
}
```
--------------------------------
### Update Next.js Middleware for Route Protection
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/creating-protected-page.md
This snippet demonstrates how to manually add a new route to the `protectedPaths` array in `middleware.ts`. It includes an example of custom middleware logic for a specific route, showing how to check for a user session using Supabase and redirect if not authenticated, ensuring a fast, preliminary check for route protection.
```TypeScript
// In middleware.ts
const protectedPaths = [
// ... existing protected paths ...
`/my-protected-page(/.*)?`,
];
const middlewares = [
{
matcher: protectedPaths.map((path) =>
urlJoin('/', `(${LOCALE_GLOB_PATTERN})`, path),
),
middleware: async (req) => {
// Existing middleware logic
},
},
// If you need custom logic for this specific route, you can add it here:
{
matcher: ['/my-protected-page'],
middleware: async (req) => {
// Custom logic for /my-protected-page if needed
// For example, checking for specific user roles
const supabase = createSupabaseMiddlewareClient(req);
const {
data: { session },
} = await supabase.auth.getSession();
if (!session) {
return NextResponse.redirect(new URL('/login', req.url));
}
// Additional checks can be performed here
return NextResponse.next();
},
},
];
```
--------------------------------
### Next.js Page for Organization Billing Details
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/stripe-subscriptions.md
This Next.js `async` page component, `OrganizationSettingsPage`, is responsible for rendering the organization's billing and subscription details. It uses Zod for parameter validation to extract the `organizationId`, then fetches the user's role and normalized subscription data. The `Subscription` sub-component displays these details, wrapped in a `Suspense` boundary for loading states.
```TypeScript
import { z } from 'zod';
import { OrganizationSubscripionDetails } from './OrganizationSubscripionDetails';
import {
getLoggedInUserOrganizationRole,
getNormalizedOrganizationSubscription,
} from '@/data/user/organizations';
import { Suspense } from 'react';
import { T } from '@/components/ui/Typography';
const paramsSchema = z.object({
organizationId: z.string(),
});
async function Subscription({ organizationId }: { organizationId: string }) {
const normalizedSubscription =
await getNormalizedOrganizationSubscription(organizationId);
const organizationRole =
await getLoggedInUserOrganizationRole(organizationId);
return (
);
}
export default async function OrganizationSettingsPage({
params,
}: {
params: unknown;
}) {
const { organizationId } = paramsSchema.parse(params);
return (
Loading billing details...}>
;
);
}
```
--------------------------------
### Navigate into Nextbase Project Directory
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/installation/first-steps.md
After cloning, use this command to change your current working directory into the newly created "nextbase-ultimate" folder, allowing you to access the project files.
```bash
cd nextbase-ultimate
```
--------------------------------
### Clone Nextbase Repository
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/installation/first-steps.md
This command clones the Nextbase project repository from GitHub to your local machine. It creates a new directory named "nextbase-ultimate" containing all project files.
```bash
git clone https://github.com/imbhargav5/nextbase-ultimate.git
```
--------------------------------
### Trigger Stripe Product and Price Events
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/installation/setting-up-stripe.md
These commands simulate product.created and price.created events using the Stripe CLI, allowing developers to test their webhook handlers and ensure product and price synchronization with their database, such as Supabase, during local development.
```bash
stripe trigger product.created
stripe trigger price.created
```
--------------------------------
### Update Nextbase Repository with Latest Changes
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/installation/first-steps.md
Run this command within the "nextbase-ultimate" directory to fetch and merge the latest changes from the remote GitHub repository, ensuring your local copy is up-to-date.
```bash
git pull
```
--------------------------------
### Stripe CLI Login
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/installation/setting-up-stripe.md
This command initiates the login process for the Stripe CLI, prompting the user to authenticate their Stripe account through a web browser. Successful login is required to use other Stripe CLI functionalities.
```bash
stripe login
```
--------------------------------
### Add Navigation Link to Protected Page
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/guides/creating-protected-page.md
This snippet provides an example of how to add a navigation link to the new protected page using a Next.js `Link` component. This is an optional step for user interface integration.
```JSX
// In your navigation component
My Protected Page
```
--------------------------------
### Forward Stripe Webhooks to Local Server
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/installation/setting-up-stripe.md
This command forwards Stripe webhook events from your Stripe account to a specified local endpoint, typically for development and testing purposes. It also provides a webhook signing secret necessary for verifying webhook authenticity.
```bash
stripe listen --forward-to localhost:3000/api/stripe/webhooks
```
--------------------------------
### Supabase Local Development Environment Variables (.env.local)
Source: https://github.com/roubzzz/usenextbase-documentation/blob/main/docs-v3/installation/setting-up-supabase-locally.md
This code block illustrates the default environment variables found in the `.env.local.example` file, essential for running Supabase locally. It includes configurations for Supabase URLs, API keys, database credentials, and placeholders for other services like Stripe, email, and analytics. These values are used by both Supabase and Next.js for local development.
```dotenv
# supabase
# These values never change when supabase is ran locally regardless of project
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321/
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
SUPABASE_DATABASE_PASSWORD=postgres
SUPABASE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long
# SUPABASE_PROJECT_REF=SUPABASE_PROJECT_REF
# stripe
STRIPE_SECRET_KEY=STRIPE_SECRET_KEY
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
STRIPE_WEBHOOK_SECRET=STRIPE_WEBHOOK_SECRET
# host
NEXT_PUBLIC_SITE_URL=http://localhost:3000
# email
ADMIN_EMAIL=admin@myapp.com
RESEND_API_KEY=RESEND_API_KEY
# analytics
# ultimate and pro
NEXT_PUBLIC_POSTHOG_API_KEY=NEXT_PUBLIC_POSTHOG_API_KEY
NEXT_PUBLIC_POSTHOG_APP_ID=NEXT_PUBLIC_POSTHOG_APP_ID
NEXT_PUBLIC_POSTHOG_HOST=NEXT_PUBLIC_POSTHOG_HOST
NEXT_PUBLIC_GA_ID=NEXT_PUBLIC_GA_ID
UNKEY_ROOT_KEY=UNKEY_ROOT_KEY
UNKEY_API_ID=UNKEY_API_ID
## Supabase providers
# this file is only used by supabase configtoml for local development
# next.js uses .env.local for local development
TWITTER_API_KEY=TWITTER_API_KEY
TWITTER_API_SECRET=TWITTER_API_SECRET
GOOGLE_CLIENT_ID=GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET=GOOGLE_CLIENT_SECRET
GITHUB_CLIENT_ID=GITHUB_CLIENT_ID
GITHUB_CLIENT_SECRET=GITHUB_CLIENT_SECRET
```