# Neon Postgres Documentation > Neon is a serverless Postgres platform that separates compute and storage to offer autoscaling, branching, instant restore, and scale-to-zero. It's fully compatible with Postgres and works with any language, framework, or ORM that supports Postgres. > This file contains the full Neon documentation. For a table of contents, see https://neon.com/docs/llms.txt --- [Document source](https://neon.com/docs/ai/agent-skills.md) --- # Agent Skills Teach your AI coding assistant how to work with Neon Agent Skills provide your AI coding assistant with structured context about Neon's features, APIs, and best practices. With skills installed, your assistant produces more accurate code and avoids common mistakes when working with Neon. [Watch on YouTube](https://youtube.com/watch?v=NN251KTjAo8) ## Install There are several ways to install the Neon skill depending on your editor and workflow. ### npx skills For any AI tool that supports the [Agent Skills](https://agentskills.io) format, install the Neon skill directly: ```bash npx skills add neondatabase/agent-skills -s neon-postgres ``` This works with Cursor, Claude Code, and other compatible tools. The `-s` flag selects a specific skill from the repository. Available skills include `neon-postgres` (the main Neon development skill) and `claimable-postgres` (for [disposable databases](https://neon.com/docs/reference/claimable-postgres)). Useful flags: - `-y` skips the interactive prompt and installs immediately - `-g` installs the skill globally instead of at the project level (see [Project-level vs. global install](https://neon.com/docs/ai/agent-skills#project-level-vs-global-install)) ### Cursor plugin If you're using Cursor, install the Neon plugin from the marketplace. It bundles skills and the Neon MCP Server in one package. In Cursor chat, run: ```text /add-plugin neon-postgres ``` Or install from [cursor.com/marketplace/neon](https://cursor.com/marketplace/neon). See [Cursor plugin for Neon](https://neon.com/docs/ai/ai-cursor-plugin) for details. ### Claude Code plugin If you're using Claude Code, install the Neon plugin for skills and MCP integration: ```bash /plugin marketplace add neondatabase/agent-skills /plugin install neon-postgres@neon ``` See [Claude Code plugin for Neon](https://neon.com/docs/ai/ai-claude-code-plugin) for details. ### Codex plugin If you're using OpenAI Codex, install the **Neon Postgres** plugin from the [Codex plugin directory](https://developers.openai.com/codex/plugins/) (in the Codex app under **Plugins**, or in the Codex CLI with `/plugins`). It includes the Neon Postgres app (MCP), the main Neon skill, and the egress optimizer skill. See [Codex plugin for Neon](https://neon.com/docs/ai/ai-codex-plugin) for details. ### neonctl init The `neonctl init` command sets up your project to use Neon with your AI coding assistant. It authenticates via OAuth, creates an API key, configures the MCP server, installs the Neon extension for Cursor and VS Code where applicable, and installs agent skills at the project level: ```bash npx neonctl@latest init ``` After running `init`, restart your editor and ask your AI assistant to "Get started with Neon" to launch the interactive onboarding guide. See the [`neonctl init` reference](https://neon.com/docs/reference/cli-init) for details. ## Project-level vs. global install Skills can be installed at two levels: - **Project level** (default): Skills are installed in your project directory, for example via `neonctl init` or `npx skills add`. Your AI assistant picks them up when working in that project. This is best for team workflows since the configuration can be committed with the project. - **Global**: Skills are installed at the user or system level and available across all projects. Useful for personal development environments where you want Neon context everywhere. Pass the `-g` flag to install globally: ```bash npx skills add neondatabase/agent-skills -s neon-postgres -g ``` ## What's covered The Neon skill provides guidance across the full development workflow: - **Getting started** with Neon, including project setup and key features (branching, autoscaling, scale-to-zero, instant restore, read replicas) - **Connections**, including the serverless driver, connection pooling, and connection strings - **Authentication** with Neon Auth - **Data API** via `@neondatabase/neon-js` - **Platform APIs and SDKs**, including the REST API, TypeScript SDK, and Python SDK - **Developer tools**, including the CLI, VS Code extension, and MCP server For example, ask your assistant to "set up Neon Auth in my Next.js app" and it will provide the correct imports, configuration, and middleware setup. Or ask it to "connect my app to Neon with Drizzle" and it will generate a working schema and connection configuration using the serverless driver. ## How it works Your AI assistant reads the `SKILL.md` file to understand what Neon guidance is available. When you ask about a specific topic, the skill fetches the relevant documentation from online, so your assistant always has up-to-date context without bundling everything locally. For the complete source, see the [Agent Skills repository](https://github.com/neondatabase/agent-skills). --- [Document source](https://neon.com/docs/ai/ai-agents-tools.md) --- # AI tools for Agents AI-powered tools for development and database management Neon provides several ways to integrate with AI tools and agents, from natural language database control to autonomous agent frameworks. Choose the tools that fit your workflow. ## Quick setup The fastest way to get started with Neon and AI: ```bash npx neonctl@latest init ``` This authenticates via OAuth, creates an API key, configures your editor or CLI, and installs [agent skills](https://github.com/neondatabase/agent-skills). Then restart and ask your AI assistant **"Get started with Neon"**. ## MCP integration The Model Context Protocol (MCP) is a standardized way for AI tools to interact with Neon databases using natural language, providing secure and contextual access to your data and infrastructure. - [Neon MCP Server](https://neon.com/docs/ai/neon-mcp-server): Learn about managing your Neon projects using natural language with Neon MCP Server - [Connect MCP clients](https://neon.com/docs/ai/connect-mcp-clients-to-neon): Learn how to connect MCP clients like Cursor, Claude Code, and ChatGPT to your Neon database ## Claude Code plugin If you're using Claude Code, install the Neon plugin to get Skills, MCP integration, and all the context rules in one package. - [Claude Code plugin for Neon](https://neon.com/docs/ai/ai-claude-code-plugin): Includes Claude Code Skills for Neon, Neon MCP integration, and context rules ## Cursor plugin If you're using Cursor, install the Neon plugin to get Neon Skills and MCP integration in one package. - [Cursor plugin for Neon](https://neon.com/docs/ai/ai-cursor-plugin): Install the Neon Cursor plugin to use Neon Skills and Neon MCP integration directly in Cursor ## GitHub Copilot agents Custom agents for GitHub Copilot that bring Neon's branching workflow directly into VS Code for safe migrations and query optimization. - [Neon agents for GitHub Copilot](https://neon.com/docs/ai/ai-github-copilot-agents): Safe database migrations and query optimization using Neon branching ## Agent Skills Give your AI assistant structured context about Neon's features, APIs, and best practices. - [Agent Skills](https://neon.com/docs/ai/agent-skills): Install Neon Agent Skills for accurate code suggestions covering Auth, Drizzle, serverless connections, APIs, and more ## Build AI agents Create autonomous agents that can manage and interact with your Neon databases programmatically. Build with our terse JavaScript client or the Neon API. - [Neon for AI agent platforms](https://neon.com/use-cases/ai-agents): Read about Neon as a solution for agents that need backends. - [@neondatabase/toolkit](https://github.com/neondatabase/toolkit): A terse JavaScript client for spinning up Postgres databases and running SQL queries - [Database versioning](https://neon.com/docs/ai/ai-database-versioning): How AI agents and codegen platforms use Neon snapshot APIs for database versioning - [Neon API](https://neon.com/docs/reference/api-reference): Integrate using the Neon API ## Agent frameworks Build AI agents using popular frameworks that integrate with Neon. - [AgentStack Integration](https://neon.com/guides/agentstack-neon): Build and deploy AI agents with AgentStack's CLI and Neon integration - [AutoGen Integration](https://neon.com/guides/autogen-neon): Create collaborative AI agents with Microsoft AutoGen and Neon - [Azure AI Agent Service](https://neon.com/guides/azure-ai-agent-service): Build enterprise AI agents with Azure AI Agent Service and Neon - [Composio + CrewAI](https://neon.com/guides/composio-crewai-neon): Create multi-agent systems with CrewAI and Neon - [LangGraph Integration](https://neon.com/guides/langgraph-neon): Build stateful, multi-actor applications with LangGraph and Neon --- [Document source](https://neon.com/docs/ai/ai-azure-notebooks.md) --- # Azure Data Studio Notebooks Use Azure Data Studio Notebooks with Neon for vector similarity search A Jupyter Notebook is an open-source web application that allows you to create and share documents containing live code, equations, visualizations, and narrative text. Azure Data Studio supports Jupyter Notebooks, enabling users to combine SQL queries, Python code, and markdown text in a single interactive document. This guide describes how to create a new python notebook in Azure Data Studio, connect to a Neon database, install the `pgvector` extension to enable Neon as a vector store, and run a vector search query. ## Prerequisites To perform the steps in this guide, you will require: - Azure Data Studio - Download the latest version of Azure Data Studio for your operating system [here](https://learn.microsoft.com/en-us/azure-data-studio/download-azure-data-studio). - A Neon account - If you do not have one, sign up at [Neon](https://console.neon.tech/signup). Your Neon project comes with a ready-to-use Postgres database named `neondb`. You can use it, or create your own by following the instructions [here](https://neon.com/docs/manage/databases#create-a-database). ## Retrieve your Neon database connection string Click **Connect** on your **Project Dashboard** to open the **Connect to your database** modal, and select a branch, a user, and the database you want to connect to. A connection string is constructed for you. ![Connection modal](https://neon.com/docs/connect/connection_details.png) ## Create a notebook 1. Go to the **File** menu for Azure Data Studio and select **New Notebook**. 2. Select **Python 3** for the Kernel and set **Attach to** to "localhost" where it can access your Python installation. You can save the notebook using the **Save** or **Save as...** command from the **File** menu. ## Configure Python for Notebooks The first time you connect to the Python kernel in a notebook, the **Configure Python for Notebooks** page is displayed. You can select either: - **New Python installation** to install a new copy of Python for Azure Data Studio, or - **Use existing Python installation** to specify the path to an existing Python installation for Azure Data Studio to use To view the location and version of the active Python kernel, you can create a code cell and run the following Python commands: ```python import os import sys print(sys.version_info) print(os.path.dirname(sys.executable)) ``` ## Running a code cell You can create cells containing Python code that you can run in place by clicking the **Run cell** button (the round blue arrow) to the left of the cell. The results are shown in the notebook after the cell finishes running. In the `pgvector` example that follows, you'll add and execute several code cells. ## pgvector example After you've set up Azure Data Studio and have created a notebook, you can use the following basic example to get started with Neon and `pgvector`. ### Install the psycopg driver psycopg is a popular Postgres database adapter for the Python programming language. It allows Python applications to connect to and interact with Postgres databases. Install the `psycopg` adapter by adding and executing the following code cell: ```python !pip install psycopg ``` ### Connect to your database 1. In your notebook, create a code block to define your Neon database connection and create a cursor object. Replace `postgresql://[user]:[password]@[neon_hostname]/[dbname]` with the database connection string you retrieved previously. ```python import os import psycopg # Provide your Neon connection string connection_string = "postgresql://[user]:[password]@[neon_hostname]/[dbname]" # Connect using the connection string connection = psycopg.connect(connection_string) # Create a new cursor object cursor = connection.cursor() ``` 2. Execute the code block. 3. Add a code block for testing the database connection. ```python # Execute this query to test the database connection cursor.execute("SELECT 1;") result = cursor.fetchone() # Check the query result if result == (1,): print("Your database connection was successful!") else: print("Your connection failed.") ``` 4. Execute the code block. ### Install the pgvector extension 1. Create a codeblock to install the `pgvector` extension to enable your Neon database as a vector store: ```python # Execute this query to install the pgvector extension cursor.execute("CREATE EXTENSION IF NOT EXISTS vector;") ``` 2. Execute the code block. ### Create a table and add vector data 1. Add a code block to create a table and insert data: ```python create_table_sql = ''' CREATE TABLE items ( id BIGSERIAL PRIMARY KEY, embedding VECTOR(3) ); ''' # Insert data insert_data_sql = ''' INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]'), ('[7,8,9]'); ''' # Execute the SQL statements cursor.execute(create_table_sql) cursor.execute(insert_data_sql) # Commit the changes connection.commit() ``` 2. Execute the code block. ### Query your data 1. Add a codeblock to perform a vector similarity search. ```python cursor.execute("SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 1;") all_data = cursor.fetchall() print(all_data) ``` 2. Execute the code block. ### Next steps For more information about using Neon with `pgvector`, see [The pgvector extension](https://neon.com/docs/extensions/pgvector). --- [Document source](https://neon.com/docs/ai/ai-claude-code-plugin.md) --- # Claude Code plugin for Neon The **Neon Claude Code plugin** is available on the official Claude plugins marketplace. It adds Neon-specific Skills and API access to Claude Code, Anthropic's AI development environment, bundling guided Skills plus an MCP (Model Context Protocol) server integration. ## Overview Claude Skills are Markdown-based workflows that tell Claude how to complete specific tasks (like setting up a database connection, editing a file, or running a script). The Neon plugin packages several of these Skills into a reusable bundle, so Claude Code can interact directly with Neon Postgres. Once installed, the plugin gives Claude the ability to: - Create and manage Neon projects and databases - Connect frameworks like Drizzle ORM - Configure serverless Postgres connections - Reference Neon documentation and best practices in context ## What's included The plugin contains: - **The `neon-postgres` skill** for guided Neon workflows, covering getting started, connections, Neon Auth, the Data API, platform SDKs, and developer tools - **An MCP server integration** that connects Claude to Neon's APIs ## How it works Each Skill is a Markdown file with a description and a step-by-step workflow. When you ask Claude to perform a task (for example, _"Integrate Neon with Drizzle"_), it checks the available Skill descriptions, finds a match, and loads the full instructions to complete the task. The plugin's MCP server integration lets Claude interact with Neon's live API endpoints. That means Claude can: - Query Neon for project information - Create or delete branches and databases - Validate connection strings - Run SQL queries and migrations ## Install the plugin in Claude Code 1. Install the Neon plugin: From your terminal: ```bash claude plugin install neon@claude-plugins-official ``` Or from within a Claude Code session: ```text /plugin install neon@claude-plugins-official ``` 2. Verify the installation: Ask Claude Code: ```text which skills do you have access to? ``` You should see the `neon-postgres` skill listed. 3. Start using the Skills: Use natural language prompts like: > "Use the neon-drizzle Skill to set up Drizzle ORM with Neon." Claude will automatically select and execute the relevant workflow. ## Use skills outside Claude Code The [Agent Skills repository](https://github.com/neondatabase/agent-skills) provides skills for other AI tools as well. You can install them with: ```bash npx skills add neondatabase/agent-skills -s neon-postgres ``` See [Agent Skills](https://neon.com/docs/ai/agent-skills) for all installation options. ## Learn more - [Agent Skills repository](https://github.com/neondatabase/agent-skills) - [Agent Skills overview](https://neon.com/docs/ai/agent-skills) - [Claude Skills documentation](https://docs.anthropic.com/en/docs/agents/claude-code) - [AI Agents and Tools overview](https://neon.com/docs/ai/ai-agents-tools) If you run into issues, visit our [Discord](https://discord.gg/92vNTzKDGp) or open an issue in the [Agent Skills repository](https://github.com/neondatabase/agent-skills/issues). --- [Document source](https://neon.com/docs/ai/ai-codex-plugin.md) --- # Codex plugin for Neon The **Neon Postgres** Codex plugin helps you manage **Neon Serverless Postgres** projects and databases. It adds Neon-specific [Agent Skills](https://developers.openai.com/codex/skills/) and Neon API access to [OpenAI Codex](https://developers.openai.com/codex/), including the **Neon MCP Server** for project and database management and skills that cover connection methods, branching, autoscaling, [Neon Auth](https://neon.com/docs/auth/overview), and more. ## Overview Codex plugins combine **skills** (reusable instructions), **apps** (connections that let Codex act in a product), and **MCP servers** so Codex can follow the right steps and use the right tools. The Neon Postgres plugin wires Codex to Neon so it can provision databases and help you connect your app, not only read static guidance. A typical starting prompt looks like: ```text Use Neon to create a new Serverless Postgres database for my project and help me connect to it. ``` You can also ask Codex to use the Neon Postgres plugin explicitly when you want it to create and manage Neon Serverless Postgres projects and databases through the bundled tools and skills. Once everything is installed, Codex can help you: - Create and manage Neon projects and databases - Choose connection patterns and frameworks (for example Drizzle ORM) - Configure serverless Postgres connections - Apply Neon best practices for branching, autoscaling, and auth ## What's included The Neon Postgres plugin bundles these parts (as shown in Codex): | Component | Type | What it does | | ---------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Neon Postgres** | App | Manage Neon Postgres databases. Backed by the **Neon MCP Server** for project and database operations. | | **Neon Postgres** | Skill | Guides and best practices for Neon Serverless Postgres: connection methods, branching, autoscaling, Neon Auth, and related topics. This corresponds to the **`neon-postgres`** skill in the [Agent Skills repository](https://github.com/neondatabase/agent-skills). | | **Neon Postgres Egress Optimizer** | Skill | Diagnose and reduce excessive Postgres **data transfer (egress)** costs. | Together, the app gives Codex tools to act on your Neon organization and projects, while the skills steer workflows and deep dives (including cost optimization). ## How it works Skills are Markdown workflows Codex can load when a task matches. For example, when you ask to integrate Neon with an ORM or tune connections, Codex can use the **Neon Postgres** skill for step-by-step guidance. The **Neon Postgres** app connects Codex to Neon's APIs through MCP so it can, for example: - Query Neon for project information - Create or delete branches and databases - Validate connection strings - Run SQL queries and migrations The **Neon Postgres Egress Optimizer** skill is for tasks focused on finding and fixing high egress, not for everyday CRUD. ## Install the plugin in Codex ### Codex CLI If the `codex` command is not available yet, install the [Codex CLI](https://developers.openai.com/codex/cli) first: **npm** ```bash npm install -g @openai/codex codex ``` **Homebrew** ```bash brew install --cask codex codex ``` The second line starts Codex in your terminal. For Windows, release binaries, and other install options, see the [Codex CLI](https://developers.openai.com/codex/cli) documentation. With Codex running, run `/plugins` to open the plugin list, open **Neon Postgres**, and choose **Add to Codex**. ### Codex app Open **Plugins**, browse or search for **Neon Postgres**, open the plugin, then choose **Add to Codex**. ### Complete setup (both paths) Some plugins ask you to authenticate when you install or the first time you use them. Follow the prompts to connect Neon if asked. Start a new thread and describe what you want in natural language, or type `@` to pick the **Neon Postgres** plugin or a specific bundled skill. See [Codex app commands](https://developers.openai.com/codex/app/commands) and [Skills](https://developers.openai.com/codex/skills/) in the OpenAI Codex documentation. ## Use with Neon quick setup To configure Neon MCP and agent skills across supported tools from the command line, you can run: ```bash npx neonctl@latest init ``` That flow can set up OAuth, API keys, MCP configuration, and project-level skills where applicable. See the [`neonctl init` reference](https://neon.com/docs/reference/cli-init) for details. ## Use skills outside the Codex plugin The [Agent Skills repository](https://github.com/neondatabase/agent-skills) publishes the same skills for other AI tools. Install the main Neon skill with: ```bash npx skills add neondatabase/agent-skills -s neon-postgres ``` See [Agent Skills](https://neon.com/docs/ai/agent-skills) for global vs project install and other options. ## Learn more - [Codex plugins](https://developers.openai.com/codex/plugins/) (OpenAI) - [Codex skills](https://developers.openai.com/codex/skills/) (OpenAI) - [Agent Skills repository](https://github.com/neondatabase/agent-skills) - [Agent Skills overview](https://neon.com/docs/ai/agent-skills) - [Connect MCP clients to Neon](https://neon.com/docs/ai/connect-mcp-clients-to-neon) (includes Codex) - [AI Agents and Tools overview](https://neon.com/docs/ai/ai-agents-tools) If you run into issues, visit our [Discord](https://discord.gg/92vNTzKDGp) or open an issue in the [Agent Skills repository](https://github.com/neondatabase/agent-skills/issues). --- [Document source](https://neon.com/docs/ai/ai-concepts.md) --- # AI Concepts Learn how embeddings are used to build AI applications Embeddings are an essential component in building AI applications. This topic describes embeddings and how they are used, generated, and stored in Postgres. ## What are embeddings? When working with unstructured data, a common objective is to transform it into a more structured format that is easier to analyze and retrieve. This transformation can be achieved through the use of 'embeddings', which are vectors containing an array of floating-point numbers that represent the features or dimensions of your data. For example, a sentence like "The cow jumped over the moon" might be represented by an embedding that looks like this: [0.5, 0.3, 0.1]. The advantage of embeddings is that they allow us to measure the similarity between different pieces of text. By calculating the distance between two embeddings, we can assess their relatedness - the smaller the distance, the greater the similarity, and vice versa. This quality is particularly useful as it enables embeddings to capture the underlying meaning of the text. Take the following three sentences, for example: - Sentence 1: "The cow jumped over the moon." - Sentence 2: "The bovine leaped above the celestial body." - Sentence 3: "I enjoy eating pancakes." You can determine the most similar sentences by following these steps: 1. Generate embeddings for each sentence. For illustrative purposes, assume these values represent actual embeddings: - Embedding for sentence 1 → [0.5, 0.3, 0.1] - Embedding for sentence 2 → [0.6, 0.29, 0.12] - Embedding for sentence 3 → [0.1, -0.2, 0.4] 2. Compute the distance between all pairs of embeddings (1 & 2, 2 & 3, and 1 & 3). 3. Identify the pair of embeddings with the shortest distance between them. When we apply this process, it is likely that sentences 1 and 2, both of which involve jumping cattle, will emerge as the most related according to a distance calculation. ## Vector similarity search Transforming data into embeddings and computing similarities between one or more items is referred to as vector search or similarity search. This process has a wide range of applications, including: - **Information retrieval:** By representing user queries as vectors, we can perform more accurate searches based on the meaning behind the queries, allowing us to retrieve more relevant information. - **Natural language processing:** Embeddings capture the essence of the text, making them excellent tools for tasks such as text classification and sentiment analysis. - **Recommendation systems:** Using vector similarity, we can recommend items similar to a given item, whether they be movies, products, books, or otherwise. This technique allows us to create more personalized and relevant recommendations. - **Anomaly detection:** By determining the similarity between items within a dataset, we can identify outliers or anomalies (items that don't quite fit the pattern). This can be crucial in many fields, from cybersecurity to quality control. ### Distance metrics Vector similarity search computes similarities (the distance) between data points. Calculating how far apart data points are helps us understand the relationship between them. Distance can be computed in different ways using different metrics. Some popular distance metrics include: - Euclidean (L2): Often referred to as the "ordinary" distance you'd measure with a ruler. - Manhattan (L1): Also known as "taxicab" or "city block" distance. - Cosine: This calculates the cosine of the angle between two vectors. Other distance metrics supported by the `pgvector` extension include [Hamming distance](https://en.wikipedia.org/wiki/Hamming_distance) and [Jaccard distance](https://en.wikipedia.org/wiki/Jaccard_index). Different distance metrics can be more appropriate for different tasks, depending on the nature of the data and the specific relationships you're interested in. For instance, cosine similarity is often used in text analysis. ## Generating embeddings A common approach to generating embeddings is to use an LLM API, such as [OpenAI's Embeddings API](https://platform.openai.com/docs/api-reference/embeddings). This API allows you to input a text string into an API endpoint, which then returns the corresponding embedding. The "cow jumped over the moon" is a simplistic example with 3 dimensions. Most embedding models generate embeddings with a much larger number of dimensions. OpenAI's newest and most performant embedding models, `text-embedding-3-small` and `text-embedding-3-large`, generate embeddings with 1536 and 3072 dimensions by default, respectively. Here's an example of how to use OpenAI's `text-embedding-3-small` model to generate an embedding: ```bash curl https://api.openai.com/v1/embeddings \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -d '{ "input": "Your text string goes here", "model": "text-embedding-3-small" }' ``` **Note:** Running the command above requires an OpenAI API key, which must be obtained from [OpenAI](https://platform.openai.com/). Upon successful execution, you'll receive a response similar to the following: ```json { "object": "list", "data": [ { "object": "embedding", "index": 0, "embedding": [ -0.006929283495992422, -0.005336422007530928, ... (omitted for spacing) -4.547132266452536e-05, -0.024047505110502243 ], } ], "model": "text-embedding-3-small", "usage": { "prompt_tokens": 5, "total_tokens": 5 } } ``` To learn more about OpenAI's embeddings, see [Embeddings](https://platform.openai.com/docs/guides/embeddings). Here, you'll find an example of obtaining embeddings from an [Amazon fine-food reviews](https://www.kaggle.com/datasets/snap/amazon-fine-food-reviews) dataset supplied as a CSV file. See [Obtaining the embeddings](https://platform.openai.com/docs/guides/embeddings/use-cases). There are many embedding models you can use, such as those provided by Mistral AI, Cohere, Hugging Face, etc. AI tools like [LanngChain](https://www.langchain.com/) provide interfaces and integrations for working with a variety of models. See [LangChain: Text embedding models](https://js.langchain.com/v0.1/docs/integrations/text_embedding/). You'll also find a [Neon Postgres guide](https://js.langchain.com/v0.1/docs/integrations/vectorstores/neon/) on the LangChain site and [Class NeonPostgres](https://v02.api.js.langchain.com/classes/langchain_community_vectorstores_neon.NeonPostgres.html), which provides an interface for working with a Neon Postgres database. ## Storing vector embeddings in Postgres Neon supports the [pgvector](https://neon.com/docs/extensions/pgvector) Postgres extension, which enables the storage and retrieval of vector embeddings directly within your Postgres database. When building AI applications, installing this extension eliminates the need to extend your architecture to include a separate vector store. Installing the `pgvector` extension simply requires running the following `CREATE EXTENSION` statement from the [Neon SQL Editor](https://neon.com/docs/get-started/query-with-neon-sql-editor) or any SQL client connected to your Neon Postgres database. ```sql CREATE EXTENSION vector; ``` After installing the `pgvector` extension, you can create a table to store your embeddings. For example, you might define a table similar to the following to store your embeddings: ```sql CREATE TABLE items(id BIGSERIAL PRIMARY KEY, embedding VECTOR(1536)); ``` To add embeddings to the table, you would insert the data as shown: ```sql INSERT INTO items(embedding) VALUES ('[ -0.006929283495992422, -0.005336422007530928, ... -4.547132266452536e-05, -0.024047505110502243 ]'); ``` For detailed information about using `pgvector`, refer to our guide: [The pgvector extension](https://neon.com/docs/extensions/pgvector). --- [Document source](https://neon.com/docs/ai/ai-cursor-plugin.md) --- # Cursor plugin for Neon The **Neon Cursor plugin** adds Neon-specific Skills and MCP integration to Cursor so your assistant can both reason about best practices and take database actions. It is distributed from the [Neon agent-skills repository](https://github.com/neondatabase/agent-skills) and packages: - The `neon-postgres` Skill bundle for Neon workflows - Neon MCP server access for project and database operations - Shared plugin packaging designed for both Cursor and Claude Code ## Overview The plugin combines guidance and actions: - **Skills** provide workflow-level instructions for tasks like choosing a connection method, setting up ORMs, and using Neon branching patterns. - **MCP integration** allows tool-driven operations such as listing projects, creating branches, and running SQL through Neon APIs. Together, this gives Cursor enough context to suggest the right approach and enough capability to execute it. ## What is included The Cursor plugin currently includes: - **Neon Skill bundle (`neon-postgres`)** - **Neon MCP server configuration** for natural language database management For plugin packaging details, see the [Cursor plugin template](https://github.com/cursor/plugin-template). ## Install the plugin in Cursor 1. Open Cursor chat and run: ```text /add-plugin neon-postgres ``` 2. After installation, prompt Cursor with: ```text Get started with Neon ``` 3. Follow the prompts to complete authentication and start using Neon Skills and MCP tools. ## Use with Neon quick setup If you want Neon to configure AI tooling automatically, run: ```bash npx neonctl@latest init ``` This configures MCP and installs Neon agent skills for supported editors. ## Learn more - [Neon Agent Skills repository](https://github.com/neondatabase/agent-skills) - [AI Agents and Tools overview](https://neon.com/docs/ai/ai-agents-tools) - [Connect MCP clients to Neon](https://neon.com/docs/ai/connect-mcp-clients-to-neon) --- [Document source](https://neon.com/docs/ai/ai-database-versioning.md) --- # Database versioning with snapshots How AI agents and codegen platforms implement database version control using snapshots and preview branches **Note: Beta** Snapshots are available in Beta. Please give us [Feedback](https://console.neon.tech/app/projects?modal=feedback) from the Neon Console or by connecting with us on [Discord](https://discord.gg/92vNTzKDGp). **Limits and pricing:** The Free plan includes 1 manual snapshot, and paid plans (including the [Agent plan](https://neon.com/use-cases/ai-agents)) include 10 manual snapshots. On paid plans, snapshots created by backup schedules do not count toward this limit. Snapshots are free during the Beta period. Snapshot storage will be billed at $0.09/GB-month, starting May 1, 2026. If you need higher limits, please reach out to [Neon support](https://neon.com/docs/introduction/support). ## Overview This guide describes how you can implement database versioning for AI agent and code generation platforms using Neon's snapshot APIs. With snapshots, you can create point-in-time database versions, perform instant rollbacks, and maintain stable database connection strings for your applications. See a working implementation in the [demonstration repository](https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo). > **Terminology note:** This guide uses "versions" to describe saved database states from the user's perspective, and "snapshots" when referring to Neon's technical implementation. You may also see these called "checkpoints" or "edits" in some AI agent contexts. **Tip: Synopsis** Use the project's root branch for production, whose database connection string stays the same when a snapshot restore is finalized. Create snapshots to save database versions. For rollbacks, restore snapshots with `finalize_restore: true` and `target_branch_id` set to your root branch ID, then poll operations until complete before connecting. For previews, use `finalize_restore: false` to create temporary branches with their own database connection strings. ## Why use snapshots for versioning Standard database branching is great for development but less suitable for versioning. Each new branch gets a new database connection string and creates dependency chains that complicate deletion. This pattern solves both problems. By restoring a Neon snapshot to your active branch with `finalize_restore: true`, you replace its data in-place while preserving the original, stable connection string. This makes the snapshot-restore pattern ideal for versioned environments where connection stability is needed. ## Quick start with the demo The best way to understand this pattern is to see it in action: 1. **Clone the snapshots demo app**: - [https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo](https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo) 2. **Key files to examine**: - [lib/neon/create-snapshot.ts](https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo/blob/main/lib/neon/create-snapshot.ts) - Snapshot creation implementation - [lib/neon/apply-snapshot.ts](https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo/blob/main/lib/neon/apply-snapshot.ts) - Complete restore workflow with operations polling - [lib/neon/operations.ts](https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo/blob/main/lib/neon/operations.ts) - Operation status polling logic - [app/[checkpointId]/page.tsx](https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo/blob/main/app/\[checkpointId]/page.tsx) - UI integration showing versions and rollbacks 3. Run locally or use the [public demo](https://snapshots-as-checkpoints-demo.vercel.app/) to see version creation, rollbacks, and previews in action > **Note:** The demo repository uses "checkpoint" terminology which maps to "version" in this guide. > The demo implements a contacts application that evolves through agent prompts, demonstrating version creation and restoration at each stage: **v0: empty app** → **v1: basic contacts** → **v2: add role/company** → **v3: add tags** ## The active branch pattern Every agent project maps to one Neon project with a designated [root branch](https://neon.com/docs/reference/glossary#root-branch) that serves as the production database. **Important:** Snapshots can only be created from root branches in Neon. A root branch is a branch with no parent (typically named `main` or `production`). **The active branch:** - Gets its data replaced during finalized rollbacks - Maintains a consistent database connection string through Neon's restore mechanism; see [How restore works](https://neon.com/docs/ai/ai-database-versioning#how-restore-works) for details - Must be a root branch for snapshot creation **The snapshots:** - Capture point-in-time database versions - Store only incremental changes (cost-efficient) - Can be restored to the active branch or to a temporary preview branch ![Active branch pattern diagram](https://neon.com/docs/guides/active_branch_pattern.jpg) ## Implementation ### Creating snapshots Create a snapshot to capture the current database version using the [snapshot endpoint](https://api-docs.neon.tech/reference/createsnapshot): ```bash POST /api/v2/projects/{project_id}/branches/{branch_id}/snapshot ``` > **Demo implementation:** See [lib/neon/create-snapshot.ts](https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo/blob/main/lib/neon/create-snapshot.ts) for an example with error handling and operation polling. > **Path parameters:** - `project_id` (string, required): The Neon project ID - `branch_id` (string, required): The active branch ID (must be a root branch) **Query parameters:** - `lsn` (string): Target Log Sequence Number. Cannot be used with `timestamp` - `timestamp` (string): Target timestamp (RFC 3339). Cannot be used with `lsn` - `name` (string): Name for the snapshot - `expires_at` (string): Auto-deletion time (RFC 3339) **Example:** ```bash curl --request POST \ --url 'https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/snapshot?name=version-session-1&expires_at=2025-08-13T00:00:00Z' \ --header 'authorization: Bearer $NEON_API_KEY' ``` **Response:** The JSON body includes a `snapshot` object. It may include optional **`full_size`** and **`diff_size`** (bytes) for storage size; the same fields appear when you [list](https://api-docs.neon.tech/reference/listsnapshots) or [update](https://api-docs.neon.tech/reference/updatesnapshot) snapshots. See [Snapshot size fields in API responses](https://neon.com/docs/guides/backup-restore#snapshot-size-fields-in-api-responses). **When to create snapshots:** - Start of each agent session - Before database schema changes - After successful operations - User-initiated save points **Tip:** Learn how our Developer Advocate approaches snapshot-based workflows in [Promoting Postgres changes safely to production](https://neon.com/blog/promoting-postgres-changes-safely-production). ### Rolling back to (restoring) a snapshot Restore any snapshot to recover a previous version using the [restore endpoint](https://api-docs.neon.tech/reference/restoresnapshot): ```bash POST /api/v2/projects/{project_id}/snapshots/{snapshot_id}/restore ``` > **Demo implementation:** See [lib/neon/apply-snapshot.ts](https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo/blob/main/lib/neon/apply-snapshot.ts) for the complete restore workflow including operation polling and error handling. > **Path parameters:** - `project_id` (string, required): The Neon project ID - `snapshot_id` (string, required): The snapshot ID being restored **Body parameters:** - `name` (string): A name for the newly restored branch. If omitted, a default name is generated - `target_branch_id` (string): The ID of the branch to restore the snapshot into. If not specified, the branch from which the snapshot was originally created will be used. **Set this value to your root branch ID for rollbacks to preserve connection strings** - `finalize_restore` (boolean): Set to `true` to finalize the restore operation immediately. This will complete the restore and move computes from the current branch to the new branch, which keeps the database connection string the same. Defaults to `false`. **Set to `true` when restoring to the active branch for rollbacks, `false` to create preview branches** **Note: Connection warning** Do not connect to the database until current API operations are complete. Any connection attempt before operations finish will either fail or connect to the old database state, not your restored version. #### How restore works Understanding the restore mechanism explains why the connection string remains stable: 1. **New branch creation**: When you restore with `finalize_restore: true`, Neon first creates a new branch from your snapshot. This new branch has a different, system-generated branch ID. 2. **Endpoint transfer**: Neon then transfers the compute endpoint (and its associated connection string) from your original active branch to this newly created branch. 3. **Settings migration**: All branch settings, including its name, are copied to the new active branch, making it appear identical to the old one. Only the branch ID is different. 4. **Branch orphan**: Your original branch becomes "orphaned." It is disconnected from the compute endpoint and renamed by adding an "(old)" suffix (for example, `main (old)`) to the branch name. **Info: Branch ID changes after restore** The connection string remains stable, but the branch ID changes with every `finalize_restore: true` operation. If you store the branch ID for use in subsequent API calls (for example, to create the next snapshot), you must retrieve and store the new branch ID after the restore operation completes. #### Rollback workflow Restore any snapshot to your active branch, preserving the connection string: ```json { "target_branch_id": "br-active-branch-123456", // Your root branch ID "finalize_restore": true // Moves computes and preserves connection string } ``` > **Important:** When restoring with `finalize_restore: true`, your previous active branch becomes orphaned and is renamed with `(old)` appended, such as `production (old)` or similar. This orphaned branch is no longer connected to any compute endpoint but preserves your pre-restore state. Delete it during cleanup to avoid unnecessary costs. > After calling the restore API: 1. Extract the array of operation IDs from the API response. 2. For each operation ID, poll the operations endpoint until its status reaches a terminal state (finished, failed, cancelled, or skipped). 3. Do not attempt to connect to the database until all operations are complete. Connections made before completion will point to the old, pre-restore database state. 4. After verifying a successful restore, delete the orphaned branch (for example, `main (old)`) to avoid incurring storage costs. > See the [poll operation status](https://neon.com/docs/manage/operations#poll-operation-status) documentation for related information. > **Polling operations example:** ```javascript // Poll operation status until complete async function waitForOperation(projectId, operationId) { while (true) { const response = await fetch( `https://console.neon.tech/api/v2/projects/${projectId}/operations/${operationId}`, { headers: { Authorization: `Bearer ${NEON_API_KEY}` } } ); const { status } = await response.json(); // Terminal states - safe to proceed if (['finished', 'skipped', 'cancelled'].includes(status)) { return; } // Error state - handle appropriately if (status === 'failed') { throw new Error('Operation failed'); } // Still running - wait and retry await new Promise((resolve) => setTimeout(resolve, 5000)); } } // After restore API call const restoreResponse = await restoreSnapshot(projectId, snapshotId); const operationIds = restoreResponse.operations.map((op) => op.id); // Wait for all operations to complete for (const id of operationIds) { await waitForOperation(projectId, id); } // NOW safe to connect to the restored database ``` ![Polling operations flow diagram](https://neon.com/docs/guides/polling_operations_flow.jpg) **Potential restore-related problems:** - **Connection to old state**: Ensure all operations completed - **Target branch not found**: Verify branch exists - **Operation timeout**: Retry with longer timeout - **Accumulating orphaned branches**: Delete orphaned branches (for example, `production (old)`) after successful restore verification #### Preview environments Create a temporary branch to preview a version without affecting the active branch: ```json { "name": "preview-version-123", "finalize_restore": false // Creates new branch for preview without moving computes } ``` This creates a new branch with its own connection string for preview. The active branch remains unchanged. Preview branches should be deleted after use to avoid storage costs. ### Managing snapshots #### List available snapshots Get all snapshots with IDs, names, and timestamps using the [list snapshots endpoint](https://api-docs.neon.tech/reference/listsnapshots): ```bash GET /api/v2/projects/{project_id}/snapshots ``` **Path parameters:** - `project_id` (string, required): The Neon project ID #### Delete snapshot Remove a snapshot using the [delete endpoint](https://api-docs.neon.tech/reference/deletesnapshot): ```bash DELETE /api/v2/projects/{project_id}/snapshots/{snapshot_id} ``` **Path parameters:** - `project_id` (string, required): The Neon project ID - `snapshot_id` (string, required): The snapshot ID #### Update snapshot name Rename a snapshot using the [update endpoint](https://api-docs.neon.tech/reference/updatesnapshot): ```bash PATCH /api/v2/projects/{project_id}/snapshots/{snapshot_id} ``` **Path parameters:** - `project_id` (string, required): The Neon project ID - `snapshot_id` (string, required): The snapshot ID **Body:** ```json { "snapshot": { "name": "important-milestone" } } ``` #### Cleanup strategy Proper cleanup reduces costs and keeps your project manageable: - Delete snapshots when no longer reachable by users - Restoring from a snapshot doesn't lock that snapshot from deletion, unlike branches where creating child branches prevents deleting the parent - Delete orphaned branches created during restores (named like `production (old)`) - These orphaned branches accumulate with each restore and consume storage - Reduces snapshot management fees while shared storage remains - Set `expires_at` for automatic cleanup or delete manually as needed - Consider removing snapshots after merging features or completing rollback testing ## Concepts and terminology ### Neon core concepts - [Project](https://neon.com/docs/reference/glossary#project): Neon project that owns branches, computes, and snapshots, and more - [Branch](https://neon.com/docs/reference/glossary#branch): An isolated database environment with its own data and schema that you can connect to and modify - [Snapshot](https://neon.com/docs/reference/glossary#snapshot): An immutable, point-in-time backup of a branch's schema and data. Read-only until restored - [Root branch](https://neon.com/docs/reference/glossary#root-branch): A branch with no parent (typically named `main` or `production`). The only type of branch from which snapshots can be created - [Operations](https://neon.com/docs/manage/operations#operations-and-the-neon-api): Backend operations that return operation IDs you must poll to completion ### Pattern-specific terminology - **Active branch**: The root branch that serves as your agent's production database (though technically replaced during restores). Connection string never changes even when data is replaced via restore. Preview branches may be created alongside for temporary exploration. - **Version**: A saved database state captured as a snapshot, which may also be referred to a checkpoint or edit. Users create and restore versions through your application interface. - **Orphaned branch**: Created when restoring with `finalize_restore: true`. The previous active branch becomes orphaned (disconnected from compute) and is renamed to `branch-name (old)`. Can be safely deleted after verifying the restore. - **Preview branch**: Temporary branch created from a snapshot for safe exploration, to preview a version ## API quick reference | Operation | Endpoint | Description | | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------- | | [Create snapshot](https://api-docs.neon.tech/reference/createsnapshot) | `POST /api/v2/projects/{project_id}/branches/{branch_id}/snapshot` | Save current database state as a new version | | [Restore snapshot](https://api-docs.neon.tech/reference/restoresnapshot) | `POST /api/v2/projects/{project_id}/snapshots/{snapshot_id}/restore` | Restore database to a previous version | | [List snapshots](https://api-docs.neon.tech/reference/listsnapshots) | `GET /api/v2/projects/{project_id}/snapshots` | Get all available versions | | [Delete snapshot](https://api-docs.neon.tech/reference/deletesnapshot) | `DELETE /api/v2/projects/{project_id}/snapshots/{snapshot_id}` | Remove a saved version | | [Update snapshot](https://api-docs.neon.tech/reference/updatesnapshot) | `PATCH /api/v2/projects/{project_id}/snapshots/{snapshot_id}` | Rename a version | | [Poll operation](https://api-docs.neon.tech/reference/getprojectoperation) | `GET /api/v2/projects/{project_id}/operations/{operation_id}` | Check restore status | | [List branches](https://api-docs.neon.tech/reference/listprojectbranches) (for cleanup) | `GET /api/v2/projects/{project_id}/branches` | Find orphaned branches to clean up | ## Implementation checklist - [ ] Create one Neon project per agent project - [ ] Designate the root branch (main/production) as the "active" branch - [ ] Store the active branch connection string and branch ID - [ ] Create snapshots at key points to save database versions - [ ] For rollbacks: restore with `finalize_restore: true` and set `target_branch_id` to the root branch ID - [ ] After a rollback, update your stored active branch ID - [ ] For previews: restore with `finalize_restore: false` to create temporary branches - [ ] Poll all operation IDs to terminal states before connecting - [ ] Implement a cleanup strategy: set snapshot expiration dates and delete orphaned branches ## Best practices - **Set `target_branch_id` for rollbacks**: When restoring to the active branch, always specify `target_branch_id` to prevent accidental restores. - **Poll operations**: Wait for terminal states before connecting to the database. - **Snapshot naming conventions**: Use descriptive conventions like `prod_snap_2025-01-17_pre-promotion`, `dev_snap_2025-01-17_candidate_v2`, `prod_snap_2025-01-17_after-promotion` to make automation easier. - **Snapshot production before promotion**: Take a snapshot of your production branch before promoting changes to provide a rollback point if needed. - **Differential retention**: Keep production snapshots longer for potential rollback, and development snapshots briefly (hours max) for promotion cycles only. - **Implement connection retry logic**: Design application code to retry queries automatically, as restore operations briefly drop active connections (typically milliseconds, occasionally up to a second). - **Keep backup branches briefly**: After restore, keep the automatically-created backup branch (for example, `prod (old)`) for sanity checks before deletion, or assign a [time to live](https://neon.com/docs/guides/branch-expiration) for automatic cleanup. - **Cleanup strategy**: Set `expires_at` on temporary snapshots and preview branches. Delete orphaned branches (for example, `production (old)`) created during restores. - **Version metadata**: Keep version metadata separate to preserve audit trail across restores. ## FAQ Why must I poll operations after restore? : With `finalize_restore: true`, Neon moves compute resources to the new state. Until operations complete, connections still point to the old compute. What happens to my active branch when I restore? : When restoring with `finalize_restore: true`, your current active branch becomes orphaned (disconnected from the compute endpoint) and is renamed with "(old)" appended. This orphaned branch preserves your pre-restore state temporarily, but you should delete it after verifying the restore to avoid storage costs. What if we need multiple preview environments? : Restore different snapshots to new branches using `finalize_restore: false`. Each restore creates a new branch with its own connection string. Why use snapshots instead of branches for versioning? : **Snapshots**: Restoring a snapshot onto your active branch (`finalize_restore: true`) replaces the data but keeps the same database connection string. This is ideal for production rollbacks. : **Branches**: Creating a new branch always generates a new connection string, which would require reconfiguring your application for every version change. Branches also create dependency chains that can complicate deletion. ## Summary The active branch pattern with Neon snapshots provides a simple, reliable versioning solution for AI agent and codegen platforms. By keeping database connection strings stable through the restore mechanism and using snapshots to implement version control, you get stable connection strings for your main database, instant rollbacks to previous versions, and the flexibility to create preview branches when needed. The implementation is straightforward: create snapshots to save versions, restore with `finalize_restore: true` to the active branch for rollbacks, or with `finalize_restore: false` for preview branches. Always poll operations to completion before connecting. See the [demo repository](https://github.com/neondatabase-labs/snapshots-as-checkpoints-demo) for a complete example. --- [Document source](https://neon.com/docs/ai/ai-github-copilot-agents.md) --- # Neon agents for GitHub Copilot Custom agents for safe database migrations and query optimization in VS Code GitHub Copilot now supports custom agents, and we've built two specialized agents that bring Neon's branching workflow directly into your IDE. These agents help you run safe database migrations and identify slow queries, all without leaving VS Code. ## Available agents ### Neon Migration Specialist The [Neon Migration Specialist](https://github.com/github/awesome-copilot/blob/main/agents/neon-migration-specialist.agent.md) helps you run safe Postgres migrations with zero downtime using Neon's branching workflow. **What it does:** 1. Creates a temporary database branch from your main branch 2. Runs your migration on the test branch to validate it works 3. Validates the changes thoroughly 4. Deletes the test branch after validation 5. Creates migration files and opens a PR, letting you or your CI/CD apply the migration to your main branch The agent works with your existing ORM migration system (Prisma, Drizzle, SQLAlchemy, Django, Rails, and more) and falls back to [migra](https://github.com/djrobstep/migra) if no migration system exists. **Important:** The Migration Specialist never runs migrations directly on your main branch. All changes are tested on temporary branches first, and the actual migration is committed to your git repository for you or your CI/CD to execute. ### Neon Performance Analyzer The [Neon Performance Analyzer](https://github.com/github/awesome-copilot/blob/main/agents/neon-optimization-analyzer.agent.md) helps you identify and fix slow Postgres queries automatically. **What it does:** 1. Creates an analysis branch from your main branch 2. Enables `pg_stat_statements` if not already installed 3. Identifies slow queries by analyzing execution statistics 4. Uses `EXPLAIN` to understand bottlenecks 5. Investigates your codebase to understand query context 6. Tests optimizations (indexes, query rewrites) on a temporary branch 7. Provides recommendations via PR with clear before/after metrics 8. Cleans up all temporary branches The agent provides actionable code fixes with performance metrics showing execution time improvements, rows scanned, and other relevant data. ## Prerequisites Both agents require: - **Neon API Key**: Create one at [console.neon.tech/app/settings/api-keys](https://console.neon.tech/app/settings#api-keys) - **Project ID or connection string**: The agent will ask for this if not provided ## Installation ### Option 1: Install from VS Code marketplace The agents are available in the GitHub Copilot agent marketplace in VS Code: 1. Open VS Code 2. Go to the Extensions view 3. Search for "Neon Migration Specialist" or "Neon Performance Analyzer" 4. Click **Install** ### Option 2: Add to your repository You can add the agent definition files directly to your project: 1. Create a `.github/copilot/agents/` directory in your repository 2. Download the agent files: - [neon-migration-specialist.agent.md](https://raw.githubusercontent.com/github/awesome-copilot/main/agents/neon-migration-specialist.agent.md) - [neon-optimization-analyzer.agent.md](https://raw.githubusercontent.com/github/awesome-copilot/main/agents/neon-optimization-analyzer.agent.md) 3. Save them to your `.github/copilot/agents/` directory ## Usage Once installed, invoke the agents in GitHub Copilot Chat by mentioning their name: ### Migration examples ``` @neon-migration-specialist Add a new email column to my users table ``` ``` @neon-migration-specialist Create a posts table with title, content, and author_id columns ``` ``` @neon-migration-specialist Add a foreign key from orders.customer_id to customers.id ``` ### Performance analysis examples ``` @neon-performance-analyzer Find and fix slow queries in my database ``` ``` @neon-performance-analyzer Analyze why my user search query is slow ``` ``` @neon-performance-analyzer Optimize the queries in my checkout flow ``` ## How branching keeps your data safe Both agents leverage Neon's instant branching to create isolated environments for testing. This means: - **No changes to production**: All migrations and optimizations are tested on temporary branches first - **Full data copy**: Test branches include a complete copy of your schema and data - **Automatic cleanup**: Temporary branches are deleted after validation (default TTL: 4 hours) - **Git-based workflow**: Changes are committed to your repository, not applied directly This workflow ensures you can safely experiment with schema changes and performance optimizations without risking your production data. ## Resources - [Neon Migration Specialist on GitHub](https://github.com/github/awesome-copilot/blob/main/agents/neon-migration-specialist.agent.md) - [Neon Performance Analyzer on GitHub](https://github.com/github/awesome-copilot/blob/main/agents/neon-optimization-analyzer.agent.md) - [Neon branching documentation](https://neon.com/docs/introduction/branching) - [awesome-copilot repository](https://github.com/github/awesome-copilot) --- [Document source](https://neon.com/docs/ai/ai-google-colab.md) --- # Google Colab Use Google Colab with Neon for vector similarity search [Google Colab](https://colab.research.google.com/) is a hosted Jupyter Notebook service that requires no setup to use and provides free access to computing resources, including GPUs and TPUs. You can use Google Colab to run python code through the browser. This guide shows how to create a notebook in Colab, connect to a Neon database, install the `pgvector` extension to enabled Neon as a vector store, and run a vector search query. ## Prerequisites To perform the steps in this guide, you require a Neon database for storing vectors. You can use the ready-to-use `neondb` database or create your own. See [Create a database](https://neon.com/docs/manage/databases#create-a-database) for instructions. ## Retrieve your database connection string Click **Connect** on your **Project Dashboard** to open the **Connect to your database** modal, and select a branch, a user, and the database you want to connect to. A connection string is constructed for you. ![Connection modal](https://neon.com/docs/connect/connection_details.png) ## Create a notebook In your browser, navigate to [Google Colab](https://colab.research.google.com/), and click **New notebook**. ![Google Colab](https://neon.com/docs/ai/google_colab.png) Alternatively, you can open a predefined Google Colab notebook for this guide by clicking the **Open in Colab** button below. [https://colab.research.google.com/github/neondatabase/neon-google-colab-notebooks/blob/main/neon_pgvector_quickstart.ipynb](https://colab.research.google.com/github/neondatabase/neon-google-colab-notebooks/blob/main/neon_pgvector_quickstart.ipynb) ## Connect to your database 1. In your Colab notebook, create a code block to define your database connection and create a cursor object. Replace `postgresql://[user]:[password]@[neon_hostname]/[dbname]` with the database connection string you retrieved in the previous step. ```python import os import psycopg2 # Provide your Neon connection string connection_string = "postgresql://[user]:[password]@[neon_hostname]/[dbname]" # Connect using the connection string connection = psycopg2.connect(connection_string) # Create a new cursor object cursor = connection.cursor() ``` 2. Execute the code block (**Ctrl** + **Enter**). 3. Add a code block for testing the database connection. ```python # Execute this query to test the database connection cursor.execute("SELECT 1;") result = cursor.fetchone() # Check the query result if result == (1,): print("Your database connection was successful!") else: print("Your connection failed.") ``` 4. Execute the code block (**Ctrl** + **Enter**). ## Install the pgvector extension 1. Create a codeblock to install the `pgvector` extension to enable your Neon database as a vector store: ```python # Execute this query to install the pgvector extension cursor.execute("CREATE EXTENSION IF NOT EXISTS vector;") ``` 2. Execute the code block (**Ctrl** + **Enter**). ## Create a table and add vector data 1. Add a code block to create a table and insert data: ```python create_table_sql = ''' CREATE TABLE items ( id BIGSERIAL PRIMARY KEY, embedding VECTOR(3) ); ''' # Insert data insert_data_sql = ''' INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]'), ('[7,8,9]'); ''' # Execute the SQL statements cursor.execute(create_table_sql) cursor.execute(insert_data_sql) # Commit the changes connection.commit() ``` 2. Execute the code block (**Ctrl** + **Enter**). ## Query your data 1. Add a codeblock to perform a vector similarity search. ```python cursor.execute("SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 3;") all_data = cursor.fetchall() print(all_data) ``` 2. Execute the code block (**Ctrl** + **Enter**). ## Next steps For more information about using Neon with `pgvector`, see [The pgvector extension](https://neon.com/docs/extensions/pgvector). --- [Document source](https://neon.com/docs/ai/ai-intro.md) --- # AI Starter Kit Resources for building AI applications with Neon Postgres This guide collects resources for building AI applications with Neon Postgres. You'll find core concepts, starter applications, framework integrations, and deployment guides. Use these resources to build applications like RAG chatbots, semantic search engines, or custom AI tools. > **Start building AI apps with Neon** > > Sign up for Neon Postgres and jumpstart your AI application with our starter apps and resources. > > [Sign Up](https://console.neon.tech/signup) ## Getting started Learn the fundamentals of building AI applications with Neon: - [AI concepts](https://neon.com/docs/ai/ai-concepts): Learn the fundamentals of embeddings and vector search for AI applications - [pgvector extension](https://neon.com/docs/extensions/pgvector): Get started with pgvector for storing and querying vector embeddings ## AI frameworks and integrations Build AI applications faster with these popular frameworks, tools, and services: - [LangChain](https://neon.com/docs/ai/langchain): Create AI applications using LangChain with OpenAI and Neon - [LlamaIndex](https://neon.com/docs/ai/llamaindex): Build RAG applications using LlamaIndex with OpenAI and Neon - [Semantic Kernel](https://neon.com/docs/ai/semantic-kernel): Develop AI applications using Semantic Kernel with Azure OpenAI - [Inngest](https://neon.com/docs/ai/inngest): Build reliable AI workflows with Inngest and Neon - [app.build](https://neon.com/docs/ai/ai-app-build): Generate and deploy web applications using the open-source app.build agent ## Starter applications Hackable, fully-featured, pre-built starter apps to get you up and running: - [AI chatbot (OpenAI + LllamIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/chatbot-nextjs): A Next.js AI chatbot starter app built with OpenAI and LlamaIndex - [AI chatbot (OpenAI + LangChain)](https://github.com/neondatabase/examples/tree/main/ai/langchain/chatbot-nextjs): A Next.js AI chatbot starter app built with OpenAI and LangChain - [RAG chatbot (OpenAI + LlamaIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/rag-nextjs): A Next.js RAG chatbot starter app built with OpenAI and LlamaIndex - [RAG chatbot (OpenAI + LangChain)](https://github.com/neondatabase/examples/tree/main/ai/langchain/rag-nextjs): A Next.js RAG chatbot starter app built with OpenAI and LangChain - [Semantic search (OpenAI + LlamaIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/semantic-search-nextjs): A Next.js Semantic Search chatbot starter app built with OpenAI and LlamaIndex - [Semantic search (OpenAI + LangChain)](https://github.com/neondatabase/examples/tree/main/ai/langchain/semantic-search-nextjs): A Next.js Semantic Search chatbot starter app built with OpenAI and LangChain - [Hybrid search (OpenAI)](https://github.com/neondatabase/examples/tree/main/ai/hybrid-search-nextjs): A Next.js Hybrid Search starter app built with OpenAI - [Reverse image search (OpenAI + LlamaIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/reverse-image-search-nextjs): A Next.js Reverse Image Search Engine starter app built with OpenAI and LlamaIndex - [Chat with PDF (OpenAI + LlamaIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/chat-with-pdf-nextjs): A Next.js Chat with PDF chatbot starter app built with OpenAI and LlamaIndex - [Chat with PDF (OpenAI + LangChain)](https://github.com/neondatabase/examples/tree/main/ai/langchain/chat-with-pdf-nextjs): A Next.js Chat with PDF chatbot starter app built with OpenAI and LangChain ## Scale your AI application - [Scale with Neon](https://neon.com/docs/ai/ai-scale-with-neon): Learn how to scale your AI application with Autoscaling and Read Replicas - [Optimize vector search](https://neon.com/docs/ai/ai-vector-search-optimization): Best practices for optimizing vector search performance ## Featured examples Real-world AI applications built with Neon that you can reference as code examples or inspiration. **Tip: Built something cool?** Share your AI app on our [#showcase](https://discord.gg/neon) channel on Discord. - [AI vector database per tenant](https://github.com/neondatabase/ai-vector-db-per-tenant): Deploy an AI vector database per-tenant architecture with Neon - [Guide: Build a RAG chatbot](https://neon.com/guides/chatbot-astro-postgres-llamaindex): Build a RAG chatbot in an Astro application with LlamaIndex and Postgres - [Guide: Build a Reverse Image Search Engine](https://neon.com/guides/llamaindex-postgres-search-images): Using LlamaIndex with Postgres to Build your own Reverse Image Search Engine - [Ask Neon Chatbot](https://github.com/neondatabase/ask-neon): An Ask Neon AI-powered chatbot built with pgvector - [Vercel Postgres pgvector Starter](https://vercel.com/templates/next.js/postgres-pgvector): Enable vector similarity search with Vercel Postgres powered by Neon - [YCombinator Semantic Search App](https://github.com/neondatabase/yc-idea-matcher): YCombinator semantic search application - [Web-based AI SQL Playground](https://github.com/neondatabase/postgres-ai-playground): An AI-enabled SQL playground application for natural language queries - [Jupyter Notebook for vector search with Neon](https://github.com/neondatabase/neon-vector-search-openai-notebooks): Jupyter Notebook for vector search with Neon, pgvector, and OpenAI - [Image search with Neon and Vertex AI](https://github.com/ItzCrazyKns/Neon-Image-Search): Community: An image search app built with Neon and Vertex AI - [Text-to-SQL conversion with Mistral + LangChain](https://github.com/mistralai/cookbook/blob/main/third_party/Neon/neon_text_to_sql.ipynb): A Text-to-SQL conversion app built with Mistral AI, Neon, and LangChain - [Postgres GPT Expert](https://neon.com/blog/openais-gpt-store-is-live-create-and-publish-a-custom-postgres-gpt-expert): Blog + repo: Create and publish a custom Postgres GPT Expert using OpenAI's GPT ## Vector search tools and notebooks Optimize your vector search implementation and experiment with different approaches: - [Vector search optimization](https://neon.com/docs/ai/ai-vector-search-optimization): Best practices for optimizing vector search performance - [Vector search notebooks](https://github.com/neondatabase/neon-vector-search-openai-notebooks): Interactive notebooks for vector search with OpenAI - [Google Colab guide](https://neon.com/docs/ai/ai-google-colab): Use Neon with Google Colab for ML experiments - [Azure Data Studio Notebooks](https://neon.com/docs/ai/ai-azure-notebooks): A cloud-based Jupyter notebook service integrated with Azure Data Studio --- [Document source](https://neon.com/docs/ai/ai-scale-with-neon.md) --- # Scale your AI application with Neon Scale your AI application with Neon's Autoscaling and Read Replica features You can scale your AI application built on Postgres with `pgvector` in the same way you would any Postgres app: Vertically with added CPU, RAM, and storage, or horizontally with read replicas. In Neon, scaling vertically is a matter of selecting the desired compute size. Neon supports compute sizes ranging from .25 CU (1 GB RAM) up to 56 CU (224 GB RAM). Autoscaling is supported up to 16 CU. Larger computes are fixed size computes (no autoscaling). The `maintenance_work_mem` values shown below are approximate. | Compute Units (CU) | RAM | maintenance\_work\_mem | | :----------------- | :----- | :--------------------- | | 0.25 | 1 GB | 64 MB | | 0.50 | 2 GB | 64 MB | | 1 | 4 GB | 67 MB | | 2 | 8 GB | 134 MB | | 3 | 12 GB | 201 MB | | 4 | 16 GB | 268 MB | | 5 | 20 GB | 335 MB | | 6 | 24 GB | 402 MB | | 7 | 28 GB | 470 MB | | 8 | 32 GB | 537 MB | | 9 | 36 GB | 604 MB | | 10 | 40 GB | 671 MB | | 11 | 44 GB | 738 MB | | 12 | 48 GB | 805 MB | | 13 | 52 GB | 872 MB | | 14 | 56 GB | 939 MB | | 15 | 60 GB | 1007 MB | | 16 | 64 GB | 1074 MB | | 18 | 72 GB | 1208 MB | | 20 | 80 GB | 1342 MB | | 22 | 88 GB | 1476 MB | | 24 | 96 GB | 1610 MB | | 26 | 104 GB | 1744 MB | | 28 | 112 GB | 1878 MB | | 30 | 120 GB | 2012 MB | | 32 | 128 GB | 2146 MB | | 34 | 136 GB | 2280 MB | | 36 | 144 GB | 2414 MB | | 38 | 152 GB | 2548 MB | | 40 | 160 GB | 2682 MB | | 42 | 168 GB | 2816 MB | | 44 | 176 GB | 2950 MB | | 46 | 184 GB | 3084 MB | | 48 | 192 GB | 3218 MB | | 50 | 200 GB | 3352 MB | | 52 | 208 GB | 3486 MB | | 54 | 216 GB | 3620 MB | | 56 | 224 GB | 3754 MB | See [Edit a compute](https://neon.com/docs/manage/computes#edit-a-compute) to configure your compute size. Available compute sizes differ according to your Neon plan. To optimize `pgvector` index build time, you can increase the `maintenance_work_mem` setting for the current session beyond the preconfigured default shown in the table above with a command similar to this: ```sql SET maintenance_work_mem='10 GB'; ``` The recommended `maintenance_work_mem` setting is your working set size (the size of your tuples for vector index creation). However, your `maintenance_work_mem` setting should not exceed 50 to 60 percent of your compute's available RAM (see the table above). For example, the `maintenance_work_mem='10 GB'` setting shown above has been successfully tested on a 7 CU compute, which has 28 GB of RAM, as 10 GB is less than 50% of the RAM available for that compute size. ## Autoscaling You can also enable Neon's autoscaling feature for automatic scaling of compute resources. Neon's _Autoscaling_ feature automatically scales up compute on demand in response to application workload and down to zero on inactivity. For example, if your AI application experiences heavy load during certain hours of the day or at different times throughout the week, month, or calendar year, Neon automatically scales compute resources without manual intervention according to the compute size boundaries that you configure. This enables you to handle peak demand while avoiding consuming compute resources during periods of low activity. Enabling autoscaling is also recommended for initial data loads and memory-intensive index builds to ensure sufficient compute resources for this phase of your AI application setup. To learn more about Neon's autoscaling feature and how to enable it, refer to our [Autoscaling guide](https://neon.com/docs/introduction/autoscaling). ## Storage On the Free plan, you get 0.5 GB of storage plus 0.5 GB of storage per branch. Storage on paid plans is usage based. See [Neon plans](https://neon.com/docs/introduction/plans) for details. ## Read replicas Neon supports read replicas, which are independent read-only computes designed to perform read operations on the same data as your primary read-write compute. Read replicas do not replicate data across database instances. Instead, read requests are directed to the same data source. This architecture enables read replicas to be created instantly, enabling you to scale out CPU and RAM, but because data is read from a single source, there are no additional storage costs. Since vector similarity search is a read-only workload, you can leverage read replicas to offload reads from your primary read-write compute to a dedicated compute when deploying AI applications. After you create a read replica, you can simply swap out your current Neon connecting string for the read replica connection string, which makes deploying a read replica for your AI application very simple. Neon's read replicas support the same compute sizes outlined above. Read replicas also support autoscaling. To learn more about the Neon read replicas, see [read replicas](https://neon.com/docs/introduction/read-replicas) and refer to our [Working with Neon read replicas](https://neon.com/docs/guides/read-replica-guide) guide. --- [Document source](https://neon.com/docs/ai/ai-vector-search-optimization.md) --- # Optimize pgvector search Fine-tune parameters for efficient and accurate similarity searches in Postgres This guide explores how to effectively use `pgvector` for vector similarity searches in your AI applications. We'll address the following key questions: 1. How to profile your vector search queries, when using `pgvector`? 2. When to use indexes and tradeoffs between the available options? 3. Which parameters to tune for best performance? We'll examine sequential scans, HNSW indexing, and IVFFlat indexing, providing benchmarks and practical recommendations for various dataset sizes. This will help you optimize `pgvector` queries in your Neon database for both accuracy and speed. Without indexes, `pgvector` performs a sequential scan on the database and calculates the distance between the query vector and all vectors in the table. This approach does an exact search and guarantees 100% **recall**, but it can be costly with large datasets. **Note: what is recall?** Recall is a metric used to evaluate the performance of a search algorithm. It measures how effectively the search retrieves relevant items from a dataset. It is defined as the ratio of the number of relevant items retrieved by the search to the total number of relevant items in the dataset. The query below uses `EXPLAIN ANALYZE` to generate an execution plan and display the performance of the similarity search query. ```sql EXPLAIN ANALYZE SELECT * FROM items ORDER BY embedding <-> '[0.011699999682605267,..., 0.008700000122189522]' LIMIT 100; ``` This is what the query plan looks like: ```sql Limit (cost=748.19..748.44 rows=100 width=173) (actual time=39.475..39.487 rows=100 loops=1) -> Sort (cost=748.19..773.19 rows=10000 width=173) (actual time=39.473..39.480 rows=100 loops=1) Sort Key: ((vec <-> '[0.0117,..., 0.0866]'::vector)) Sort Method: top-N heapsort Memory: 70kB -> Seq Scan on items (cost=0.00..366.00 rows=10000 width=173) (actual time=0.087..37.571 rows=10000 loops=1) Planning Time: 0.213 ms Execution Time: 39.527 ms ``` You can see in the plan that the query performs a sequential scan (`Seq Scan`) on the `items` table, which means that the query compares the query vector against all vectors in the `items` table. In other words, the query does not use an index. To understand how queries perform at scale, we tested sequential scan vector searches with `pgvector` on subsets of the [GIST-960 dataset](http://corpus-texmex.irisa.fr/) with 10k, 50k, 100k, 500k, and 1M rows using a Neon database instance with 4 CU (16 GB of RAM). The sequential scan search performed reasonably well for tables with 10k rows (~36ms). However, sequential scans start to become costly at 50k rows. So, when should you use sequential scans rather than defining an index? - When your dataset is small and you do not intend to scale it. - When you need 100% recall (accuracy). Adding indexes trades recall for performance. - When you do not expect a high volume of queries per second, which would require indexes for performance. Otherwise, consider adding an index for better performance. ## Indexing with HNSW HNSW is a graph-based approach to indexing multi-dimensional data. It constructs a multi-layered graph, where each layer is a subset of the previous one. During a vector similarity search, the algorithm navigates through the graph from the top layer to the bottom to quickly find the nearest neighbor. An HNSW graph is known for its superior performance in terms of speed and accuracy. **Note:** An HNSW index performs better than IVFFlat (in terms of speed-recall tradeoff) and can be created without any data in the table since there isn't a training step like there is for an IVFFlat index. However, HNSW indexes have slower build times and use more memory. ![HNSW graph](https://neon.com/docs/extensions/hnsw_graph.png) The search process begins at the topmost layer of the HNSW graph. From the starting node, the algorithm navigates to the nearest neighbor in the same layer. The algorithm repeats this step until it can no longer find neighbors more similar to the query vector. Using the found node as an entry point, the algorithm moves down to the next layer in the graph and repeats the process of navigating to the nearest neighbor. The process of navigating to the nearest neighbor and moving down a layer is repeated until the algorithm reaches the bottom layer. In the bottom layer, the algorithm continues navigating to the nearest neighbor until it cannot find any nodes that are more similar to the query vector. The current node is then returned as the most similar node to the query vector. The key idea behind HNSW is that by starting the search at the top layer and moving down through each layer, the algorithm can quickly navigate to the area of the graph that contains the node that is most similar to the query vector. This makes the search process much faster than if it had to search through every node in the graph. ### Tuning the HNSW algorithm The following options allow you to tune the HNSW algorithm when creating an index: - `m`: Defines the maximum number of links created for each node during graph construction. A higher value increases accuracy (recall), but it also increases the size of the index in memory and index construction time. Higher values are typically used with high-dimensionality datasets or when a high degree of accuracy is required. The default value is 16. Acceptable values for m typically fall between 2 and 100. For many applications, beginning with a range of 12 to 48 is advisable. - `ef_construction`: Defines the size of the list for the nearest neighbors. This value influences the tradeoff between index quality and construction speed. A high `ef_construction` value creates a higher quality graph, enabling more accurate search results but also means that index construction takes longer. The value should be set to at least twice the value of `m`. The default setting is 64. There comes a point where increasing `ef_construction` no longer improves index quality. To evaluate search accuracy, you can start by setting `ef_construction` equal to `ef_search` and incrementally increasing `ef_construction` to achieve the desired result. If accuracy is lower than 0.9, there may be opportunity for improvement by increasing `ef_construction`. This example demonstrates how to set the parameters: ```sql CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64); ``` HNSW search tuning: - `ef_search`: Defines the size of the dynamic candidate list for search. The default value is 40. This value influences the trade-off between query accuracy (recall) and speed. A higher value increases accuracy at the cost of speed. The value should be equal to or larger than `k`, which is the number of nearest neighbors you want your search to return (defined by the `LIMIT` clause in your `SELECT` query). To configure this value, do so using a `SET` statement before executing queries: ```sql SET hnsw.ef_search = 100; ``` You can also use `SET LOCAL` inside a transaction to set it for a single query: ```sql BEGIN; SET LOCAL hnsw.ef_search = 100; SELECT ... COMMIT; ``` In summary: - To prioritize search speed over accuracy, use lower values for `m` and `ef_search`. - Conversely, to prioritize accuracy over search speed, use a higher value for `m` and `ef_search`. - Using a higher value for `ef_construction` yields more accurate search results at the cost of index build time. ## Indexing with IVFFlat IVFFlat indexes partition the dataset into clusters ("lists") to optimize for vector search. You can create an IVFFlat index using the query below: ```sql CREATE INDEX items_embedding_cosine_idx ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 1000); ``` IVFFlat in `pgvector` has two parameters: 1. `lists` - This parameter specifies the number of [k-means clusters](https://en.wikipedia.org/wiki/K-means_clustering) (or "lists") to divide the dataset into - Each cluster contains a subset of the data, and each data point belongs to the closest cluster centroid. 2. `probes` - This parameter determines the number of lists to explore during the search for the nearest neighbors. - By probing multiple lists, the search algorithm can find the closest points more accurately, balancing between speed and accuracy. By default, the `probes` parameter is set to `1`. This means that during a search, only one cluster is explored. This approach is fine if your query vector is close to the centroid. However, if the query vector is located near the edge of the cluster, closer neighbors in adjacent clusters will not be included in the search, which can result in a lower recall. You must specify the number of probes in the same connection as the search query: ```sql SET ivfflat.probes = 100; SET enable_seqscan=off; SELECT * FROM items ORDER BY embedding <-> '[0.011699999682605267,..., 0.008700000122189522]' LIMIT 100; ``` **Note:** In the example above, `enable_seqscan=off` forces Postgres to use index scans. The output of this query appears as follows: ```sql Limit (cost=1971.50..1982.39 rows=100 width=173) (actual time=4.500..5.738 rows=100 loops=1) -> Index Scan using items_embedding_idx on vectors (cost=1971.50..3060.50 rows=10000 width=173) (actual time=4.499..5.726 rows=100 loops=1) Order By: (vec <-> '[0.0117, ... ,0.0866]'::vector) Planning Time: 0.295 ms Execution Time: 5.867 ms ``` We've experimented with `lists` equal to 1000, 2000, and 4000, and `probes` equal to 1, 2, 10, 50, 100, 200. Although there is a substantial gain in recall for increasing the number of `probes`, you will reach a point of diminishing returns when recall plateaus and execution time increases. Therefore, we encourage experimenting with different values for `probes` and `lists` to achieve optimal search performance for your queries. Good places to start are: - Using a `lists` size equal to rows / 1000 for tables with up to 1 million rows, and `sqrt(rows)` for larger datasets. - Start with a `probes` value equal to lists / 10 for tables up to 1 million rows, and `sqrt(lists)` for larger datasets. ## Conclusion The sequential scan approach of `pgvector` performs well for small datasets but can be costly for larger ones. Use sequential scans if you require 100% accuracy, but expect performance issues with higher volumes of queries per second. You can optimize searches using HNSW or IVFFlat indexes for approximate nearest neighbor (ANN) search, but HNSW indexes have better query performance than IVFFlat with build time and memory usage tradeoffs. Be sure to test different index tuning parameter settings to find the right balance between speed and accuracy for your specific use case and dataset. --- [Document source](https://neon.com/docs/ai/connect-mcp-clients-to-neon.md) --- # Connect MCP clients to Neon Learn how to connect MCP clients such as Cursor, Claude Code, VS Code, ChatGPT, and other tools to your Neon Postgres database. This guide covers connecting MCP clients to the Neon MCP Server for natural language interaction with your Neon Postgres databases. **Important: Security** The Neon MCP Server is intended for **development and testing only**. Always review LLM-requested actions before execution. See [MCP security guidance](https://neon.com/docs/ai/neon-mcp-server#mcp-security-guidance). ## Quick setup (`neonctl init`) The fastest way to get started: ```bash npx neonctl@latest init ``` **`neonctl init`** (see [`neonctl init` reference](https://neon.com/docs/reference/cli-init)) creates a Neon API key and configures the MCP server with **API key** auth so you can skip OAuth when using the connection. It installs the VS Code/Cursor extension where applicable, wires **Claude Code** and **many other assistants** the wizard supports, and installs Neon's [agent skills](https://github.com/neondatabase/agent-skills). Then restart and ask your AI assistant **"Get started with Neon"**. **Note:** Each run of `npx neonctl@latest init` creates a new Neon API key. If you run it multiple times, review your [API keys](https://console.neon.tech/app/settings/api-keys) and revoke any you no longer need. If you only want the MCP server and nothing else, use: ```bash npx add-mcp https://mcp.neon.tech/mcp ``` This adds the MCP config to your editor's configuration files. Add `-g` for global (user-level) setup instead of project-level. Restart your editor (or enable the MCP server in your editor's settings); when you use the connection, an OAuth window will open to authorize. For API key authentication, add `--header "Authorization: Bearer $NEON_API_KEY"`. For more options, see the [add-mcp repository](https://github.com/neondatabase/add-mcp). ## Supported agents (add-mcp) **add-mcp** is the CLI Neon uses to patch each tool's MCP config. Use **`npx add-mcp list-agents`** for the live list from your installed version. As of the current [add-mcp](https://github.com/neondatabase/add-mcp) release, **`--agent`** values include: | Assistant | `--agent` | | :------------------------ | :------------------- | | Antigravity | `antigravity` | | Cline (VS Code extension) | `cline` | | Cline CLI | `cline-cli` | | Claude Code | `claude-code` | | Claude Desktop | `claude-desktop` | | Codex | `codex` | | Cursor | `cursor` | | Gemini CLI | `gemini-cli` | | GitHub Copilot CLI | `github-copilot-cli` | | Goose | `goose` | | MCPorter | `mcporter` | | OpenCode | `opencode` | | VS Code | `vscode` | | Zed | `zed` | **Aliases:** `cline-vscode` → `cline`, `gemini` → `gemini-cli`, `github-copilot` → `vscode`. Config paths differ by agent and by project vs global (`-g`); see the [add-mcp README](https://github.com/neondatabase/add-mcp#supported-agents). ## Setup options - **Quick setup:** `npx neonctl@latest init` (MCP with API key auth, extension where supported, agent skills, and many assistants via the wizard) - **OAuth:** Connect to Neon's remote MCP server (no local installation needed) - **Local:** Run the MCP server locally with your API key (requires Node.js >= v18) For Local setup, you'll need a [Neon API key](https://neon.com/docs/manage/api-keys#creating-api-keys). ## Cursor **Quick Setup** Run the [init](https://neon.com/docs/reference/cli-init) command: ```bash npx neonctl@latest init ``` Authenticates via OAuth, creates an API key, installs the [Neon extension](https://neon.com/docs/local/vscode-extension) (which includes the MCP Server), and installs [agent skills](https://github.com/neondatabase/agent-skills). Then ask your AI assistant **"Get started with Neon"**. **OAuth** ```bash npx add-mcp https://mcp.neon.tech/mcp -a cursor ``` Restart Cursor (or enable the MCP server in settings). When the OAuth window opens, click **Authorize** to complete the connection. **Local** 1. Open Cursor. Create a `.cursor` directory in your project root if needed. 2. Create or open the `mcp.json` file in the `.cursor` directory. 3. Add the "Neon" server entry within the `mcpServers` object. Replace `` with your Neon API key: ```json { "mcpServers": { "neon": { "command": "npx", "args": ["-y", "@neondatabase/mcp-server-neon", "start", ""] } } } ``` 4. Save the configuration file. Cursor may detect the change or require a restart. For more, see [Get started with Cursor and Neon Postgres MCP Server](https://neon.com/guides/cursor-mcp-neon). ## Claude Code **Quick Setup** Run the [init](https://neon.com/docs/reference/cli-init) command: ```bash npx neonctl@latest init ``` Authenticates via OAuth, creates an API key, configures the MCP Server in `~/.claude.json`, and installs [agent skills](https://github.com/neondatabase/agent-skills). Then ask your AI assistant **"Get started with Neon"**. **OAuth** ```bash npx add-mcp https://mcp.neon.tech/mcp -a claude-code ``` Restart Claude Code (or enable the MCP server in settings). When the OAuth window opens, click **Authorize** to complete the connection. **Local** ```bash claude mcp add neon -- npx -y @neondatabase/mcp-server-neon start "" ``` Replace `` with your [Neon API key](https://neon.com/docs/manage/api-keys). For more, see [Get started with Claude Code and Neon Postgres MCP Server](https://neon.com/guides/claude-code-mcp-neon). ## VS Code (with GitHub Copilot) **Note:** To use MCP servers with VS Code, you need [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) and [GitHub Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat) extensions installed **Quick Setup** Run the [init](https://neon.com/docs/reference/cli-init) command: ```bash npx neonctl@latest init ``` Authenticates via OAuth, creates an API key, installs the [Neon extension](https://neon.com/docs/local/vscode-extension) (which includes the MCP Server), and installs [agent skills](https://github.com/neondatabase/agent-skills). Then ask your AI assistant **"Get started with Neon"**. **OAuth** ```bash npx add-mcp https://mcp.neon.tech/mcp -a vscode ``` Restart VS Code (or enable the MCP server in settings). When the OAuth window opens, click **Authorize** to complete the connection. Then open GitHub Copilot Chat and [switch to Agent mode](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode). **Local** Add the Neon MCP server to your [User Settings (JSON)](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-user-settings): ```json { "mcp": { "servers": { "neon": { "command": "npx", "args": ["-y", "@neondatabase/mcp-server-neon", "start", ""] } } } } ``` Replace `` with your [Neon API key](https://neon.com/docs/manage/api-keys). Then open GitHub Copilot Chat and [switch to Agent mode](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode). For a detailed guide including an Azure Function REST API example, see [Using Neon MCP Server with GitHub Copilot in VS Code](https://neon.com/guides/neon-mcp-server-github-copilot-vs-code). ## ChatGPT Connect ChatGPT to Neon using custom MCP connectors. Enable Developer mode, add the Neon connector, then enable it per chat. ![ChatGPT with Neon MCP Server](https://neon.com/docs/changelog/chatgpt_mcp.png) 1. **Add MCP server to ChatGPT** In your ChatGPT account settings, go to **Settings** → **Connectors** → **Advanced Settings** and enable **Developer mode**. Still on the Connectors tab, you can then **create** a Neon connection from the **Browse connectors** section. Use the following URL: ```bash https://mcp.neon.tech/mcp ``` Make sure you choose **OAuth** for authentication and check "I trust this application", then complete the authorization flow when prompted. ![ChatGPT connector configuration](https://neon.com/docs/ai/chatgpt_mcp_add_connector.png) ![ChatGPT with Neon MCP tools enabled](https://neon.com/docs/ai/chatgpt_mcp_tools.png) 2. **Enable Neon per chat** In each chat where you want to use Neon, click the **+** button and enable Developer Mode for that chat. Under **Add sources**, you can then enable the Neon connector you just created. Once connected, you can use natural language to manage your Neon databases directly in ChatGPT. ## Claude Desktop **OAuth** ```bash npx add-mcp https://mcp.neon.tech/mcp -a claude-desktop ``` Restart Claude Desktop. When the OAuth window opens, click **Authorize** to complete the connection. **Local** ```bash npx @neondatabase/mcp-server-neon init ``` Replace `` with your [Neon API key](https://neon.com/docs/manage/api-keys), then restart Claude Desktop. For more, see [Get started with Neon MCP server with Claude Desktop](https://neon.com/guides/neon-mcp-server). ## Cline (VS Code Extension) **OAuth** 1. Open Cline in VS Code (Sidebar -> Cline icon). 2. Click **MCP Servers** Icon -> **Installed** -> **Configure MCP Servers** to open the configuration file. 3. Add the "Neon" server entry within the `mcpServers` object: ```json { "mcpServers": { "neon": { "command": "npx", "args": ["-y", "mcp-remote@latest", "https://mcp.neon.tech/mcp"] } } } ``` 4. Save the file. Cline should reload the configuration automatically. 5. When the OAuth window opens in your browser, review the requested permissions and click **Authorize** to complete the connection. **Local** 1. Open Cline in VS Code (Sidebar -> Cline icon). 2. Click **MCP Servers** Icon -> **Installed** -> **Configure MCP Servers** to open the configuration file. 3. Add the "Neon" server entry within the `mcpServers` object: ```json { "mcpServers": { "neon": { "command": "npx", "args": ["-y", "@neondatabase/mcp-server-neon", "start", ""] } } } ``` > Replace `` with your Neon API key. 4. Save the file. Cline should reload the configuration automatically. For more, see [Get started with Cline and Neon Postgres MCP Server](https://neon.com/guides/cline-mcp-neon). ## Windsurf (Codeium) **OAuth** 1. Open Windsurf and navigate to the Cascade assistant sidebar. 2. Click the hammer (MCP) icon, then **Configure** which opens up the "Manage MCPs" configuration file. 3. Click on "View raw config" to open the raw configuration file in Windsurf. 4. Add the "Neon" server entry within the `mcpServers` object: ```json { "mcpServers": { "neon": { "command": "npx", "args": ["-y", "mcp-remote@latest", "https://mcp.neon.tech/mcp"] } } } ``` 5. Save the file. 6. Click the **Refresh** button in the Cascade sidebar next to "available MCP servers". 7. When the OAuth window opens in your browser, review the requested permissions and click **Authorize** to complete the connection. **Local** 1. Open Windsurf and navigate to the Cascade assistant sidebar. 2. Click the hammer (MCP) icon, then **Configure** which opens up the "Manage MCPs" configuration file. 3. Click on "View raw config" to open the raw configuration file in Windsurf. 4. Add the "Neon" server entry within the `mcpServers` object: ```json { "mcpServers": { "neon": { "command": "npx", "args": ["-y", "@neondatabase/mcp-server-neon", "start", ""] } } } ``` > Replace `` with your Neon API key. 5. Save the file. 6. Click the **Refresh** button in the Cascade sidebar next to "available MCP servers". For more, see [Get started with Windsurf and Neon Postgres MCP Server](https://neon.com/guides/windsurf-mcp-neon). ## Zed **Note:** MCP support in Zed is currently in **preview**. Ensure you're using the Preview version of Zed to add MCP servers (called **Context Servers** in Zed). Download the preview version from [zed.dev/releases/preview](https://zed.dev/releases/preview). **OAuth** ```bash npx add-mcp https://mcp.neon.tech/mcp -a zed ``` Restart Zed (or enable the MCP server in settings). When the OAuth window opens, click **Authorize** to complete the connection. **Local** 1. Open the Zed Preview application. 2. Click the Assistant (✨) icon, then **Settings** > **Context Servers** > **+ Add Context Server**. 3. Enter **neon** as the name and this command: ```bash npx -y @neondatabase/mcp-server-neon start ``` 4. Replace `` with your [Neon API key](https://neon.com/docs/manage/api-keys) and click **Add Server**. For more details, including workflow examples and troubleshooting, see [Get started with Zed and Neon Postgres MCP Server](https://neon.com/guides/zed-mcp-neon). ## Jules 1. Create a [Neon API key](https://neon.com/docs/manage/api-keys#creating-api-keys) from your Neon Console **Settings**. 2. Go to [jules.google.com](https://jules.google.com) > **Settings** > **MCP** (or use [this direct link](https://jules.google.com/settings/mcp)). 3. Click **Connect** on the Neon server and paste your API key when prompted. 4. Run a task invoking the Neon MCP server to verify the connection. ## Other MCP clients Prefer **`npx neonctl@latest init`** for the full flow (see [Quick setup](https://neon.com/docs/ai/connect-mcp-clients-to-neon#quick-setup-neonctl-init) above). If you **only** want MCP config lines, or you are re-running wiring for one tool, use **add-mcp**: ```bash npx add-mcp https://mcp.neon.tech/mcp ``` This tool auto-detects supported clients and configures them. Use `-a ` to target a specific agent (for example, `-a cursor`). Add `-g` for global (user-level) setup instead of project-level. For more options (including global vs project-level), see the [add-mcp repository](https://github.com/neondatabase/add-mcp). For manual configuration, add one of these to your client's `mcpServers` section: **OAuth (remote server):** ```json "neon": { "command": "npx", "args": ["-y", "mcp-remote@latest", "https://mcp.neon.tech/mcp"] } ``` **Local setup:** ```json "neon": { "command": "npx", "args": ["-y", "@neondatabase/mcp-server-neon", "start", ""] } ``` For Windows-specific configurations, see [Local MCP Server](https://neon.com/docs/ai/neon-mcp-server#other-setup-options). ## Troubleshooting ### Configuration Issues If your client does not use `JSON` for configuration of MCP servers (such as older versions of Cursor), you can use the following command when prompted: ```bash # For OAuth (remote server) npx -y mcp-remote https://mcp.neon.tech/mcp # For Local setup npx -y @neondatabase/mcp-server-neon start ``` **Note:** For clients that don't support Streamable HTTP, you can use the deprecated SSE endpoint: `https://mcp.neon.tech/sse`. SSE is not supported with API key authentication. ### OAuth Authentication Errors When using the remote MCP server with OAuth authentication, you might encounter the following error: ``` {"code":"invalid_request","error":"invalid redirect uri"} ``` This typically occurs when there are issues with cached OAuth credentials. To resolve this: 1. Remove the MCP authentication cache directory: ```bash rm -rf ~/.mcp-auth ``` 2. Restart your MCP client application 3. The OAuth flow will start fresh, allowing you to properly authenticate This error is most common when using OAuth authentication and can occur after OAuth configuration changes or when cached credentials become invalid. ## Next steps Once connected, explore the [available MCP tools](https://neon.com/docs/ai/neon-mcp-server#supported-actions-tools) to see what you can do with natural language. ## Resources - [MCP Protocol](https://modelcontextprotocol.org) - [Neon API Reference](https://api-docs.neon.tech/reference/getting-started-with-neon-api) - [Neon API Keys](https://neon.com/docs/manage/api-keys#creating-api-keys) - [Neon MCP server GitHub](https://github.com/neondatabase/mcp-server-neon) - [VS Code MCP Server Documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) --- [Document source](https://neon.com/docs/ai/inngest.md) --- # Inngest Quickly build AI RAG and Agentic workflows that scale with Inngest and Neon Inngest is a popular framework for building AI RAG and Agentic workflows. [Inngest](https://www.inngest.com/?utm_source=neon\&utm_medium=inngest-ai-integration) provides automatic retries, caching along with concurrency and throttling management and AI requests offloading. Inngest also integrates with Neon Postgres to trigger workflows based on database changes. ## Build RAG with `step.run()` Inngest provides a `step.run()` API that allows you to compose your workflows into cacheable, retryable, and concurrency-safe steps: ![A RAG workflow built with Inngest. A failing step is retried while the previous steps results are cached.](https://neon.com/docs/guides/inngest-rag-workflow.png) In the above workflow, a network issue prevented the AI workflow to connect to the vector store. Fortunately, Inngest retries the failed step and uses the cached results from the previous steps, avoiding an unnecessary additional OpenAI call. This workflow translates to the following code: ```typescript import { inngest } from '@/inngest'; import { getToolsForMessage, vectorSearch } from '@/helpers'; export const ragWorkflow = client.createFunction( { id: 'rag-workflow', concurrency: 10 }, { event: 'chat.message' }, async ({ event, step }) => { const { message } = event.data; const page = await step.run('tools.search', async () => { // Calls OpenAI return getToolsForMessage(message); }); await step.run('vector-search', async () => { // Search in Neon's vector store return vectorSearch(page); }); // step 3 and 4... } ); ``` Configuring [concurrency](https://www.inngest.com/docs/guides/concurrency?utm_source=neon\&utm_medium=inngest-ai-integration) or [throttling](https://www.inngest.com/docs/guides/throttling?utm_source=neon\&utm_medium=inngest-ai-integration) to match your LLM provider's limits is achieved with a single line of code. Learn more about using Inngest for RAG in the following article: [Multi-Tenant RAG With One Neon Project Per User](https://neon.com/blog/multi-tenant-rag). ## AI requests offloading: `step.ai.infer()` Inngest also provides a `step.ai.infer()` API that offloads AI requests. By using `step.ai.infer()` your AI workflows will pause while waiting for the slow LLM response, avoiding unnecessary compute use on Serverless environments: ![An AI workflow built with Inngest. The AI request is offloaded to the LLM provider.](https://neon.com/docs/guides/inngest-with-step-ai-infer.png) ![An AI workflow built with Inngest. The AI request is offloaded to the LLM provider.](https://neon.com/docs/guides/inngest-without-step-ai-infer.png) The previous RAG workflow can be rewritten to use [`step.ai.infer()`](https://www.inngest.com/docs/features/inngest-functions/steps-workflows/step-ai-orchestration?utm_source=neon\&utm_medium=inngest-ai-integration#step-ai-infer) to offload the AI request to the LLM provider: ```typescript import { inngest } from '@/inngest'; import { getPromptForToolsSearch, vectorSearch } from '@/helpers'; export const ragWorkflow = client.createFunction( { id: 'rag-workflow', concurrency: 10 }, { event: 'chat.message' }, async ({ event, step }) => { const { message } = event.data; const prompt = getPromptForToolsSearch(message); await step.ai.infer('tools.search', { model: openai({ model: 'gpt-4o' }), body: { messages: prompt, }, }); // other steps... } ); ``` `step.ai.infer()`, combined with Neon's Scale-to-zero feature, allows you to build AI workflows that scale costs with its success! Learn more about using `step.ai.infer()` in the following article: [step.ai: Build Serverless AI Applications That Won't Break the Bank](https://www.inngest.com/blog/step-ai-for-serverless-ai-applications?utm_source=neon\&utm_medium=inngest-ai-integration). ## Trigger AI workflows based on database changes Inngest also integrates with Neon Postgres to trigger AI workflows based on database changes: ![Inngest integrates with Neon Postgres to trigger AI workflows based on database changes](https://neon.com/docs/guides/inngest.jpg) This integration allows you to trigger AI workflows based on database changes, such as generating embeddings as soon as a new row is inserted into a table (see example below). Configure the Inngests Neon integration to trigger AI workflows from your Neon database changes [by following this guide](https://neon.com/docs/guides/trigger-serverless-functions). ## Starter apps Hackable, fully-featured, pre-built [starter apps](https://github.com/neondatabase/examples/tree/main/ai/inngest) to get you up and running with Inngest and Postgres. - [RAG starter (OpenAI + Inngest)](https://github.com/neondatabase/examples/tree/main/ai/inngest/rag-starter-nextjs): A Next.js RAG starter app built with OpenAI and Inngest - [multi-tenant RAG (OpenAI + Inngest)](https://github.com/inngest/multi-tenant-rag-example): A Next.js contacts importer multi-tenant RAG built with OpenAI and Inngest - [Auto-embedding (OpenAI + Inngest)](https://github.com/neondatabase/examples/tree/main/ai/inngest/auto-embeddings-nextjs): A Next.js app example of auto-embedding with Inngest --- [Document source](https://neon.com/docs/ai/langchain.md) --- # LangChain Build AI applications faster with LangChain and Postgres LangChain is a popular framework for working with AI, Vectors, and embeddings. LangChain supports using Neon as a vector store, using the `pgvector` extension. ## Initialize Postgres Vector Store LangChain simplifies the complexity of managing document insertion and embeddings generation using vector stores by providing streamlined methods for these tasks. Here's how you can initialize Postgres Vector with LangChain: ```tsx // File: vectorStore.ts import { NeonPostgres } from '@langchain/community/vectorstores/neon'; import { OpenAIEmbeddings } from '@langchain/openai'; const embeddings = new OpenAIEmbeddings({ dimensions: 512, model: 'text-embedding-3-small', }); export async function loadVectorStore() { return await NeonPostgres.initialize(embeddings, { connectionString: process.env.POSTGRES_URL as string, }); } // Use in your code (say, in API routes) const vectorStore = await loadVectorStore(); ``` ## Generate Embeddings with OpenAI LangChain handles embedding generation internally while adding vectors to the Postgres database, simplifying the process for users. For more detailed control over embeddings, refer to the respective [JavaScript](https://js.langchain.com/v0.2/docs/integrations/text_embedding/openai#specifying-dimensions) and [Python](https://python.langchain.com/v0.2/docs/how_to/embed_text/#embed_query) documentation. ## Stream Chat Completions with OpenAI LangChain can find similar documents to the user's latest query and invoke the OpenAI API to power [chat completion](https://platform.openai.com/docs/guides/text-generation/chat-completions-api) responses, providing a seamless integration for creating dynamic interactions. Here's how you can power chat completions in an API route: ```tsx import { loadVectorStore } from './vectorStore'; import { pull } from 'langchain/hub'; import { ChatOpenAI } from '@langchain/openai'; import { createRetrievalChain } from 'langchain/chains/retrieval'; import type { ChatPromptTemplate } from '@langchain/core/prompts'; import { AIMessage, HumanMessage } from '@langchain/core/messages'; import { createStuffDocumentsChain } from 'langchain/chains/combine_documents'; const topK = 3; export async function POST(request: Request) { const llm = new ChatOpenAI(); const encoder = new TextEncoder(); const vectorStore = await loadVectorStore(); const { messages = [] } = await request.json(); const userMessages = messages.filter((i) => i.role === 'user'); const input = userMessages[userMessages.length - 1].content; const retrievalQAChatPrompt = await pull('langchain-ai/retrieval-qa-chat'); const retriever = vectorStore.asRetriever({ k: topK, searchType: 'similarity' }); const combineDocsChain = await createStuffDocumentsChain({ llm, prompt: retrievalQAChatPrompt, }); const retrievalChain = await createRetrievalChain({ retriever, combineDocsChain, }); const customReadable = new ReadableStream({ async start(controller) { const stream = await retrievalChain.stream({ input, chat_history: messages.map((i) => i.role === 'user' ? new HumanMessage(i.content) : new AIMessage(i.content) ), }); for await (const chunk of stream) { controller.enqueue(encoder.encode(chunk.answer)); } controller.close(); }, }); return new Response(customReadable, { headers: { Connection: 'keep-alive', 'Content-Encoding': 'none', 'Cache-Control': 'no-cache, no-transform', 'Content-Type': 'text/plain; charset=utf-8', }, }); } ``` ## Starter apps Hackable, fully-featured, pre-built [starter apps](https://github.com/neondatabase/examples/tree/main/ai/llamaindex) to get you up and running with LlamaIndex and Postgres. - [AI chatbot (OpenAI + LangChain)](https://github.com/neondatabase/examples/tree/main/ai/langchain/chatbot-nextjs): A Next.js AI chatbot starter app built with OpenAI and LangChain - [RAG chatbot (OpenAI + LangChain)](https://github.com/neondatabase/examples/tree/main/ai/langchain/rag-nextjs): A Next.js RAG chatbot starter app built with OpenAI and LangChain - [Semantic search chatbot (OpenAI + LangChain)](https://github.com/neondatabase/examples/tree/main/ai/langchain/semantic-search-nextjs): A Next.js Semantic Search chatbot starter app built with OpenAI and LangChain - [Chat with PDF (OpenAI + LangChain)](https://github.com/neondatabase/examples/tree/main/ai/langchain/chat-with-pdf-nextjs): A Next.js Chat with PDF chatbot starter app built with OpenAI and LangChain --- [Document source](https://neon.com/docs/ai/llamaindex.md) --- # LlamaIndex Build AI applications faster with LlamaIndex and Postgres LlamaIndex is a popular framework for working with AI, Vectors, and embeddings. LlamaIndex supports using Neon as a vector store, using the `pgvector` extension. ## Initialize Postgres Vector Store LlamaIndex simplifies the complexity of managing document insertion and embeddings generation using vector stores by providing streamlined methods for these tasks. Here's how you can initialize Postgres Vector with LlamaIndex: ```tsx // File: vectorStore.ts import { OpenAIEmbedding, Settings } from 'llamaindex'; import { PGVectorStore } from 'llamaindex/storage/vectorStore/PGVectorStore'; Settings.embedModel = new OpenAIEmbedding({ dimensions: 512, model: 'text-embedding-3-small', }); const vectorStore = new PGVectorStore({ dimensions: 512, connectionString: process.env.POSTGRES_URL, }); export default vectorStore; // Use in your code (say, in API routes) const index = await VectorStoreIndex.fromVectorStore(vectorStore); ``` ## Generate Embeddings with OpenAI LlamaIndex handles embedding generation internally while adding vectors to the Postgres database, simplifying the process for users. For more detailed control over embeddings, refer to the respective [JavaScript](https://ts.llamaindex.ai/docs/llamaindex/modules/models/embeddings/openai) and [Python](https://docs.llamaindex.ai/en/stable/examples/embeddings/OpenAI) documentation. ## Stream Chat Completions with OpenAI LlamaIndex can find similar documents to the user's latest query and invoke the OpenAI API to power [chat completion](https://platform.openai.com/docs/guides/text-generation/chat-completions-api) responses, providing a seamless integration for creating dynamic interactions. Here's how you can power chat completions in an API route: ```tsx import vectorStore from './vectorStore'; import { ContextChatEngine, VectorStoreIndex } from 'llamaindex'; interface Message { role: 'user' | 'assistant' | 'system' | 'memory'; content: string; } export async function POST(request: Request) { const encoder = new TextEncoder(); const { messages = [] } = (await request.json()) as { messages: Message[] }; const userMessages = messages.filter((i) => i.role === 'user'); const query = userMessages[userMessages.length - 1].content; const index = await VectorStoreIndex.fromVectorStore(vectorStore); const retriever = index.asRetriever(); const chatEngine = new ContextChatEngine({ retriever }); const customReadable = new ReadableStream({ async start(controller) { const stream = await chatEngine.chat({ message: query, chatHistory: messages, stream: true }); for await (const chunk of stream) { controller.enqueue(encoder.encode(chunk.response)); } controller.close(); }, }); return new Response(customReadable, { headers: { Connection: 'keep-alive', 'Content-Encoding': 'none', 'Cache-Control': 'no-cache, no-transform', 'Content-Type': 'text/plain; charset=utf-8', }, }); } ``` ## Starter apps Hackable, fully-featured, pre-built [starter apps](https://github.com/neondatabase/examples/tree/main/ai/llamaindex) to get you up and running with LlamaIndex and Postgres. - [AI chatbot (OpenAI + LllamIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/chatbot-nextjs): A Next.js AI chatbot starter app built with OpenAI and LlamaIndex - [RAG chatbot (OpenAI + LlamaIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/rag-nextjs): A Next.js RAG chatbot starter app built with OpenAI and LlamaIndex - [Semantic search chatbot (OpenAI + LlamaIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/semantic-search-nextjs): A Next.js Semantic Search chatbot starter app built with OpenAI and LlamaIndex - [Reverse image search (OpenAI + LlamaIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/reverse-image-search-nextjs): A Next.js Reverse Image Search Engine starter app built with OpenAI and LlamaIndex - [Chat with PDF (OpenAI + LlamaIndex)](https://github.com/neondatabase/examples/tree/main/ai/llamaindex/chat-with-pdf-nextjs): A Next.js Chat with PDF chatbot starter app built with OpenAI and LlamaIndex --- [Document source](https://neon.com/docs/ai/neon-mcp-server.md) --- # Neon MCP Server overview Learn about managing your Neon projects using natural language with Neon MCP Server The **Neon MCP Server** is an open-source tool that lets you interact with your Neon Postgres databases in **natural language**: - Manage projects, branches, and databases with conversational commands - Run SQL queries and make schema changes without writing code - Use branch-based migrations for safer schema modifications ## Quick setup The fastest way to set up Neon's MCP Server is with one command: ```bash npx neonctl@latest init ``` This configures the Neon MCP Server for compatible MCP clients in your workspace using API key authentication, including Cursor, VS Code, Claude Code, and other assistants [add-mcp can target](https://neon.com/docs/ai/connect-mcp-clients-to-neon#supported-agents-add-mcp). See the [neonctl init documentation](https://neon.com/docs/reference/cli-init). **If you only want the MCP server and nothing else**, use: ```bash npx add-mcp https://mcp.neon.tech/mcp ``` This command adds the required configuration to your editor's MCP config files; it does not open a browser by itself. Add `-g` for global (user-level) setup instead of project-level. Restart your editor (or enable the MCP server in your editor's settings). When you use the MCP connection, an OAuth window will open in your browser to authorize access to your Neon account. For more options (for example, global vs project-level), see the [add-mcp repository](https://github.com/neondatabase/add-mcp). **Other setup options:** - **API key authentication (remote agents):** For remote agents or when OAuth isn't available: ```bash npx add-mcp https://mcp.neon.tech/mcp --header "Authorization: Bearer $NEON_API_KEY" ``` - **Manual configuration:** See [Connect MCP clients](https://neon.com/docs/ai/connect-mcp-clients-to-neon) for step-by-step instructions for any editor, including Windsurf, ChatGPT, Zed, and others. After setup, restart your editor and ask your AI assistant to **"Get started with Neon"** to launch the interactive onboarding guide. --- Imagine you want to create a new database. Instead of using the Neon Console or API, you could just type a request like, "Create a database named 'my-new-database'". Or, to see your projects, you might ask, "List all my Neon projects". The Neon MCP Server makes this possible. It works by acting as a bridge between natural language requests and the [Neon API](https://api-docs.neon.tech/reference/getting-started-with-neon-api). Built upon the [Model Context Protocol (MCP)](https://modelcontextprotocol.org), it translates your requests into the necessary Neon API calls, allowing you to manage everything from creating projects and branches to running queries and performing database migrations. **Important: Neon MCP Server Security Considerations** The Neon MCP Server grants powerful database management capabilities through natural language requests. **Always review and authorize actions requested by the LLM before execution.** Ensure that only authorized users and applications have access to the Neon MCP Server. ## Other setup options **Remote (OAuth)** Connect to Neon's managed MCP server using OAuth. No API key configuration needed. ```bash npx add-mcp https://mcp.neon.tech/mcp ``` Or add this to your MCP config file: ```json { "mcpServers": { "neon": { "type": "http", "url": "https://mcp.neon.tech/mcp" } } } ``` **Tip: One-click install for Cursor** [cursor://anysphere.cursor-deeplink/mcp/install?name=Neon&config=eyJ1cmwiOiJodHRwczovL21jcC5uZW9uLnRlY2gvbWNwIn0%3D](https://neon.com/docs/ai/cursor://anysphere.cursor-deeplink/mcp/install?name=Neon\&config=eyJ1cmwiOiJodHRwczovL21jcC5uZW9uLnRlY2gvbWNwIn0%3D) After saving, restart your MCP client. When the OAuth window opens in your browser, review the requested permissions and click **Authorize** to complete the connection. **Remote (API Key)** Connect using API key authentication. Useful for remote agents where OAuth isn't available. **Requires:** [Neon API key](https://neon.com/docs/manage/api-keys) ```bash npx add-mcp https://mcp.neon.tech/mcp --header "Authorization: Bearer " ``` ### MCP-only setup (OAuth) If you only want the MCP server and prefer OAuth, run: ```bash npx add-mcp https://mcp.neon.tech/mcp ``` The command adds the config to your editor; restart your editor (or enable the MCP server) for it to take effect. When you use the MCP connection, an OAuth window will open in your browser; follow the prompts to authorize. For the recommended quick setup (API key + agent skills), use `npx neonctl@latest init` instead. **Tip: Install in a single click for Cursor users** Click the button below to install the Neon MCP server in Cursor. When prompted, click **Install** within Cursor. ```json { "mcpServers": { "neon": { "type": "http", "url": "https://mcp.neon.tech/mcp", "headers": { "Authorization": "Bearer <$NEON_API_KEY>" } } } } ``` **Note:** Use an organization API key to limit access to organization projects only. ### Manual setup 1. Go to your MCP Client's settings where you configure MCP Servers (this varies by client) 2. Register a new MCP Server. When prompted for the configuration, name the server "Neon" and add the following configuration: ```json { "mcpServers": { "Neon": { "type": "http", "url": "https://mcp.neon.tech/mcp" } } } ``` > MCP supports two remote server transports: the deprecated Server-Sent Events (SSE) and the newer, recommended Streamable HTTP. If your LLM client doesn't support Streamable HTTP yet, you can switch the endpoint from `https://mcp.neon.tech/mcp` to `https://mcp.neon.tech/sse` to use SSE instead. **Local** Run the MCP server locally on your machine. **Requires:** Node.js >= v18, [Neon API key](https://neon.com/docs/manage/api-keys) ```bash npx add-mcp "npx -y @neondatabase/mcp-server-neon start " --name neon ``` Or add this to your MCP config file: ```json { "mcpServers": { "neon": { "command": "npx", "args": ["-y", "@neondatabase/mcp-server-neon", "start", ""] } } } ``` **Note: Windows users** Use `cmd` or `wsl` if you encounter issues: **Windows** ```json { "mcpServers": { "neon": { "command": "cmd", "args": ["/c", "npx", "-y", "@neondatabase/mcp-server-neon", "start", ""] } } } ``` **Windows (WSL)** ```json { "mcpServers": { "neon": { "command": "wsl", "args": ["npx", "-y", "@neondatabase/mcp-server-neon", "start", ""] } } } ``` ## Supported actions (tools) The Neon MCP Server provides the following actions, which are exposed as "tools" to MCP clients. You can use these tools to interact with your Neon projects and databases using natural language commands. **Project management:** - `list_projects`: Lists the first 10 Neon projects in your account, providing a summary of each project. If you can't find a specific project, increase the limit by passing a higher value to the limit parameter. - `list_shared_projects`: Lists Neon projects shared with the current user. Supports a search parameter and limiting the number of projects returned (default: 10). - `describe_project`: Fetches detailed information about a specific Neon project, including its ID, name, and associated branches and databases. - `create_project`: Creates a new Neon project in your Neon account. A project acts as a container for branches, databases, roles, and computes. - `delete_project`: Deletes an existing Neon project and all its associated resources. - `list_organizations`: Lists all organizations that the current user has access to. Optionally filter by organization name or ID using the search parameter. **Branch management:** - `create_branch`: Creates a new branch within a specified Neon project. Leverages [Neon's branching](https://neon.com/docs/introduction/branching) feature for development, testing, or migrations. - `delete_branch`: Deletes an existing branch from a Neon project. - `describe_branch`: Retrieves details about a specific branch, such as its name, ID, and parent branch. - `list_branch_computes`: Lists compute endpoints for a project or specific branch, including compute ID, type, size, last active time, and autoscaling information. - `compare_database_schema`: Shows the schema diff between the child branch and its parent. - `reset_from_parent`: Resets the current branch to its parent's state, discarding local changes. Automatically preserves to backup if branch has children, or optionally preserve on request with a custom name. **SQL query execution:** - `get_connection_string`: Returns your database connection string. - `run_sql`: Executes a single SQL query against a specified Neon database. Supports both read and write operations. - `run_sql_transaction`: Executes a series of SQL queries within a single transaction against a Neon database. - `get_database_tables`: Lists all tables within a specified Neon database. - `describe_table_schema`: Retrieves the schema definition of a specific table, detailing columns, data types, and constraints. **Database migrations (schema changes):** - `prepare_database_migration`: Initiates a database migration process. Critically, it creates a temporary branch to apply and test the migration safely before affecting the main branch. - `complete_database_migration`: Finalizes and applies a prepared database migration to the main branch. This action merges changes from the temporary migration branch and cleans up temporary resources. **Query performance optimization:** - `list_slow_queries`: Identifies performance bottlenecks by finding the slowest queries in a database. Requires the pg_stat_statements extension. - `explain_sql_statement`: Provides detailed execution plans for SQL queries to help identify performance bottlenecks. - `prepare_query_tuning`: Analyzes query performance and suggests optimizations, like index creation. Creates a temporary branch for safely testing these optimizations. - `complete_query_tuning`: Finalizes query tuning by either applying optimizations to the main branch or discarding them. Cleans up the temporary tuning branch. **Neon Auth:** - `provision_neon_auth`: Provisions Neon Auth for a Neon project. It allows developers to easily set up authentication infrastructure by creating an integration with an Auth provider. **Neon Data API:** - `provision_neon_data_api`: Provisions the Neon Data API for a branch, enabling HTTP-based Data API access with optional JWT authentication. **Search and discovery:** - `search`: Searches across organizations, projects, and branches matching a query. Returns IDs, titles, and direct links to the Neon Console. - `fetch`: Fetches detailed information about a specific organization, project, or branch using an ID (typically from the search tool). **Documentation and resources:** - `list_docs_resources`: Lists all available Neon documentation pages by fetching the docs index. Returns page URLs and titles that can be fetched individually using the `get_doc_resource` tool. - `get_doc_resource`: Fetches a specific Neon documentation page as markdown content. Use the `list_docs_resources` tool first to discover available page slugs, then pass the slug to this tool. ## Troubleshooting If your client does not use JSON for configuration of MCP servers (such as older versions of Cursor), use this command when prompted: ```bash npx -y @neondatabase/mcp-server-neon start ``` **Note:** For clients that don't support Streamable HTTP, you can use the deprecated SSE endpoint: `https://mcp.neon.tech/sse`. SSE is not supported with API key authentication. ## Usage examples After setup, interact with your Neon databases using natural language: - `"Get started with Neon"`: Launch the interactive onboarding guide - `"List my Neon projects"` - `"Create a project named 'my-app'"` - `"Show tables in database 'main'"` - `"Search for 'production' across my Neon resources"` - `"SELECT * FROM users LIMIT 10"` ## MCP security guidance The Neon MCP server provides powerful database tools. We recommend MCP for **development and testing only**, not production environments. - Use MCP only for local development or IDE-based workflows - Never connect MCP agents to production databases - Avoid exposing production or PII data; use anonymized data only - Always review and authorize LLM-requested actions before execution - Restrict MCP access to trusted users and regularly audit access ## Resources - [MCP Protocol](https://modelcontextprotocol.org) - [Neon API Reference](https://api-docs.neon.tech/reference/getting-started-with-neon-api) - [Neon API Keys](https://neon.com/docs/manage/api-keys#creating-api-keys) - [Neon MCP server GitHub](https://github.com/neondatabase/mcp-server-neon) --- [Document source](https://neon.com/docs/ai/semantic-kernel.md) --- # Semantic Kernel Quickly build AI RAG and Agentic workflows with Semantic Kernel and Neon [Semantic Kernel](https://learn.microsoft.com/en-us/semantic-kernel/overview/) is an open-source SDK developed by Microsoft that enables the integration of large language models (LLMs) with traditional programming constructs. It allows developers to build AI-powered applications by combining natural language processing, planning, and memory capabilities. Semantic Kernel supports orchestration of AI workflows, plugin-based extensibility, and vector-based memory storage for retrieval-augmented generation (RAG) use cases. It is commonly used to create intelligent agents, chatbots, and automation tools that leverage LLMs like OpenAI's GPT models. ## Initialize Postgres Vector Store Semantic Kernel supports using Neon as a vector store, using its the `pgvector` extension and existing [Postgres Vector Store connector](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/postgres-connector?pivots=programming-language-csharp) to access and manage data in Neon. It establishes a Neon connection, enables vector support, and initializes a vector store for AI-driven search and retrieval tasks Here's how you can initialize Postgres Vector Store with Semantic Kernel in .NET using `Microsoft.SemanticKernel.Connectors.Postgres` NuGet package: ```csharp // File: Program.cs using Microsoft.SemanticKernel.Connectors.Postgres; using Npgsql; class Program { static void Main() { var connectionString = "Host=myhost;Username=myuser;Password=mypass;Database=mydb"; var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); dataSourceBuilder.UseVector(); using var dataSource = dataSourceBuilder.Build(); var vectorStore = new PostgresVectorStore(dataSource); Console.WriteLine("Vector store created successfully."); } } ``` ## Generate Embeddings with Azure OpenAI You can generate text embeddings using Azure OpenAI in the same .NET application. ```csharp // File: Program.cs using Microsoft.SemanticKernel.Connectors.Postgres; using Microsoft.SemanticKernel.Connectors.AzureOpenAI; using Npgsql; using System; using System.Threading.Tasks; class Program { static async Task Main() { string connectionString = "Host=myhost;Username=myuser;Password=mypass;Database=mydb"; // Create and configure the vector store var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); dataSourceBuilder.UseVector(); using var dataSource = dataSourceBuilder.Build(); var vectorStore = new PostgresVectorStore(dataSource); Console.WriteLine("Vector store created successfully."); // Generate embeddings using Azure OpenAI var embeddingService = new AzureOpenAITextEmbeddingGenerationService( deploymentName: "your-deployment-name", endpoint: "https://api.openai.com", apiKey: "your-api-key" ); string text = "This is an example sentence for embedding."; var embedding = await embeddingService.GenerateEmbeddingsAsync(new[] { text }); Console.WriteLine($"Generated Embedding: [{string.Join(", ", embedding[0].AsReadOnlySpan().Slice(0, 5))}...]"); } } ``` ## Chat Completions with Azure OpenAI Here is how you can run a chat completion query with Azure OpenAI and Semantic Kernel ```csharp // File: Program.cs using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.Postgres; using Microsoft.SemanticKernel.Connectors.AzureOpenAI; using Npgsql; using System; using System.Threading.Tasks; class Program { static async Task Main() { string connectionString = "Host=myhost;Username=myuser;Password=mypass;Database=mydb"; // Step 1: Create and configure the vector store var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); dataSourceBuilder.UseVector(); using var dataSource = dataSourceBuilder.Build(); var vectorStore = new PostgresVectorStore(dataSource); Console.WriteLine("✅ Vector store created successfully."); // Step 2: Generate embeddings using Azure OpenAI var embeddingService = new AzureOpenAITextEmbeddingGenerationService( deploymentName: "your-deployment-name", endpoint: "https://api.openai.com", apiKey: "your-api-key" ); string text = "This is an example sentence for embedding."; var embedding = await embeddingService.GenerateEmbeddingsAsync(new[] { text }); Console.WriteLine($"✅ Generated Embedding: [{string.Join(", ", embedding[0].AsReadOnlySpan().Slice(0, 5))}...]"); // Step 3: Perform chat completion using Azure OpenAI var kernel = Kernel.CreateBuilder() .AddAzureOpenAIChatCompletion( deploymentName: "your-chat-deployment-name", endpoint: "https://api.openai.com", apiKey: "your-api-key" ).Build(); string userPrompt = "Explain Retrieval-Augmented Generation (RAG) in simple terms."; var response = await kernel.InvokePromptAsync(userPrompt); Console.WriteLine("✅ Chat Completion Response:"); Console.WriteLine(response); } } ``` ## Examples Explore examples and sample code for using SemanticKernel with Neon Serverless Postgres. - [RAG .NET console app (Azure OpenAI + Semantic Kernel)](https://github.com/neondatabase-labs/neon-semantic-kernel-examples): A .NET RAG example app built with Azure OpenAI and Semantic Kernel --- [Document source](https://neon.com/docs/auth/authentication-flow.md) --- # Authentication flow Understanding the complete sign-in and sign-up process **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). This guide explains the authentication flow: how sign-in works from SDK call to session creation. **Note:** Anyone can sign up for your application by default. Support for restricted signups is coming soon. Until then, consider adding a verification step by enabling [email verification](https://neon.com/docs/auth/guides/email-verification) via verification link or verification code. ## Architecture overview Neon Auth is a managed REST API service built on Better Auth that connects directly to your Neon database. You use the SDK in your application and configure settings in the Console; no servers to manage. ``` Your App (SDK) ↓ HTTP requests Neon Auth Service (REST API) ↓ connects to database Your Neon Database (neon_auth schema) ``` All authentication data (users, sessions, OAuth configurations) lives in your database's `neon_auth` schema. You can query these tables directly with SQL for debugging, analytics, or custom logic. ## Complete sign-in flow Here's what happens when a user signs in, from your code to the database: ## User signs in Your application calls the SDK's sign-in method: ```typescript const { data, error } = await client.auth.signIn.email({ email: 'user@example.com', password: 'password', }); ``` The SDK posts to `{NEON_AUTH_URL}/auth/sign-in/email`. The Auth service validates credentials against `neon_auth.account`, creates a session in `neon_auth.session`, and returns the session cookie with user data. **Response you receive:** ```typescript { data: { session: { access_token: "eyJhbGc...", // JWT token expires_at: 1763848395, // ... other session fields }, user: { id: "dc42fa70-09a7-4038-a3bb-f61dda854910", email: "user@example.com", emailVerified: true, // ... other user fields } } } ``` ## Session cookie is set The Auth service sets an HTTP-only cookie (`__Secure-neonauth.session_token`) in your browser. This cookie: - Contains an opaque session token (not a JWT) - Is automatically sent with every request to the Auth API - Is secure (HTTPS only, HttpOnly, SameSite=None) - Is managed entirely by the SDK; you never touch it **Where to see it:** Open DevTools → Application → Cookies → look for `__Secure-neonauth.session_token` ## JWT token is retrieved The SDK automatically retrieves a JWT token and stores it in `session.access_token`. You don't need to call `/auth/token` separately; the SDK handles this behind the scenes. **What's in the JWT:** ```json { "sub": "dc42fa70-09a7-4038-a3bb-f61dda854910", // User ID "email": "user@example.com", "role": "authenticated", "exp": 1763848395, // Expiration timestamp "iat": 1763847495 // Issued at timestamp } ``` The `sub` claim contains the user ID from `neon_auth.user.id`. This is what Row Level Security policies use to identify the current user. ## JWT is used for database queries When you query your database via Data API, the SDK automatically includes the JWT in the `Authorization` header: ```typescript // JWT is automatically included in Authorization header const { data } = await client.from('posts').select('*'); ``` **What happens:** 1. SDK gets JWT from `session.access_token` 2. Adds `Authorization: Bearer ` header 3. Data API validates JWT signature using JWKS public keys 4. Data API extracts user ID from JWT and makes it available to RLS policies 5. Your query runs with the authenticated user context ## Sign-up flow The sign-up flow creates a new user: ```typescript const { data, error } = await client.auth.signUp.email({ email: 'newuser@example.com', password: 'securepassword', name: 'New User', }); ``` The SDK posts to `{NEON_AUTH_URL}/auth/sign-up/email`. The Auth service creates a new row in `neon_auth.user`, stores hashed credentials in `neon_auth.account`, and returns user data. If email verification is required, it creates a verification token in `neon_auth.verification` and may delay session creation until verification. **Note:** By default, anyone can sign up for your application. To add an additional verification layer, enable email verification (see [Email Verification](https://neon.com/docs/auth/guides/email-verification)). Built-in signup restrictions are coming soon. ## OAuth flow OAuth authentication (Google, GitHub, Vercel, etc.): ```typescript await client.auth.signIn.social({ provider: 'google', callbackURL: 'http://localhost:3000/auth/callback', }); ``` The SDK redirects to the OAuth provider. After the user authenticates, the provider redirects back with an authorization code. The SDK exchanges the code for an access token, then the Auth service creates or updates the user in `neon_auth.user`, stores OAuth tokens in `neon_auth.account`, and creates a session. ## Database as source of truth Neon Auth stores all data in your database's `neon_auth` schema: - Changes are immediate (no sync delays) - Query auth data directly with SQL - Each branch has isolated auth data - You own your data ```sql SELECT id, email, "emailVerified", "createdAt" FROM neon_auth.user ORDER BY "createdAt" DESC; ``` ## Data API integration When you enable the [Data API](https://neon.com/docs/data-api/get-started), JWT tokens from Neon Auth are validated automatically. The user ID is available via the `auth.uid()` function, enabling Row-Level Security policies to grant data access based on the authenticated user. The Data API also works with [other authentication providers](https://neon.com/docs/data-api/custom-authentication-providers) that issue JWTs, such as Auth0, Clerk, and Firebase. **Example RLS policy:** ```sql CREATE POLICY "Users can view own posts" ON posts FOR SELECT TO authenticated USING (user_id = auth.uid()); ``` **Learn more about securing your data:** - [Row-Level Security with Neon](https://neon.com/docs/guides/row-level-security) - [Simplify RLS with Drizzle](https://neon.com/docs/guides/rls-drizzle) ## What's next - [Branching Authentication](https://neon.com/docs/auth/branching-authentication): How auth works with database branches - [Row Level Security](https://neon.com/docs/guides/row-level-security): Secure your data with RLS policies --- [Document source](https://neon.com/docs/auth/branching-authentication.md) --- # Branching authentication How authentication works with Neon database branches **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Authentication is often one of the hardest parts of the application stack to test. In traditional architectures, identity data lives in a separate third-party service, while your business data lives in your database. This separation makes it difficult to create realistic staging environments or test changes to permissions without affecting production users. One of Neon Auth's unique features is native support for [database branching](https://neon.com/docs/introduction/branching). Because authentication data (users, sessions, and configuration) lives directly in your database's `neon_auth` schema, it is cloned along with your business data when you create a branch. This gives each branch its own isolated authentication environment, enabling safe testing of permission changes, new OAuth providers, or full application refactors. **Info:** Neon Auth branching is also supported via API. See the [Neon Auth API reference](https://api-docs.neon.tech/reference/getneonauth) for a full set of REST API endpoints. ## How it works When you create a [database branch](https://neon.com/docs/introduction/branching), you get an exact copy of all authentication data from the parent branch at that point in time: ``` Production (main) Preview Branch (preview-pr-123) ├── Users → ├── Users (copied at branch time) ├── Sessions → ├── Sessions (copied, but will expire) ├── Configuration → ├── Configuration (independent copy) ├── OAuth providers → ├── OAuth providers (same credentials) ├── JWKS keys → ├── JWKS keys (copied) └── Organizations → └── Organizations (copied) ``` After branching, the environments operate independently: 1. **Data Isolation:** Changes in one branch don't affect others. Creating a user in a preview branch does not create them in production. 2. **Config Isolation:** You can modify auth settings (for example, email templates, token settings) in the branch without affecting the parent. 3. **Endpoint Isolation:** Each branch gets a unique Auth API URL. Tokens issued in one branch are not valid in another. ``` Production Branch Preview Branch ├── New user: alice@co.com ├── New user: test@co.com ├── Alice's sessions ├── Test user's sessions ├── Config: email with links ├── Config: testing email codes └── ep-abc123.neonauth... └── ep-xyz789.neonauth... (production endpoint) (preview endpoint) ``` **Note:** Neon Auth works with your branch's **default** database (typically `neondb`) and read-write endpoint only. You cannot use Neon Auth with other databases in the same branch. This aligns with our recommended pattern of one database per branch. ## Session management details Sessions do not transfer between branches. If you sign in to your production app and then visit your staging environment: 1. The session _record_ exists in the staging database (if it was created before the branch happened). 2. However, your browser's _cookie_ is scoped to the production domain. 3. Therefore, you will need to sign in again on the staging environment. This isolation is intentional and prevents security issues like sessions accidentally working across environments or test actions affecting production users. ## Common Use Cases This branch isolation enables several powerful workflows for developers, QA teams, and product managers. ### 1. Developer isolation In a team environment, developers often step on each other's toes when sharing a single development database. With branching, each developer can have their own instance: ```bash filename="Terminal" # Alice and Bob create their own branches neon branches create --name dev-alice neon branches create --name dev-bob ``` - **Alice** works on a "Delete Account" flow. She can delete users and test the full flow without worrying about affecting others. - **Bob** works on the "User Dashboard". His user list remains intact, even though Alice is deleting users in her environment. Because Neon Auth is part of the database, Alice and Bob don't need to set up separate auth providers or mock data. They can work in parallel without conflicts. ### 2. Testing auth configuration safely Say you want to add Google OAuth to your production app, but you're not sure if your configuration will work. Instead of testing directly in production, create a branch: ```bash filename="Terminal" # Create test branch from production neon branches create --name test-google-oauth ``` ```env filename=".env.local" # Point your local app to the test branch's Auth URL VITE_NEON_AUTH_URL=https://ep-test-google-oauth.neonauth.region.aws.neon.tech/neondb/auth ``` Now configure Google OAuth in the test branch's Console and verify the sign-in flow works locally. Your production app and users are completely unaffected. Once you confirm it works, apply the same OAuth settings to your production branch. The same approach works for any auth changes: password reset flows, email verification settings, or testing with anonymized production data. ### 3. Preview environments for pull requests When building full-stack applications, you often deploy "Preview Deployments" (using Vercel, Netlify, etc.) for every Pull Request. Without Auth Branching, these previews usually share a single "Staging" auth tenant. This leads to data conflicts where one developer deletes a user that another developer was testing with. **The workflow:** You can automate this using GitHub Actions. When a PR is opened: 1. Create a Neon branch. 2. Deploy your frontend/backend to a preview URL. 3. Inject the **Branch Auth URL** into the preview deployment's environment variables. 4. Set the Redirect URLs in the branch's Auth configuration to point to the preview URL using the [Neon API](https://api-docs.neon.tech/reference/addbranchneonauthtrusteddomain). Because the branch contains a snapshot of production data, the preview environment is fully functional immediately. You can log in with real test accounts that exist in production, but any actions taken (changing passwords, updating profiles) happen in isolation. **Tip:** See the [GitHub Actions guide](https://neon.com/docs/guides/branching-github-actions) for instructions on how to automate branch creation for preview environments. ### 4. Testing multi-tenant & RBAC hierarchies For applications with complex Role-Based Access Control (RBAC) or multi-tenant architectures, testing permission changes can be risky. A misconfiguration could lock out users or expose sensitive data. **The scenario:** You are refactoring your RLS policies to allow "Managers" to view "Department" data, but not modify it. **The workflow:** 1. Create a branch `refactor-rbac`. 2. This branch contains your real production users and their existing role assignments. 3. Modify your RLS policies in the branch. 4. You can log in as a "Manager" user and verify they can only view the appropriate data. 5. If the policy is incorrect and you accidentally expose data or lock a user out, **it only affects the branch**. Production users are never impacted. ### 5. Major refactors and "v2" betas When rebuilding your application from scratch or launching a major "v2" update, you often need to validate the new system with real user data before the official switch-over. Traditionally, this required complex data dumps or asking users to re-register on a beta site. With Neon Auth, you can spin up a complete parallel environment for your new version instantly. **The workflow:** 1. **Branch production:** Create a branch named `v2-beta` from your main production database. This clones your entire application state, including the `neon_auth` schema containing all user identities and hashed passwords. 2. **Deploy v2:** Deploy your new application code (for example, to `beta.myapp.com`) and point it to the `v2-beta` branch's Auth URL. 3. **Seamless login:** Existing users can visit your new v2 site and **log in immediately using their existing credentials**. They do not need to sign up again or reset their passwords. This allows you to test radical architectural changes such as renaming database columns, changing table structures, or modifying authentication flows in a live environment. Your v1 application remains completely unaffected, while your v2 beta feels like a production-ready extension of your platform. **Note: Data separation** Remember that once branched, the environments are separate. If a user changes their password on the v2 site, it will not change on the v1 site, and vice versa. This makes this workflow ideal for "Public Betas" or staging environments prior to a final cutover. ### 6. AI agents and ephemeral sandboxes AI Agents, particularly those designed for coding or QA, require safe, isolated environments to generate code, run migrations, and validate features. Traditionally, giving an agent access to a full authentication stack was complex - you had to mock auth tokens or risk exposing production user pools. With Neon, an agent can programmatically provision its own "sandbox." Because Neon Auth moves with the data, this branch instantly creates a working Authentication service isolated from production, complete with its own user tables, sessions, and configuration. **This ensures your entire application stack mimics production behavior without risking real user data.** **The workflow:** 1. **Provision:** The Agent uses the Neon API to create a new database branch. It instantly receives a dedicated Auth URL for that specific environment. 2. **Interact:** The Agent uses tools like Playwright or Puppeteer to interact with the application, registering new users and performing real login flows against the branch's auth service. 3. **Validate:** The Agent runs a test suite to verify that the code it generated works correctly with the database schema, RLS policies, and authentication rules. 4. **Teardown:** Once the task is complete, the Agent deletes the branch, cleaning up all data and auth state. This capability allows agents to spin up "full stack" environments (Database + Auth + Compute) in seconds, enabling autonomous testing loops that rigorously test user-facing security without manual setup. **Important:** An AI agent cannot log in as a real production user in a branch. Although user records are copied, valid session cookies are domain-scoped and remain with the user's browser; they are not sent to the branch URL. Unless the agent explicitly knows a user's password, it must either perform a sign-up flow or use existing test credentials to log in. To streamline this, consider maintaining specific test users with known credentials in your production database; these records are automatically cloned to child branches during creation, allowing agents to log in immediately without needing to perform a sign-up step. ## What's Next - [Database Branching](https://neon.com/docs/introduction/branching): Learn about database branching fundamentals - [Branching with CLI](https://neon.com/docs/guides/branching-neon-cli): Create and manage branches with CLI - [Branching with GitHub Actions](https://neon.com/docs/guides/branching-github-actions): Automate branching in CI/CD - [Row-Level Security](https://neon.com/docs/guides/row-level-security): Secure data with RLS --- [Document source](https://neon.com/docs/auth/guides/configure-domains.md) --- # Configure trusted domains Add your application domains to enable secure authentication redirects **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Add your application domains to Neon Auth's allowlist to enable OAuth and email verification redirects in production. ## Why domains are required Neon Auth only redirects to domains in your allowlist. This prevents phishing attacks and unauthorized redirects by ensuring users are only sent to your legitimate application URLs. Without adding your production domain, OAuth sign-in and verification links will fail when users try to access your application. ## Add a domain 1. Go to **Console → Auth → Configuration → Domains** 2. Enter your domain with protocol: `https://myapp.com` 3. Click **Add domain** Repeat for each domain where your app runs. **Note:** Include the protocol (`https://`) and omit trailing slashes. For example: `https://myapp.com` not `https://myapp.com/` ## Localhost is pre-configured Development domains are automatically allowed, so you don't need to add them: - `http://localhost:3000` - `http://localhost:5173` - Any `localhost` port ## Production domains Add all domains where users access your application: - `https://myapp.com` - `https://www.myapp.com` (if you support www subdomain) - `https://app.myapp.com` (if using a subdomain) **Important:** Add each subdomain explicitly. Wildcards like `*.myapp.com` are not supported. ## Common issues **Redirect blocked after OAuth sign-in:** - Verify the domain is in your allowlist - Ensure you included `https://` (not `http://` for production) - Check spelling matches exactly (including www vs non-www) **Verification link doesn't redirect:** - Verification links use the same domain allowlist - Add the domain where users should land after clicking the verification link ## Next steps - [Production checklist](https://neon.com/docs/auth/production-checklist) - Complete setup for launch --- [Document source](https://neon.com/docs/auth/guides/email-verification.md) --- # Email verification Verify user email addresses during sign-up or account creation **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Email verification ensures users own the email addresses they register with. Neon Auth supports two verification methods: - **Verification codes** (users enter a numeric code from their email) - works with shared or custom email providers - **Verification links** (users click a link in their email) - requires a custom email provider **Note:** Verification links require a [custom email provider](https://neon.com/docs/auth/production-checklist#email-provider). If you're using the shared email provider, use verification codes instead. ## Enable email verification In your project's **Settings** → **Auth** page, enable **Sign-up with Email** and **Verify at Sign-up**. Choose your verification method. ![Email verification settings in Neon Console](https://neon.com/docs/auth/email-verification-settings.png) ## Verification links Verification links require a custom email provider. See [Email provider configuration](https://neon.com/docs/auth/production-checklist#email-provider) to set this up. When a user clicks the verification link in their email, the Neon Auth server handles verification and redirects them back to your application. Your app checks for the new session and shows the appropriate UI. ### 1. Check session on mount (#check-session-on-mount) Add a session check when your component mounts to detect when a user returns from clicking the verification link: ```jsx filename="src/App.jsx" {9-14} import { useEffect, useState } from 'react'; import { authClient } from './auth'; export default function App() { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { authClient.getSession().then(({ data }) => { if (data?.session) { setUser(data.session.user); } setLoading(false); }); }, []); } ``` ### 2. Handle sign-up with verification (#handle-signup-with-verification) After calling `signUp.email()`, check if verification is required and show a message: ```jsx {16-18} filename="src/App.jsx" const handleSignUp = async (e) => { e.preventDefault(); setMessage(''); try { const { data, error } = await authClient.signUp.email({ email, password, name: name || email.split('@')[0] || 'User', }); if (error) throw error; // Check if email verification is required if (data?.user && !data.user.emailVerified) { setMessage('Check your email for a verification link!'); } else { setMessage('Account created! Please sign in.'); } } catch (error) { setMessage(error?.message || 'An error occurred'); } }; ``` ### 3. Check verification status (#check-verification-status) Access the `emailVerified` field from the user object: ```jsx {3} filename="src/App.jsx" const { data } = await authClient.getSession(); if (data?.session?.user && !data.session.user.emailVerified) { // Show verification prompt or restrict features console.log('Please verify your email to continue'); } ``` ## Verification codes If you prefer verification codes, users receive a numeric code via email and enter it in your application. Your app switches between the auth form and a verification form. ### 1. Add verification state (#add-verification-state) Add state to track which form to show: ```jsx filename="src/App.jsx" const [step, setStep] = useState('auth'); // 'auth' or 'verify' const [code, setCode] = useState(''); ``` ### 2. Handle code verification (#handle-code-verification) Create a handler for code verification: ```jsx {6-9} filename="src/App.jsx" const handleVerify = async (e) => { e.preventDefault(); setMessage(''); try { const { data, error } = await authClient.emailOtp.verifyEmail({ email, otp: code, }); if (error) throw error; // Check if auto-sign-in is enabled (default behavior) if (data?.session) { setUser(data.session.user); setStep('auth'); } else { setMessage('Email verified! You can now sign in.'); setStep('auth'); setIsSignUp(false); setCode(''); } } catch (error) { setMessage(error?.message || 'An error occurred'); } }; ``` ### 3. Show verification form (#show-verification-form) When `step` is `'verify'`, show the verification form: ```jsx filename="src/App.jsx" if (step === 'verify') { return (

Verify Your Email

Enter the code sent to {email}

setCode(e.target.value)} required />
{message &&

{message}

}
); } ``` ### 4. Switch to verification after sign-up (#switch-to-verification) After calling `signUp.email()`, switch to the verification step: ```jsx {3} filename="src/App.jsx" if (data?.user && !data.user.emailVerified) { setMessage('Check your email for a verification code'); setStep('verify'); // Switch to verification form } ```
Complete example: App.jsx with verification codes Here's a complete, minimal `App.jsx` file that includes sign-up, sign-in, and verification code functionality: ```jsx filename="src/App.jsx" import { useState, useEffect } from 'react'; import { authClient } from './auth'; import './App.css'; export default function App() { const [session, setSession] = useState(null); const [user, setUser] = useState(null); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(true); const [message, setMessage] = useState(''); const [step, setStep] = useState('auth'); // 'auth' or 'verify' const [code, setCode] = useState(''); const [isSignUp, setIsSignUp] = useState(true); useEffect(() => { authClient.getSession().then((result) => { if (result.data?.session && result.data?.user) { setSession(result.data.session); setUser(result.data.user); } setLoading(false); }); }, []); const handleSignUp = async (e) => { e.preventDefault(); setMessage(''); const { data, error } = await authClient.signUp.email({ email, password, name: email.split('@')[0] || 'User', }); if (error) { setMessage(error.message); return; } if (data?.user && !data.user.emailVerified) { setMessage('Check your email for a verification code'); setStep('verify'); // Switch to verification form } else { const sessionResult = await authClient.getSession(); if (sessionResult.data?.session && sessionResult.data?.user) { setSession(sessionResult.data.session); setUser(sessionResult.data.user); } } }; const handleSignIn = async (e) => { e.preventDefault(); setMessage(''); const { data, error } = await authClient.signIn.email({ email, password }); if (error) { setMessage(error.message); return; } if (data?.session && data?.user) { setSession(data.session); setUser(data.user); } }; const handleVerify = async (e) => { e.preventDefault(); setMessage(''); try { const { data, error } = await authClient.emailOtp.verifyEmail({ email, otp: code, }); if (error) throw error; if (data?.session) { setSession(data.session); setUser(data.session.user); setStep('auth'); } else { setMessage('Email verified! You can now sign in.'); setStep('auth'); setIsSignUp(false); setCode(''); } } catch (error) { setMessage(error?.message || 'An error occurred'); } }; const handleSignOut = async () => { await authClient.signOut(); setSession(null); setUser(null); }; if (loading) return
Loading...
; if (session && user) { return (

Logged in as {user.email}

); } if (step === 'verify') { return (
{' '}

Verify Your Email

Enter the code sent to {email}

{' '} setCode(e.target.value)} required />{' '}
{' '} {message &&

{message}

}
); } return (

{isSignUp ? 'Sign Up' : 'Sign In'}

{message &&

{message}

}
setEmail(e.target.value)} required /> setPassword(e.target.value)} required />

); } ```
## Resending verification emails Both verification links and verification codes expire after **15 minutes**. Allow users to request a new one: ```jsx {3-6} filename="src/App.jsx" const handleResend = async () => { try { const { error } = await authClient.sendVerificationEmail({ email, callbackURL: window.location.origin + '/', }); if (error) throw error; setMessage('Verification email sent! Check your inbox.'); } catch (error) { setMessage(error?.message || 'An error occurred'); } }; ``` The server sends whichever type (verification link or verification code) you configured in the Console. ## Required vs optional verification When email verification is **required** in your Console settings, users cannot sign in until they verify. When verification is **optional**, users can sign in immediately but their `emailVerified` field remains `false` until verified. --- [Document source](https://neon.com/docs/auth/guides/manage-auth-api.md) --- # Manage Neon Auth via the API Enable, configure, and disable Neon Auth using the Neon API **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). You can manage Neon Auth programmatically using the [Neon API](https://api-docs.neon.tech/reference/getting-started). **Note:** Neon Auth operates at the **branch level**. Each branch can have its own independent auth configuration, which means preview and development branches can have separate auth state from your production branch. ## Prerequisites - A [Neon API key](https://neon.com/docs/manage/api-keys) - A Neon project with at least one branch All requests use the base URL `https://console.neon.tech/api/v2` and require the `Authorization: Bearer $NEON_API_KEY` header. The `project_id` and `branch_id` values are returned when you [create a project](https://neon.com/docs/manage/projects#create-a-project-with-the-api) or [list branches](https://neon.com/docs/manage/branches#list-branches-with-the-api) via the API. ## Enable Neon Auth Send a `POST` request to enable Neon Auth on a branch: ```bash curl -X POST 'https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth' \ -H 'Authorization: Bearer $NEON_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"auth_provider": "better_auth"}' ``` Response (201 Created): ```json { "auth_provider": "better_auth", "auth_provider_project_id": "cab6949a-10e3-4d25-a879-512beed281e3", "pub_client_key": "", "secret_server_key": "", "jwks_url": "https://ep-example.neonauth.us-east-1.aws.neon.tech/neondb/auth/.well-known/jwks.json", "schema_name": "neon_auth", "table_name": "users_sync", "base_url": "https://ep-example.neonauth.us-east-1.aws.neon.tech/neondb/auth" } ``` The response includes: | Field | Description | | -------------------------- | --------------------------------------------------------------------------------------------------------- | | `auth_provider` | The configured provider (`better_auth`) | | `auth_provider_project_id` | Unique ID for the auth provider instance | | `pub_client_key` | Public client key (shown once at creation, may be empty for `better_auth`) | | `secret_server_key` | Secret server key (shown once at creation, may be empty for `better_auth`) | | `jwks_url` | JWKS endpoint for JWT verification | | `schema_name` | Database schema created for auth tables (`neon_auth`) | | `table_name` | Table name for synced user data (`users_sync`) | | `base_url` | Base URL of the auth service, used for SDK configuration and the interactive API reference (`/reference`) | **Important:** The enable response is the only time the API returns `pub_client_key` and `secret_server_key`. Store them securely. Subsequent `GET` requests do not include these fields. If Neon Auth is already enabled on the branch, this call returns an error. **Tip: Using a non-default database** By default, Neon Auth uses the branch's default database. To target a different database, add `database_name` to the request body: `{"auth_provider": "better_auth", "database_name": "my_other_db"}` ## Get Auth configuration Retrieve the current Neon Auth configuration for a branch: ```bash curl -X GET 'https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth' \ -H 'Authorization: Bearer $NEON_API_KEY' ``` Response (200 OK): ```json { "auth_provider": "better_auth", "auth_provider_project_id": "cab6949a-10e3-4d25-a879-512beed281e3", "branch_id": "br-example-abc123", "db_name": "neondb", "created_at": "2026-02-26T04:29:05Z", "owned_by": "neon", "jwks_url": "https://ep-example.neonauth.us-east-1.aws.neon.tech/neondb/auth/.well-known/jwks.json", "base_url": "https://ep-example.neonauth.us-east-1.aws.neon.tech/neondb/auth" } ``` ## Disable Neon Auth Send a `DELETE` request to disable Neon Auth on a branch: ```bash curl -X DELETE 'https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth' \ -H 'Authorization: Bearer $NEON_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"delete_data": true}' ``` Response (200 OK): Empty body. The `delete_data` field controls whether the system removes the `neon_auth` schema from your database: - **`true`**: Deletes the `neon_auth` schema and all auth tables (users, sessions, accounts). - **`false`** (default): Disables the auth service but leaves the schema and data intact. You can re-enable later without losing user data. **Warning:** Setting `delete_data` to `true` permanently removes all auth data from the database. You cannot undo this. ## Related auth endpoints The Neon API also provides endpoints for managing auth configuration at the branch level. These are available at `https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth/...`: | Endpoint | Methods | Description | | --------------------- | ------------------------ | ---------------------------------------------------------------------------- | | `/domains` | GET, POST, DELETE | Manage trusted redirect domains | | `/oauth_providers` | GET, POST, PATCH, DELETE | Configure OAuth providers (Google, GitHub, etc.) | | `/email_provider` | GET, PATCH | Configure the email provider | | `/email_and_password` | GET, PATCH | Configure email/password authentication | | `/users` | POST, DELETE, PUT | Create, delete, and manage user roles | | `/plugins` | GET, PATCH | View and configure [auth plugins](https://neon.com/docs/auth/guides/plugins) | | `/webhooks` | GET, PUT | Configure webhook notifications | | `/allow_localhost` | GET, PATCH | Toggle localhost access for development | | `/send_test_email` | POST | Send a test email to verify email configuration | For full request/response details on these endpoints, see the [interactive API reference](https://api-docs.neon.tech/reference/getting-started). **Tip: TypeScript SDK** You can also manage Neon Auth using the [Neon TypeScript SDK](https://neon.com/docs/reference/typescript-sdk). --- [Document source](https://neon.com/docs/auth/guides/password-reset.md) --- # Password reset Allow users to reset forgotten passwords **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Password reset allows users to securely reset forgotten passwords. Neon Auth supports password reset via verification links sent to the user's email address. ## Enable password reset In your project's **Settings** → **Auth** page, ensure **Sign-up with Email** is enabled. Password reset is automatically available when email authentication is enabled. ## Using UI components The easiest way to add password reset is using the pre-built UI components `` and ``. ### 1. Enable forgot password in AuthView (#enable-forgot-password-authview) If you're using ``, enable the forgot password flow: ```tsx filename="src/App.tsx" import { NeonAuthUIProvider } from '@neondatabase/neon-js/auth/react'; import { AuthView } from '@neondatabase/neon-js/auth/react/ui'; import { authClient } from './auth'; export default function App() { return ( ); } ``` The `` component automatically includes a "Forgot password?" link when `forgotPassword` is enabled. ### 2. Use standalone form components (#use-standalone-forms) For more control, use `` and `` separately: ```tsx filename="src/App.tsx" import { useState } from 'react'; import { ForgotPasswordForm, ResetPasswordForm } from '@neondatabase/neon-js/auth/react/ui'; import { authClient } from './auth'; export default function App() { const [step, setStep] = useState<'forgot' | 'reset'>('forgot'); const [email, setEmail] = useState(''); if (step === 'forgot') { return ( { setEmail(data.email); setStep('reset'); }} /> ); } return ( { setStep('forgot'); // Redirect to sign-in or show success message }} /> ); } ``` **Note:** SDK methods for password reset (`resetPasswordForEmail`) are not fully supported yet. Use the UI components (`` and ``) for password reset functionality. ## Password reset flow The complete password reset flow works as follows: 1. **User requests reset**: User enters their email and clicks "Send reset link" 2. **Email sent**: User receives a verification link with a reset token 3. **User clicks link**: User is redirected to your app's reset password page 4. **User enters new password**: User submits the new password 5. **Password reset**: Password is updated and user is signed in (if auto-sign-in is enabled) ## Reset link expiration Password reset links expire after **15 minutes**. If a link expires, users need to request a new one. ## Next steps - [Add email verification](https://neon.com/docs/auth/guides/email-verification) to ensure users own their email addresses - [Learn how to branch your auth](https://neon.com/docs/auth/branching-authentication) to use database branches with isolated auth environments --- [Document source](https://neon.com/docs/auth/guides/plugins.md) --- # Plugins Supported Better Auth plugins in Neon Auth **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth is built on [Better Auth](https://www.better-auth.com/), which supports a variety of plugins to extend authentication functionality. **Info: Plugins are managed by Neon Auth** Neon Auth is a **managed** Better Auth service. You **don't install or configure Better Auth plugins directly** - instead, Neon Auth exposes a supported subset of plugins through the Neon SDK. For plugins that have Console settings (for example [Organization](https://neon.com/docs/auth/guides/plugins/organization)), open **Auth** > **Plugins** (beta) in the Neon Console, or use the Neon API. Additional plugin options may still arrive over time; see the [Neon Auth roadmap](https://neon.com/docs/auth/roadmap). The following Better Auth plugins are currently supported in Neon Auth: ## Supported plugins | Plugin | Status | | ---------------------------------------------------------------------- | ----------------------------------------------- | | [Admin](https://neon.com/docs/auth/guides/plugins/admin) | ✅ Supported | | [Email OTP](https://neon.com/docs/auth/guides/plugins/email-otp) | ✅ Supported | | [JWT](https://neon.com/docs/auth/guides/plugins/jwt) | ✅ Supported | | [Organization](https://neon.com/docs/auth/guides/plugins/organization) | ⚠️ Partial (JWT token claims under development) | | [Open API](https://neon.com/docs/auth/guides/plugins/openapi) | ✅ Supported | For more runnable Neon Auth samples, see [Example applications](https://neon.com/docs/auth/overview#example-applications). The **Organization** plugin demo is **[neon-auth-orgs-example](https://github.com/neondatabase/neon-js/tree/main/examples/neon-auth-orgs-example)**; see the [Organization plugin](https://neon.com/docs/auth/guides/plugins/organization) page for context. For the latest status (including what's coming next), see the [Neon Auth roadmap](https://neon.com/docs/auth/roadmap). --- [Document source](https://neon.com/docs/auth/guides/plugins/admin.md) --- # Admin Manage users, roles, bans, sessions, and impersonation **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth is built on [Better Auth](https://www.better-auth.com/) and provides support for Admin plugin APIs through the Neon SDK. You do not need to manually install or configure the Better Auth Admin plugin. The Admin plugin provides APIs to manage your users and their authentication state. It's commonly used to build internal tooling (admin dashboards, support tools) that can: - Create and update users - Assign roles - Ban and unban users - List and revoke sessions - Impersonate a user for support/debugging ## Prerequisites - A Neon project with **Auth enabled** - An existing user with an **admin** role to call Admin APIs. You can assign the **admin** role to a user through the Neon Console. Navigate to **Auth** → **Users**, open the three‑dot menu next to the user, and select **Make admin**. ![Assign admin role in Neon Console](https://neon.com/docs/auth/make-admin.png) ## Use Admin with SDK methods You can call Admin plugin methods using the Neon SDK auth client. > If you haven't set up Neon Auth yet, follow the [Next.js](https://neon.com/docs/auth/quick-start/nextjs-api-only) or [React](https://neon.com/docs/auth/quick-start/react) quick start to create an `authClient`. ### Create a user Use the Admin APIs to create users on behalf of others (for example, back-office onboarding). #### Parameters
View parameters | Parameter | Type | Required | Notes | | --------- | --------------------------------- | :------: | -------------------------------------------------------- | | email | string | ✓ | Email address for the new user | | password | string | ✓ | Password for the new user | | name | string | ✓ | Display name | | role | string \| string\[] \| undefined | | Optional role(s) for the user (for example: user, admin) | | data | Record\ \| undefined | | Optional custom fields |
```ts const { data, error } = await authClient.admin.createUser({ email: 'user@email.com', password: 'secure-password', name: 'User Name', role: 'user', data: { customUserField: 'value' }, }); ``` ### List users List users with optional search, filtering, sorting, and pagination. #### Parameters
View parameters | Parameter | Type | Required | Notes | | -------------- | ----------------------------------------------------------- | :------: | ------------------------------------ | | searchValue | string \| undefined | | Value to search for | | searchField | 'email' \| 'name' \| undefined | | Field to search in | | searchOperator | 'contains' \| 'starts\_with' \| 'ends\_with' \| undefined | | Search operator | | limit | number \| string \| undefined | | Max users to return (page size) | | offset | number \| string \| undefined | | Number of users to skip (pagination) | | sortBy | string \| undefined | | Field to sort by | | sortDirection | 'asc' \| 'desc' \| undefined | | Sort direction | | filterField | string \| undefined | | Field to filter by | | filterValue | string \| number \| boolean \| undefined | | Filter value | | filterOperator | 'eq' \| 'ne' \| 'lt' \| 'lte' \| 'gt' \| 'gte' \| undefined | | Filter operator |
```ts const { data, error } = await authClient.admin.listUsers({ query: { // Following parameters are optional searchValue: 'text to search', searchField: 'email', searchOperator: 'contains', limit: 10, offset: 0, sortBy: 'name', sortDirection: 'asc', }, }); ``` > Use `filterField`, `filterValue`, and `filterOperator` to further filter results (for example, by role etc) The `data` object contains a list of users and pagination metadata: ```ts { users: [/* array of user objects */], total: 100, // total number of users matching the query limit: 10, // limit used in the query offset: 0 // offset used in the query } ``` Use the `total`, `limit`, and `offset` values to implement pagination in your admin tooling. ### Set a user role Assign roles to control who can call admin operations. #### Parameters
View parameters | Parameter | Type | Required | Notes | | --------- | ------------------- | :------: | ------------------------------------- | | userId | string | ✓ | The user ID to update | | role | string \| string\[] | ✓ | Role(s) to apply (for example, admin) |
```ts const { error } = await authClient.admin.setRole({ userId: 'user-id', role: 'admin' }); ``` ### Set a user password Set or reset a user's password.
View parameters | Parameter | Type | Required | Notes | | ----------- | ------ | :------: | --------------------- | | userId | string | ✓ | The user ID to update | | newPassword | string | ✓ | The new password |
```ts const { error } = await authClient.admin.setUserPassword({ userId: 'user-id', newPassword: 'new-secure-password', }); ``` ### Update user details Update user information such as email, name, and custom fields.
View parameters | Parameter | Type | Required | Notes | | --------- | -------------------- | :------: | --------------------------------------------- | | userId | string | ✓ | The user ID to update | | data | Record\ | ✓ | Fields to update (email, name, custom fields) |
```ts const { error } = await authClient.admin.updateUser({ userId: 'user-id', data: { name: 'New Name' }, }); ``` ### Ban user Banning prevents sign-in for a user. You can optionally provide a reason and expiration for the ban.
View parameters | Parameter | Type | Required | Notes | | ------------ | ------------------- | :------: | ----------------------------------------------------------------------------------- | | userId | string | ✓ | The user ID to ban | | banReason | string \| undefined | | Reason for the ban | | banExpiresIn | number \| undefined | | Duration in seconds until the ban expires. If not provided, the ban does not expire |
```ts const { error } = await authClient.admin.banUser({ userId: 'user-id', banReason: 'Policy violation', // banExpiresIn: 60 * 60 * 24, // optional (seconds) }); ``` ### Unban user Unban a previously banned user.
View parameters | Parameter | Type | Required | Notes | | --------- | ------ | :------: | -------------------- | | userId | string | ✓ | The user ID to unban |
```ts const { error } = await authClient.admin.unbanUser({ userId: 'user-id' }); ``` ### Manage sessions Use session APIs to view active sessions and revoke them. #### List sessions
View parameters | Parameter | Type | Required | Notes | | --------- | ------ | :------: | ------------------------------------------- | | userId | string | ✓ | The user ID whose sessions you want to list |
```ts const { data, error } = await authClient.admin.listUserSessions({ userId: 'user-id' }); ``` #### Revoke a session
View parameters | Parameter | Type | Required | Notes | | ------------ | ------ | :------: | --------------------------- | | sessionToken | string | ✓ | The session token to revoke |
```ts const { error } = await authClient.admin.revokeUserSession({ sessionToken: 'session-token' }); ``` #### Revoke all sessions
View parameters | Parameter | Type | Required | Notes | | --------- | ------ | :------: | --------------------------------------------- | | userId | string | ✓ | The user ID whose sessions you want to revoke |
```ts const { error } = await authClient.admin.revokeUserSessions({ userId: 'user-id' }); ``` ### Impersonate a user Impersonation creates a session that behaves like the target user (useful for support and debugging).
View parameters | Parameter | Type | Required | Notes | | --------- | ------ | :------: | -------------------------- | | userId | string | ✓ | The user ID to impersonate |
```ts const { data, error } = await authClient.admin.impersonateUser({ userId: 'user-id' }); ``` ### Stop impersonation Stop an active impersonation session.
View parameters This method does not take any parameters.
```ts const { error } = await authClient.admin.stopImpersonating(); ``` ## Limitations - Admin operations require an authenticated session (HTTP-only cookies). This means your admin tooling must run on the same site that can send those cookies to the Neon Auth API. - Impersonation sessions are intentionally time‑limited, lasting for the duration of the active browser session or up to 1 hour. This design helps minimize security risks associated with long‑lived impersonation. --- [Document source](https://neon.com/docs/auth/guides/plugins/email-otp.md) --- # Email OTP Sign in and verify email addresses with one-time passwords **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth is built on [Better Auth](https://www.better-auth.com/) and provides full support for Email OTP plugin APIs through the Neon SDK. You do not need to manually install or configure the Better Auth Email OTP plugin. Email OTP lets users receive a one-time password (OTP) by email and use it to: - Sign in without a password - Perform password resets - Verify their email address (verification codes) Neon Auth UI and Neon SDK are client-side SDKs, so you only invoke their methods. OTP generation and delivery are handled automatically - you do not have direct control over the codes sent to users. ## Prerequisites - A Neon project with **Auth enabled** - **Sign-up and Sign-in with Email** enabled in your project's **Settings** → **Auth**. **Note: Email verification during sign-up** To use Email OTP for sign-up verification, enable **Verify at Sign-up** and select **Verification code** under **Verification method**. ![Email OTP verification code setting](https://neon.com/docs/auth/email-otp.png) ## Use Email OTP with UI components If you're using Neon Auth UI components, enable Email OTP by passing the `emailOTP` prop to `NeonAuthUIProvider`. This enables OTP flows in the pre-built auth UI. ```tsx filename="app/layout.tsx" import { authClient } from '@/lib/auth/client'; import { NeonAuthUIProvider } from '@neondatabase/neon-js/auth/react/ui'; import './globals.css'; export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) { return ( {children} ); } ``` Users can now sign in with Email OTP by selecting the option on the sign-in screen and entering the one-time code sent to their email. ![Email OTP verification](https://neon.com/docs/auth/email-otp-verification.png) > If you haven't set up Neon Auth UI components yet, see the [UI components reference](https://neon.com/docs/auth/reference/ui-components) and the [Next.js](https://neon.com/docs/auth/quick-start/nextjs-api-only) or [React](https://neon.com/docs/auth/quick-start/react) quick start. ## Use Email OTP with SDK methods You can also implement OTP flows directly using the [Neon SDK](https://neon.com/docs/reference/javascript-sdk). ### Send an OTP To send an OTP, call `emailOtp.sendVerificationOtp()` and specify a `type`: - `sign-in` - passwordless sign-in - `email-verification` - verify an email address ```ts filename="src/send-otp.ts" import { authClient } from './auth'; export async function sendSignInOtp(email: string) { const { error } = await authClient.emailOtp.sendVerificationOtp({ email, type: 'sign-in' }); if (error) throw error; } ``` > For more details, see the [Send verification OTP code](https://neon.com/docs/reference/javascript-sdk#auth-sendverificationotp) in Neon SDK. ### Sign in with OTP After the user receives the code, sign them in using `signIn.emailOtp()`: ```ts filename="src/sign-in-with-otp.ts" import { authClient } from './auth'; export async function signInWithOtp(email: string, otp: string) { const { data, error } = await authClient.signIn.emailOtp({ email, otp }); if (error) throw error; return data; } ``` > For more details, see the [Sign in with OTP code](https://neon.com/docs/reference/javascript-sdk#auth-signinwithemailotp) in Neon SDK. ### Verify email with OTP If your project has email verification enabled with **verification codes**, Neon Auth sends an OTP during sign-up. Once the user enters the code, verify the email address using `emailOtp.verifyEmail()`: ```ts filename="src/verify-email.ts" import { authClient } from './auth'; export async function verifyEmail(email: string, otp: string) { const { data, error } = await authClient.emailOtp.verifyEmail({ email, otp }); if (error) throw error; return data; } ``` > For more details, see the [Verify email with OTP code](https://neon.com/docs/reference/javascript-sdk#auth-verifyemail) in Neon SDK. Checkout our [Email verification guide](https://neon.com/docs/auth/guides/email-verification) for a complete walkthrough. ### Check an OTP (optional) If you want to validate an OTP without completing the flow (for example, to check the code before enabling a sensitive UI), you can use `emailOtp.checkVerificationOtp()`: ```ts filename="src/otp.ts" import { authClient } from './auth'; export async function isOtpValid(email: string, otp: string) { const { data, error } = await authClient.emailOtp.checkVerificationOtp({ email, otp, type: 'sign-in', }); if (error) throw error; return Boolean(data?.success); } ``` > For more details, see the [Check verification OTP code](https://neon.com/docs/reference/javascript-sdk#auth-checkverificationotp) in Neon SDK. ## Reset Password with OTP You can also use Email OTP to implement password reset flows. To do this use the `authClient.forgetPassword.emailOtp` method to send a password reset OTP to the user's email address. ```ts filename="src/send-reset-otp.ts" import { authClient } from './auth'; export async function sendPasswordResetOtp(email: string) { const { error } = await authClient.forgetPassword.emailOtp({ email }); if (error) throw error; } ``` Once the user receives the OTP, verify it using `authClient.emailOtp.checkVerificationOtp()` with the `type` set to `forget-password`. ```ts filename="src/verify-reset-otp.ts" import { authClient } from './auth'; export async function verifyPasswordResetOtp(email: string, otp: string) { const { data, error } = await authClient.emailOtp.checkVerificationOtp({ email, otp, type: 'forget-password', }); if (error) throw error; return Boolean(data?.success); } ``` Finally, reset the user's password using `authClient.emailOtp.resetPassword()`: ```ts filename="src/reset-password.ts" import { authClient } from './auth'; export async function resetPasswordUsingOtp(email: string, otp: string, newPassword: string) { const { data, error } = await authClient.emailOtp.resetPassword({ email, otp, password: newPassword, }); } ``` ## Limitations Email OTP codes are time-limited and rate-limited. If users exceed the allowed verification attempts, the API returns an error code like `TOO_MANY_ATTEMPTS` and the user must request a new code. ## Email provider configuration For production environments, we strongly recommend using a dedicated email provider. The default shared SMTP should be used only during development. Refer to the [Email provider configuration guide](https://neon.com/docs/auth/production-checklist#email-provider) for setup instructions. --- [Document source](https://neon.com/docs/auth/guides/plugins/jwt.md) --- # JWT Authenticate using JSON Web Tokens (JWT) for external services **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth is built on [Better Auth](https://www.better-auth.com/) and provides support for JWT plugin APIs through the Neon SDK. You do not need to manually install or configure the Better Auth JWT plugin. While Neon Auth primarily relies on **secure, HTTP‑only cookies** (sessions) for browser‑based authentication, certain scenarios require a raw token. In these cases, the JWT plugin is especially useful: - **Microservices:** Sharing identity between backend services. - **Separate frontend and backend domains:** Authenticating API requests from a domain different than your main application. - **CLI tools:** Enabling authentication from the command‑line interface. **Warning: Sessions vs. JWTs** This plugin is **not** a replacement for session management in web applications. For standard browser-based apps (Next.js, React, Vue, etc.), rely on the default session cookie mechanism provided by `authClient.signIn` and `authClient.getSession`. Only use JWTs when you specifically need to authorize requests to services that cannot access the browser's cookie jar. ## Prerequisites - A Neon project with **Auth enabled**. ## Retrieve a Token You can retrieve a JWT for the currently signed-in user using the Neon SDK. ### Using the SDK method To fetch a raw token string, use the `authClient.token()` method. This is the recommended approach for client applications that need to attach a token to an API request header manually. ```ts filename="src/get-token.ts" import { authClient } from './auth'; export async function getJwtToken() { const { data, error } = await authClient.token(); if (error) throw error; // The token string (for example, "eyJhbGciOiJFZ...") return data.token; } ``` ### Using the session header When you call `authClient.getSession()`, Neon Auth automatically includes a JWT in the response headers. If you are using a custom fetcher or need to intercept the token immediately after a session check: ```ts await authClient.getSession({ fetchOptions: { onSuccess: (ctx) => { const jwt = ctx.response.headers.get('set-auth-jwt'); console.log('JWT:', jwt); }, }, }); ``` ### Example decoded JWT payload A typical decoded JWT payload looks like this: ```json { "iat": 1766320685, "name": "User Name", "email": "user@email.com", "emailVerified": false, "image": null, "createdAt": "2025-12-20T11:04:41.437Z", "updatedAt": "2025-12-20T11:04:41.437Z", "role": "authenticated", "banned": false, "banReason": null, "banExpires": null, "id": "860dc360-609f-4b7d-9e70-ec93fe6414d3", "sub": "860dc360-609f-4b7d-9e70-ec93fe6414d3", "exp": 1766321585, "iss": "", "aud": "" } ``` ## Verify a token To verify the authenticity of a JWT, you need to validate its signature using the public keys provided by JWKS (JSON Web Key Set). Neon Auth exposes a public JWKS endpoint that contains the public keys necessary to verify the signature of your JWTs. ### The JWKS endpoint Your Neon Auth JWKS endpoint is located at: ``` /.well-known/jwks.json ``` ### Verification example The following examples demonstrate how to verify a Neon Auth JWT in several programming languages. No matter which language you use, the process is the same: fetch the JWKS from the provided endpoint and use it to validate the token's signature and claims. If your preferred language isn't included here, you can apply these same principles in your own environment. **Info: Production Readiness** The following examples are provided for reference only and are not guaranteed to be production‑ready. Be sure to implement proper caching, error handling, and security best practices as required for your application. **Node.js** 1. Install the library: ```bash npm install jose ``` 2. Use the following example code as a reference to verify a JWT: ```ts import { jwtVerify, createRemoteJWKSet } from 'jose'; const NEON_JWKS_URL = `${process.env.NEON_AUTH_BASE_URL}/.well-known/jwks.json`; const JWKS = createRemoteJWKSet(new URL(NEON_JWKS_URL)); export async function validateNeonToken(token: string) { try { const { payload } = await jwtVerify(token, JWKS, { issuer: new URL(process.env.NEON_AUTH_BASE_URL!).origin }); return payload; } catch (error) { console.error('Token validation failed:', error); return null; } } validateNeonToken().then((payload) => { console.log('Token is valid. Payload:', payload); }).catch(() => { console.log('Token is invalid.'); }); ``` > Replace `` with the actual JWT token you want to verify. **Python** 1. Install the library: ```bash pip install PyJWT requests cryptography ``` 2. Use the following example code as a reference to verify a JWT: ```py import base64 import os from urllib.parse import urlparse import jwt import requests from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey from jwt import PyJWTError NEON_AUTH_BASE_URL = os.environ.get("NEON_AUTH_BASE_URL", "") NEON_JWKS_URL = f"{NEON_AUTH_BASE_URL}/.well-known/jwks.json" parsed = urlparse(NEON_AUTH_BASE_URL) ORIGIN = f"{parsed.scheme}://{parsed.netloc}" def get_jwks(): response = requests.get(NEON_JWKS_URL) response.raise_for_status() return response.json() def get_signing_key(token, jwks): unverified_header = jwt.get_unverified_header(token) kid = unverified_header["kid"] for jwk in jwks["keys"]: if jwk["kid"] == kid: x = jwk["x"] public_key_bytes = base64.urlsafe_b64decode(x + "==") return Ed25519PublicKey.from_public_bytes(public_key_bytes) raise ValueError("Matching JWK not found") def validate_neon_token(token: str): try: jwks = get_jwks() signing_key = get_signing_key(token, jwks) payload = jwt.decode( token, key=signing_key, algorithms=["EdDSA"], issuer=ORIGIN, audience=ORIGIN ) return payload except PyJWTError as error: print("Token validation failed:", error) return None except Exception as error: print("Unexpected error:", error) return None payload = validate_neon_token("") if payload: print("Token is valid. Payload:", payload) else: print("Token is invalid.") ``` > Replace `` with the actual JWT token you want to verify. **Go** 1. Install the library: ```bash go get github.com/golang-jwt/jwt/v5 go get github.com/MicahParks/keyfunc/v3 ``` 2. Use the following example code as a reference to verify a JWT: ```go package main import ( "fmt" "log" "net/url" "os" "github.com/MicahParks/keyfunc/v3" "github.com/golang-jwt/jwt/v5" ) func ValidateNeonToken(tokenString string) (jwt.MapClaims, error) { baseURL := os.Getenv("NEON_AUTH_BASE_URL") if baseURL == "" { return nil, fmt.Errorf("NEON_AUTH_BASE_URL is not set") } jwksURL := fmt.Sprintf("%s/.well-known/jwks.json", baseURL) u, err := url.Parse(baseURL) if err != nil { return nil, fmt.Errorf("failed to parse base URL: %w", err) } expectedIssuer := fmt.Sprintf("%s://%s", u.Scheme, u.Host) jwks, err := keyfunc.NewDefault([]string{jwksURL}) if err != nil { return nil, fmt.Errorf("failed to create JWKS from resource at %s: %w", jwksURL, err) } token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return jwks.Keyfunc(token) }, jwt.WithIssuer(expectedIssuer), jwt.WithValidMethods([]string{"EdDSA"}), ) if err != nil { return nil, err } if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { return claims, nil } return nil, fmt.Errorf("invalid token claims") } func main() { tokenString := "" claims, err := ValidateNeonToken(tokenString) if err != nil { log.Printf("Token validation failed: %v\n", err) return } fmt.Printf("Token is valid. Payload: %+v\n", claims) } ``` > Replace `` with the actual JWT token you want to verify. ## Limitations Because Neon Auth is a managed service, certain server-side configurations available in the standalone Better Auth library are pre-configured by Neon and cannot be changed: - **Signing algorithm:** Neon Auth uses **EdDSA (Ed25519)** by default for high security and performance. Ensure your verification libraries support this algorithm. - **Expiration:** Tokens expire in **15 minutes** (access tokens). You should implement logic to refresh the token using `authClient.token()` when it expires. - **Custom claims:** Currently, the JWT payload contains the default user information. Custom claims are not supported at this time. ## Troubleshooting ### Token rejection If a token is rejected during verification, check the following: 1. Verify that you are using the correct JWKS endpoint for your Neon Auth instance. The issuer of the token must match the origin of your Neon Auth URL. (for example, if your Neon Auth URL is `https://ep-xx.aws.neon.tech/neondb/auth`, the issuer should be `https://ep-xx.aws.neon.tech`). 2. Confirm that your verification library supports **EdDSA** (Ed25519). 3. Make sure the token has not expired. 4. Check that the `kid` in the JWT header matches one of the keys in the JWKS response. If not, fetch the latest keys from the JWKS endpoint. --- [Document source](https://neon.com/docs/auth/guides/plugins/openapi.md) --- # Open API Interactive API documentation and client generation **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth is built on [Better Auth](https://www.better-auth.com/) and comes with the Open API plugin enabled by default. You do not need to manually install or configure it. The OpenAPI plugin provides two main features: 1. An interactive **API reference UI** (powered by [Scalar](https://scalar.com/)) to explore and test all available Neon Auth endpoints. 2. A **JSON Schema endpoint** that you can use to generate type-safe clients for your application. ## Prerequisites - A Neon project with **Auth enabled**. ## Accessing the API reference You can view the interactive documentation for your specific project by appending `/reference` to your Neon Auth URL. **URL Format:** ``` /reference ``` **Example:** `https://ep-xxx.aws.neon.tech/neondb/auth/reference` This interface allows you to: - Browse all available endpoints (grouped by Core, Session, User, Plugins, etc). - View request and response schemas. - **"Try it out"**: Make real API requests against your database directly from the browser. ![Neon Auth Open API Reference](https://neon.com/docs/auth/openapi-reference.png) **Note: Testing endpoints** When using the "Try it out" feature, you are interacting with your live database. Be careful when testing endpoints that modify or delete data (like `admin.banUser` or `organization.delete`). ## JSON schema If you need the raw OpenAPI 3.x specification (for example, to import into Postman or Insomnia), it is available at the `/open-api/generate-schema` endpoint. **URL format:** ``` /open-api/generate-schema ``` This endpoint returns a standard JSON object describing your authentication API. ## Generating API clients One of the most powerful use cases for the OpenAPI plugin is generating type-safe API clients for languages that do not yet have a dedicated Neon Auth SDK, or for server-side integration where you prefer raw fetch calls. ### Using Scalar SDK generator If you're using Next.js, you can access the OpenAPI reference page at `/api/auth/reference` in your application, once Neon Auth is set up according to the [Next.js quick start](https://neon.com/docs/auth/quick-start/nextjs-api-only). Unlike the default Neon Auth reference page, this version can be fully customized to match your application's theme. It also provides additional options, such as generating SDK clients in multiple languages using Scalar's [built‑in tools](https://guides.scalar.com/scalar/scalar-sdks/getting-started). ## Limitations Because Neon Auth is a managed service, the OpenAPI configuration is preset: - **Path:** The reference UI is always served at `/reference`. - **Theme:** The UI uses the default Scalar theme and cannot be customized. - **Discovery:** The reference doc automatically reflects the plugins enabled in your Neon Auth instance. --- [Document source](https://neon.com/docs/auth/guides/plugins/organization.md) --- # Organization Manage multi-tenant organizations, members, and invitations **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth is built on [Better Auth](https://www.better-auth.com/) and comes with a pre-configured Organization plugin, so your app can support multi-tenancy without additional setup. **Note: Preview Feature** The Organization plugin is currently in **Beta**. Support for JWT token claims is under development. ## Why use this plugin? Use the Organization plugin when you need multi-tenancy in your app. It supports: - Multi-tenant apps where each tenant is an organization - Workspaces or groups that share a Neon branch (same database) - Inviting users and assigning roles: owner, admin, or member - Role-based access: owners and admins can manage the org; the member role has read-only access Better Auth also has a **Teams** feature (sub-groups within an org); that feature is not currently enabled in Neon Auth. ## Prerequisites - A Neon project with **Auth enabled** - A signed-in user (organizations are associated with users) ## Example application **[neon-auth-orgs-example](https://github.com/neondatabase/neon-js/tree/main/examples/neon-auth-orgs-example)** is a multi-tenant sample that uses the Organization plugin with **Drizzle** and **`@neondatabase/auth`** (see that folder's README for **bun** setup from the monorepo root). For other runnable Neon Auth apps, see [Example applications](https://neon.com/docs/auth/overview#example-applications). ## Configure the organization plugin The Organization plugin is enabled by default for each branch; you can disable it or change settings in the Console or via the API. If the plugin is disabled, your application users and admins cannot create or manage organizations, and any organization-related API calls will return an error. **Console** Open your project in the Neon Console, then go to **Auth** > **Plugins** (beta) and use the **Organizations** section (per branch). This tab is available when your project uses Neon Auth with **Better Auth**; other Auth settings, including your **Auth URL**, stay on the **Configuration** tab. From there you can customize: ![Neon Console Auth Plugins tab with Organizations settings](https://neon.com/docs/changelog/neon_auth_plugins_organizations.png) - **Enable Organizations** (toggle): Turn the Organization plugin on or off for the branch. When off, all organization API calls are disabled and return an error. - **Limit:** Maximum total organization memberships (created + joined) per user. Once reached, the user cannot create new organizations. Default: 10. - **Membership Limit:** Maximum number of members per organization (default: 100). - **Creator role:** Role assigned to the user who creates an organization: **Owner** or **Admin**. Choose Admin if you want the org creator to have fewer privileges than Owner (for example, they cannot delete the org or change the owner). - **Send Invitation Email** (toggle): When on, invited users receive an email with an accept link. This requires **Verify email at signup** to be enabled in the Authentication configuration. Accepting the invitation requires the [`AuthView` component](https://neon.com/docs/auth/reference/ui-components#core-components) or a custom route that handles `/auth/accept-invitation?invitationId=` in your application. When off, no email is sent and you handle invitations in your app (for example, via the [invitation ID](https://neon.com/docs/auth/guides/plugins/organization#accept-invitation) or the [user invitation list](https://neon.com/docs/auth/guides/plugins/organization#list-user-invitations)). **API** You can also configure the plugin via the [Neon API](https://api-docs.neon.tech/reference/getting-started-with-neon-api). Use your API key in the `Authorization` header. **Get current plugin config (including organization):** ```bash curl -X GET \ 'https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth/plugins' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer $NEON_API_KEY' ``` Example response (excerpt showing the `organization` object): ```json { "organization": { "enabled": true, "organization_limit": 10, "membership_limit": 100, "creator_role": "owner", "send_invitation_email": false } } ``` The full response includes other plugin configs (email provider, OAuth, etc.). `creator_role` is either `owner` or `admin`. **Update the organization plugin:** Send only the fields you want to change; all request body fields are optional. ```bash curl -X PATCH \ 'https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth/plugins/organization' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer $NEON_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "enabled": true, "organization_limit": 10, "membership_limit": 100, "creator_role": "owner", "send_invitation_email": false }' ``` Example response: ```json { "enabled": true, "organization_limit": 10, "membership_limit": 100, "creator_role": "owner", "send_invitation_email": false } ``` ### API fields reference | Field | Type | Description | | :---------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | | `enabled` | boolean | Turn the Organization plugin on or off for the branch. When false, all organization API calls return an error. | | `organization_limit` | number (≥ 1) | Max total organization memberships (created + joined) per user. Once reached, the user cannot create new organizations. Default: 10. | | `membership_limit` | number (≥ 1) | Max members per organization. Default: 100. | | `creator_role` | string (`owner` \| `admin`) | Role for the user who creates an org. Owner has full control; Admin cannot delete the org or change the owner. | | `send_invitation_email` | boolean | When true, invited users receive an email with an accept link. Requires verified email at signup. Default: false. | ### API Documentation - [Get all plugin configurations](https://api-docs.neon.tech/reference/getneonauthpluginconfigs) - [Update organization plugin configuration](https://api-docs.neon.tech/reference/updateneonauthorganizationplugin) ## Organizations Use the following methods to manage the organization lifecycle. ### Create an organization Creates a new organization. The user creating it automatically becomes the **Owner**.
View parameters | Parameter | Type | Required | Notes | | :---------------------------- | :--------------------------------- | :------: | :-------------------------------------------------------------------- | | name | string | ✓ | The display name of the organization | | slug | string | ✓ | A URL-friendly identifier | | logo | string \| undefined | | Optional URL to a logo image for the organization | | metadata | `Record` \| undefined | | Optional JSON metadata for the organization | | userId | string \| undefined | | The user ID of the organization creator | | keepCurrentActiveOrganization | boolean \| undefined | | If `true`, does not switch the active session to the new organization |
```ts const { data, error } = await authClient.organization.create({ name: 'My Organization', slug: 'my-org', logo: 'https://example.com/logo.png', metadata: { plan: 'pro' }, keepCurrentActiveOrganization: false, }); ``` ### Check organization slug Checks if an organization slug is available.
View parameters | Parameter | Type | Required | Notes | | :-------- | :----- | :------: | :---------------- | | slug | string | ✓ | The slug to check |
```ts const { data, error } = await authClient.organization.checkSlug({ slug: 'my-org', }); ``` ### List organizations Lists all organizations the current user is a member of.
View parameters This method does not take any parameters.
```ts const { data, error } = await authClient.organization.list(); ``` In a React component, you can use the `useListOrganizations` hook to fetch and display organizations: ```tsx import { authClient } from './auth'; export default function OrganizationList() { const { data: organizations } = authClient.useListOrganizations(); return (
{organizations?.map((org) => (

{org.name}

))}
); } ``` ### Set active organization Switches the user's active context to a specific organization.
View parameters | Parameter | Type | Required | Notes | | :--------------- | :------------------ | :------: | :--------------------------------------------- | | organizationId | string \| null | | The ID to set as active. Pass `null` to unset. | | organizationSlug | string \| undefined | | Alternatively, pass the slug to set as active. |
```ts const { data, error } = await authClient.organization.setActive({ organizationId: 'org_12345678', }); ``` ### Get active organization Retrieves full details of the currently active organization.
View parameters | Parameter | Type | Required | Notes | | :--------------- | :------------------ | :------: | :------------------------------------------------------ | | organizationId | string \| undefined | | Optional ID to get details for (defaults to active org) | | organizationSlug | string \| undefined | | Optional slug to get details for | | membersLimit | number \| undefined | | Limit members returned in the response (default: 100) |
```ts const { data, error } = await authClient.organization.getFullOrganization({ query: { organizationId: 'org-id', organizationSlug: 'org-slug', membersLimit: 10, }, }); ``` In a React component, you can use the `useActiveOrganization` hook to fetch and display the active organization: ```tsx import { authClient } from './auth'; export default function ActiveOrganization() { const { data: organization } = authClient.useActiveOrganization(); return (

{organization?.name}

Members: {organization?.members.length}

); } ``` ### Update organization Updates organization details. Requires **Owner** or **Admin** permissions.
View parameters | Parameter | Type | Required | Notes | | :------------- | :----- | :------: | :---------------------------------------------------------------------- | | data | object | ✓ | Object containing fields to update (`name`, `slug`, `logo`, `metadata`) | | organizationId | string | | The ID of the organization to update |
```ts await authClient.organization.update({ data: { name: 'New Name', metadata: { plan: 'enterprise' }, }, organizationId: 'org-id', }); ``` ### Delete organization Deletes the organization and all associated data. Requires **Owner** permission.
View parameters | Parameter | Type | Required | Notes | | :------------- | :----- | :------: | :----------------------------------- | | organizationId | string | ✓ | The ID of the organization to delete |
```ts const { data, error } = await authClient.organization.delete({ organizationId: 'org-id', }); ``` ## Invitations Manage invitations to join an organization. **Note: Invitation Emails** Invitation emails are supported when [**Send Invitation Email**](https://neon.com/docs/auth/guides/plugins/organization#configure-the-organization-plugin) is enabled in the organization config. This also requires **Verify email at signup** in the Authentication configuration. Accepting an email invitation requires the [`AuthView` component](https://neon.com/docs/auth/reference/ui-components#core-components) or a custom route handling `/auth/accept-invitation?invitationId=` in your app. When the email toggle is off, handle invitations in your app using the [invitation ID](https://neon.com/docs/auth/guides/plugins/organization#accept-invitation) or the [user invitation list](https://neon.com/docs/auth/guides/plugins/organization#list-user-invitations). ### Invite member Sends an invitation to a user.
View parameters | Parameter | Type | Required | Notes | | :------------- | :------------------- | :------: | :-------------------------------------------------- | | email | string | ✓ | Email address to invite | | role | string | ✓ | Role to assign (`owner`, `admin`, `member`) | | organizationId | string \| undefined | | ID of the organization (defaults to active org) | | resend | boolean \| undefined | | If true, resends email if invitation already exists |
```ts const { data, error } = await authClient.organization.inviteMember({ email: 'new-user@example.com', role: 'member', resend: true, }); ``` ### Accept invitation Accepts an invitation using the invitation ID.
View parameters | Parameter | Type | Required | Notes | | :----------- | :----- | :------: | :------------------------------ | | invitationId | string | ✓ | The ID from the invitation link |
```ts const { data, error } = await authClient.organization.acceptInvitation({ invitationId: 'invitation-id', }); ``` ### Reject invitation Declines an invitation that the user has received and chooses not to accept.
View parameters | Parameter | Type | Required | Notes | | :----------- | :----- | :------: | :--------------------------------- | | invitationId | string | ✓ | The ID of the invitation to reject |
```ts const { data, error } = await authClient.organization.rejectInvitation({ invitationId: 'invitation-id', }); ``` ### Cancel invitation Cancel a pending invitation that has been sent to a user.
View parameters | Parameter | Type | Required | Notes | | :----------- | :----- | :------: | :--------------------------------- | | invitationId | string | ✓ | The ID of the invitation to cancel |
```ts const { data, error } = await authClient.organization.cancelInvitation({ invitationId: 'invitation-id', }); ``` ### Get invitation Retrieves details of a specific invitation.
View parameters | Parameter | Type | Required | Notes | | :-------- | :----- | :------: | :----------------------------------- | | query.id | string | ✓ | The ID of the invitation to retrieve |
```ts const { data, error } = await authClient.organization.getInvitation({ query: { id: 'invitation-id', }, }); ``` ### List invitations Lists all pending invitations for an organization.
View parameters | Parameter | Type | Required | Notes | | :------------------- | :------------------ | :------: | :---------------------------------- | | query.organizationId | string \| undefined | | Defaults to the active organization |
```ts const { data, error } = await authClient.organization.listInvitations({ query: { organizationId: 'org-id', }, }); ``` ### List user invitations Lists all invitations received by the current user.
View parameters This method does not take any parameters.
```ts const { data, error } = await authClient.organization.listUserInvitations(); ``` ## Members Manage users within the organization. ### List members Lists members with support for pagination, sorting, and filtering.
View parameters | Parameter | Type | Required | Notes | | :------------------- | :-------------------------------------- | :------: | :------------------------------------------ | | query.organizationId | string \| undefined | | Defaults to the active organization | | query.limit | number \| undefined | | Items per page (default: 100) | | query.offset | number \| undefined | | Items to skip | | query.sortBy | string \| undefined | | Field to sort by (for example, `createdAt`) | | query.sortDirection | "asc" \| "desc" \| undefined | | Sort direction | | query.filterField | string \| undefined | | Field to filter by | | query.filterOperator | "eq" \| "ne" \| "gt" \| "contains" etc. | | Operator for filtering | | query.filterValue | string \| undefined | | Value to filter for |
```ts const { data, error } = await authClient.organization.listMembers({ query: { limit: 20, offset: 0, sortBy: 'createdAt', sortDirection: 'desc', filterField: 'role', filterOperator: 'eq', filterValue: 'admin', }, }); ``` ### Update member role Updates a member's role.
View parameters | Parameter | Type | Required | Notes | | :------------- | :------------------ | :------: | :------------------------------------------------- | | memberId | string | ✓ | The ID of the member to update | | role | string\| string\[] | ✓ | New role(s) to assign (`owner`, `admin`, `member`) | | organizationId | string \| undefined | | Defaults to active organization |
```ts const { data, error } = await authClient.organization.updateMemberRole({ memberId: 'member-id', role: 'admin', }); ``` ### Remove member Removes a member from the organization.
View parameters | Parameter | Type | Required | Notes | | :-------------- | :------------------ | :------: | :------------------------------ | | memberIdOrEmail | string | ✓ | Member ID or Email address | | organizationId | string \| undefined | | Defaults to active organization |
```ts const { data, error } = await authClient.organization.removeMember({ memberIdOrEmail: 'member-id-or-email', }); ``` ### Get active member Gets the current user's membership details for the active organization.
View parameters This method does not take any parameters.
```ts const { data, error } = await authClient.organization.getActiveMember(); ``` ### Get Active Member Role Gets the current user's role(s) in the active organization.
View parameters This method does not take any parameters.
```ts const { data, error } = await authClient.organization.getActiveMemberRole(); ``` ### Leave organization Removes the current user from an organization.
View parameters | Parameter | Type | Required | Notes | | :------------- | :----- | :------: | :---------------------------------- | | organizationId | string | ✓ | The ID of the organization to leave |
```ts const { data, error } = await authClient.organization.leave({ organizationId: 'org-id', }); ``` ## Access Control The Organization plugin includes a Role-Based Access Control (RBAC) system. | Role | Permissions | | :--------- | :----------------------------------------------------------------------------------------------------------------------------------------- | | **Owner** | Full control. Can delete the organization and manage all roles. The user who creates the organization is automatically assigned this role. | | **Admin** | Can invite members, update roles, and manage organization settings. Cannot delete the organization. | | **Member** | Read-only access to organization data. Cannot manage other members. | You can check permissions on the client side using `checkRolePermission`: ```ts const canDelete = authClient.organization.checkRolePermission({ permission: { organization: ['delete'], }, role: 'admin', // returns false, admins cannot delete orgs }); // console.log(canDelete); // false ``` ## Limitations Because Neon Auth is a managed service, some Better Auth features are not currently supported: - **Teams:** The Teams sub-feature is not currently enabled. - **Hooks:** Server-side hooks (for example, `beforeCreateOrganization`) are not supported. - **Custom Permissions:** You cannot currently define custom roles or modify default permissions. - **Dynamic Access Control:** Dynamic creation of roles via API is not enabled. Check the [Neon Auth roadmap](https://neon.com/docs/auth/roadmap) for updates on these features. --- [Document source](https://neon.com/docs/auth/guides/setup-oauth.md) --- # Set up OAuth Add Google or GitHub sign-in to your application **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). OAuth lets users sign in with their Google, GitHub, or Vercel account. Neon Auth handles the OAuth flow and creates a session after authorization. ## Development mode Google OAuth is enabled by default with shared credentials for development and testing. You can start using Google sign-in immediately without any configuration. **Note:** GitHub and Vercel OAuth require custom credentials and is not available with shared credentials. See [Production setup](https://neon.com/docs/auth/guides/setup-oauth#production-setup) to configure your own OAuth apps. For production, configure your own OAuth app credentials for both providers. See [Production setup](https://neon.com/docs/auth/guides/setup-oauth#production-setup) below. ## Sign in with OAuth Call `signIn.social()` with your provider (`"google"`, `"github"` or `"vercel"`). The SDK redirects the user to the provider's authorization page, then back to your `callbackURL`: **Google** ```jsx {6} filename="src/App.jsx" import { authClient } from './auth'; const handleGoogleSignIn = async () => { try { await authClient.signIn.social({ provider: "google", callbackURL: window.location.origin, }); } catch (error) { console.error("Google sign-in error:", error); } }; ``` **GitHub** ```jsx {6} filename="src/App.jsx" import { authClient } from './auth'; const handleGitHubSignIn = async () => { try { await authClient.signIn.social({ provider: "github", callbackURL: window.location.origin, }); } catch (error) { console.error("GitHub sign-in error:", error); } }; ``` **Vercel** ```jsx {6} filename="src/App.jsx" import { authClient } from './auth'; const handleVercelSignIn = async () => { try { await authClient.signIn.social({ provider: "vercel", callbackURL: window.location.origin, }); } catch (error) { console.error("Vercel sign-in error:", error); } }; ``` ## Handle the callback After the provider redirects back to your app, check for a session: ```jsx {4-9} filename="src/App.jsx" import { authClient } from './auth'; useEffect(() => { authClient.getSession().then(({ data }) => { if (data?.session) { setUser(data.session.user); } setLoading(false); }); }, []); ``` ## Custom redirect URLs Specify different URLs for new users or errors: ```jsx {3-5} filename="src/App.jsx" await authClient.signIn.social({ provider: "google", // or "github", "vercel" callbackURL: "/dashboard", newUserCallbackURL: "/welcome", errorCallbackURL: "/error", }); ``` ## Production setup For production, configure your own OAuth app credentials. GitHub and Vercel OAuth require custom credentials, while Google OAuth works with shared credentials for development but should use custom credentials in production. 1. Create OAuth apps with your providers: - [Google OAuth setup](https://developers.google.com/identity/protocols/oauth2/web-server) (see [Google OAuth branding](https://neon.com/docs/auth/guides/setup-oauth#google-oauth-branding) below before going live) - [GitHub OAuth setup](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) - [Vercel OAuth setup](https://vercel.com/docs/sign-in-with-vercel/manage-from-dashboard#create-an-app) 2. In your project's **Settings** → **Auth** page, configure your Client ID and Client Secret for each provider Your app will automatically use your configured credentials ## Google OAuth branding When using your own Google OAuth credentials, users will see a consent screen before signing in. Without branding configured, Google displays the `redirect_uri` domain (**"Continue to neon.tech"**) instead of your app's name. This happens even when your OAuth client ID and secret are correctly configured. To show your app's name on the consent screen: 1. Go to [Google Cloud Console → OAuth consent screen](https://console.cloud.google.com/auth/branding) 2. Fill in the required app information: - **App name**: the name users will see on the consent screen - **User support email**: a contact email for users with auth questions - **Developer contact information**: your email address (not shown to users) 3. Under **Authorized domains**, add your app's domain (for example, `myapp.com`) 4. Save your changes **Important: Verification required for public apps** Apps in **Testing** status will still show the redirect_uri domain ("neon.tech") to users outside your test user list, regardless of branding settings. To show your app's name to all users, you must publish the app and submit it for Google verification. Verification typically takes a few business days but can take several weeks depending on the OAuth scopes requested. - [Email Verification](https://neon.com/docs/auth/guides/email-verification): Add email verification --- [Document source](https://neon.com/docs/auth/guides/user-management.md) --- # User management Update profiles, change passwords, and manage account settings **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Manage user profiles and account settings after users sign in. This guide covers: - Updating profile information (name, image, phone number) - Changing passwords securely - Changing email addresses with verification - Deleting user accounts ## Update user profile Update user profile fields like name, image, or phone number using `updateUser()`: ```jsx filename="src/App.jsx" import { authClient } from './auth'; const handleUpdateProfile = async (e) => { e.preventDefault(); setMessage(''); try { const { data, error } = await authClient.updateUser({ name: 'New Name', }); if (error) throw error; // Refresh session to get updated user data const sessionResult = await authClient.getSession(); if (sessionResult.data?.session) { setUser(sessionResult.data.session.user); setMessage('Profile updated successfully!'); } } catch (error) { setMessage(error?.message || 'Update failed'); } }; ``` ### Available profile fields You can update these fields with `updateUser()`: - `name` (string) - User's display name **Note:** Email address changes are not currently supported. To reset a forgotten password, see [Password Reset](https://neon.com/docs/auth/guides/password-reset). ## Change password Change a user's password while they are logged in using `changePassword()`. This requires the current password for security: ```jsx filename="src/App.jsx" import { authClient } from './auth'; const handleChangePassword = async (e) => { e.preventDefault(); setMessage(''); try { const { data, error } = await authClient.changePassword({ newPassword: 'new-secure-password', currentPassword: 'current-password', }); if (error) throw error; setMessage('Password changed successfully!'); } catch (error) { setMessage(error?.message || 'Password change failed'); } }; ``` ### Revoke other sessions Optionally sign out from all other devices when changing the password: ```jsx filename="src/App.jsx" const { data, error } = await authClient.changePassword({ newPassword: 'new-secure-password', currentPassword: 'current-password', revokeOtherSessions: true, // Signs out all other devices }); ``` **Note:** If a user forgot their password, use the password reset flow (`requestPasswordReset()` and `resetPassword()`) instead. See [Password Reset](https://neon.com/docs/auth/guides/password-reset). ## Refresh user data After updating profile information, refresh the session to get the latest user data: ```jsx filename="src/App.jsx" import { authClient } from './auth'; const refreshUser = async () => { const { data } = await authClient.getSession(); if (data?.session) { setUser(data.session.user); } }; ``` Call `refreshUser()` after successful `updateUser()` calls to ensure your UI displays the latest information. - [Password Reset](https://neon.com/docs/auth/guides/password-reset): Reset forgotten passwords - [Email Verification](https://neon.com/docs/auth/guides/email-verification): Verify email addresses - [Authentication Flow](https://neon.com/docs/auth/authentication-flow): Understand the auth flow --- [Document source](https://neon.com/docs/auth/guides/webhooks.md) --- # Webhooks Handle authentication events with custom server logic **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth webhooks send HTTP POST requests to your server when authentication events occur. By default, Neon Auth handles OTP and magic link delivery through its built-in email provider. Webhooks let you replace this with your own delivery channels (SMS, custom email templates, WhatsApp) so you control how verification messages reach your users. Webhooks also let you hook into the user creation lifecycle to validate signups before they happen or sync new user data to external systems like CRMs and analytics platforms. For a step-by-step Next.js walkthrough that implements signature verification, custom OTP and magic link emails with Resend, blocking signups by email domain, optional SMS delivery, and local testing with ngrok, see [Customizing Neon Auth with Webhooks](https://neon.com/guides/neon-auth-webhooks-nextjs). ## Supported events | Event | Type | Trigger | Use case | | -------------------- | ------------ | ------------------------------------------------ | --------------------------------------------------- | | `send.otp` | Blocking | OTP code needs delivery | Custom OTP delivery via SMS or email service | | `send.magic_link` | Blocking | Magic link needs delivery | Custom link delivery via any channel | | `user.before_create` | Blocking | User attempts to sign up (before database write) | Signup validation, allowlists, user data enrichment | | `user.created` | Non-blocking | User created in the database | Sync to CRM, analytics, post-signup workflows | **Blocking** events pause the auth flow until your server responds (or the timeout expires). **Non-blocking** events are fire-and-forget; failures do not affect the user. When you subscribe to `send.otp` or `send.magic_link`, Neon Auth skips its built-in email delivery for that event. Your webhook handler is responsible for delivering the code or link. ## Configure webhooks Configure webhooks per project and branch using the Neon API. Your webhook URL must use HTTPS protocol. See the API reference for [Get webhook configuration](https://api-docs.neon.tech/reference/getneonauthwebhookconfig) and [Update webhook configuration](https://api-docs.neon.tech/reference/updateneonauthwebhookconfig). ```bash PUT /projects/{project_id}/branches/{branch_id}/auth/webhooks GET /projects/{project_id}/branches/{branch_id}/auth/webhooks ``` Both endpoints use the following fields: | Field | Type | Description | | ----------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `enabled` | boolean (required) | Enable or disable webhook delivery | | `webhook_url` | string | HTTPS endpoint to receive webhook POST requests | | `enabled_events` | string\[] | Event types to subscribe to: `send.otp`, `send.magic_link`, `user.before_create`, `user.created` | | `timeout_seconds` | integer (1-10) | Per-attempt timeout in seconds. Default: 5. Total delivery time across all attempts is capped at 15 seconds. See [Retry behavior](https://neon.com/docs/auth/guides/webhooks#retry-behavior). | ### Set or update configuration ```bash curl -X PUT "https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth/webhooks" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $NEON_API_KEY" \ -d '{ "enabled": true, "webhook_url": "https://your-app.com/webhooks/neon-auth", "enabled_events": ["send.otp", "send.magic_link", "user.before_create", "user.created"], "timeout_seconds": 5 }' ``` ### Get current configuration ```bash curl "https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth/webhooks" \ -H "Authorization: Bearer $NEON_API_KEY" ``` Both endpoints return the configuration in the same format: ```json { "enabled": true, "webhook_url": "https://your-app.com/webhooks/neon-auth", "enabled_events": [ "send.otp", "send.magic_link", "user.before_create", "user.created" ], "timeout_seconds": 5 } ``` ### Delete a webhook To delete a webhook and stop receiving authentication events, update your configuration by setting the `enabled` field to `false` using the update endpoint. This disables the webhook and resumes Neon Auth's default delivery behavior for all events. ```bash curl -X PUT "https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth/webhooks" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $NEON_API_KEY" \ -d '{ "enabled": false }' ``` ## Payload structure All events share a common JSON envelope: ```json { "event_id": "550e8400-e29b-41d4-a716-446655440000", "event_type": "send.otp", "timestamp": "2026-02-23T12:00:00.000Z", "context": { "endpoint_id": "ep-cool-sound-12345678", "project_name": "My SaaS App" }, "user": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "email": "user@example.com", "name": "Jane Smith", "email_verified": false, "created_at": "2026-02-23T12:00:00.000Z" }, "event_data": { "otp_code": "123456", "otp_type": "sign-in", "expires_at": "2026-02-23T12:10:00.000Z", "ip_address": "192.0.2.1", "user_agent": "Mozilla/5.0" } } ``` The `user` object fields are all optional and vary by event. Available fields: `id`, `email`, `name`, `phone_number`, `image`, `email_verified`, `phone_number_verified`, `created_at`. ### `send.otp` event data | Field | Type | Description | | --------------------- | ----------------- | ----------------------------------------------------------- | | `otp_code` | string | 6-digit OTP code | | `otp_type` | string | `"sign-in"`, `"email-verification"`, or `"forget-password"` | | `delivery_preference` | string (optional) | `"email"` or `"sms"` | | `expires_at` | ISO datetime | Expiry time | | `ip_address` | string | Requester's IP address | | `user_agent` | string | Requester's user agent | ### `send.magic_link` event data | Field | Type | Description | | ------------ | ------------ | --------------------------------------------- | | `link_type` | string | `"email-verification"` or `"forget-password"` | | `link_url` | string | Full verification URL with embedded token | | `token` | string | Raw token for building custom redirect URLs | | `expires_at` | ISO datetime | Expiry time | | `ip_address` | string | Requester's IP address | | `user_agent` | string | Requester's user agent | Magic links do not include a `delivery_preference` field. Your webhook handler determines the delivery channel. ### `user.before_create` and `user.created` event data These events fire only when a new user record is created in the database. They do not fire on subsequent sign-ins, including returning OAuth users. | Field | Type | Description | | --------------- | ------ | ----------------------------------------------------- | | `auth_provider` | string | `"credential"`, `"google"`, `"github"`, or `"vercel"` | | `ip_address` | string | Requester's IP address | | `user_agent` | string | Requester's user agent | ## Signature verification Neon Auth uses asymmetric EdDSA (Ed25519) signatures with detached JWS, so key rotation does not require reconfiguring your endpoint. Verify signatures before processing webhooks. ### Request headers Each webhook request includes the following headers: | Header | Description | | ------------------------- | ---------------------------------------------- | | `X-Neon-Signature` | Detached JWS signature (`header..signature`) | | `X-Neon-Signature-Kid` | Key ID for looking up the public key from JWKS | | `X-Neon-Timestamp` | Unix timestamp in milliseconds | | `X-Neon-Event-Type` | Event type (for example, `user.created`) | | `X-Neon-Event-Id` | Unique event UUID | | `X-Neon-Delivery-Attempt` | Attempt number: 1, 2, or 3 | Example incoming webhook request: ```http POST /webhooks/neon-auth HTTP/1.1 Content-Type: application/json X-Neon-Signature: eyJhbGciOiJFZERTQSIsInR5cCI6IkpXUyIsImtpZCI6IjAxZGVjNTJiIn0..MEUCIQDZ8Qs X-Neon-Signature-Kid: 01dec52b-4666-40f7-87ed-6423552eecaf X-Neon-Timestamp: 1740312000000 X-Neon-Event-Type: send.otp X-Neon-Event-Id: 550e8400-e29b-41d4-a716-446655440000 X-Neon-Delivery-Attempt: 1 {"event_id":"550e8400-e29b-41d4-a716-446655440000","event_type":"send.otp",...} ``` ### Verification steps 1. Fetch your JWKS from `/.well-known/jwks.json`. Find the key where `kid` matches the `X-Neon-Signature-Kid` header. 2. Parse the detached JWS from `X-Neon-Signature`. The format is `header..signature` (empty middle section). 3. Reconstruct the signing input using standard JWS with double base64url encoding: - `payloadB64 = base64url(rawRequestBody)` - `signaturePayload = timestamp + "." + payloadB64` - `signaturePayloadB64 = base64url(signaturePayload)` - `signingInput = header + "." + signaturePayloadB64` 4. Verify the Ed25519 signature against the signing input using the public key. The double base64url encoding occurs because the timestamp is bound into the JWS payload per RFC 7515 Compact Serialization. ### Idempotency and additional checks Retries send the same `X-Neon-Event-Id`. Your endpoint should track this value and return the same response for duplicate deliveries. This is especially important for `user.before_create`, where a lost response triggers a retry with the same event. Consider rejecting requests where `X-Neon-Timestamp` is more than 5 minutes old to prevent replay attacks. ### Node.js example ```javascript import crypto from 'node:crypto'; async function verifyWebhook(rawBody, headers) { const signature = headers['x-neon-signature']; const kid = headers['x-neon-signature-kid']; const timestamp = headers['x-neon-timestamp']; // 1. Fetch JWKS and find the matching key const res = await fetch(`${process.env.NEON_AUTH_URL}/.well-known/jwks.json`); const jwks = await res.json(); const jwk = jwks.keys.find((k) => k.kid === kid); if (!jwk) throw new Error(`Key ${kid} not found in JWKS`); // 2. Import the Ed25519 public key const publicKey = crypto.createPublicKey({ key: jwk, format: 'jwk' }); // 3. Parse detached JWS (header..signature) const [headerB64, emptyPayload, signatureB64] = signature.split('.'); if (emptyPayload !== '') throw new Error('Expected detached JWS format'); // 4. Reconstruct signing input (standard JWS, double base64url encoding) const payloadB64 = Buffer.from(rawBody, 'utf8').toString('base64url'); const signaturePayload = `${timestamp}.${payloadB64}`; const signaturePayloadB64 = Buffer.from(signaturePayload, 'utf8').toString('base64url'); const signingInput = `${headerB64}.${signaturePayloadB64}`; // 5. Verify Ed25519 signature const isValid = crypto.verify( null, Buffer.from(signingInput), publicKey, Buffer.from(signatureB64, 'base64url') ); if (!isValid) throw new Error('Invalid webhook signature'); // 6. Check timestamp freshness (recommended) const ageMs = Date.now() - parseInt(timestamp, 10); if (ageMs > 5 * 60 * 1000) throw new Error('Webhook timestamp too old'); return JSON.parse(rawBody); } ``` **Important:** Preserve the raw request body before JSON parsing. If your framework parses the body automatically, save the raw bytes first. Re-serialized JSON may differ from the original bytes and cause signature verification to fail. **Next.js App Router example:** ```javascript // app/webhooks/neon-auth/route.js export async function POST(request) { const rawBody = await request.text(); const payload = await verifyWebhook( rawBody, Object.fromEntries(request.headers) ); // process payload return Response.json({ allowed: true }); } ``` **Tip:** In production, cache the JWKS response and refresh it when you encounter an unknown key ID. Rate-limit refresh attempts to avoid excessive requests to the JWKS endpoint. ## Expected responses Webhook responses must not exceed 10KB. ### `send.otp` and `send.magic_link` Return any 2xx status code. The response body is ignored. If all 3 delivery attempts fail or the 15-second global timeout expires, the auth flow fails and the user sees an error. ### `user.before_create` Return a 2xx status code with a JSON body. **Allow signup:** ```json { "allowed": true } ``` **Reject signup:** ```json { "allowed": false, "error_message": "Signups from this domain are not allowed.", "error_code": "DOMAIN_BLOCKED" } ``` | Field | Type | Description | | --------------- | ------------------ | -------------------------------------------------- | | `allowed` | boolean (required) | Whether to permit user creation | | `error_message` | string (optional) | User-facing rejection message (max 500 characters) | | `error_code` | string (optional) | Machine-readable code for client-side handling | If the webhook fails or returns an invalid response, signup is rejected. This fail-closed behavior prevents bypassing your validation logic. **Important:** If your webhook endpoint is unreachable, all signups fail. Monitor your endpoint availability and keep response times well under the configured timeout to leave room for network latency and retries. ### `user.created` Return any 2xx status code. The response body is ignored. This event is non-blocking. Failures are logged but do not affect the user creation. Return 200 immediately and process the event asynchronously (for example, via a job queue). This prevents timeouts under load. ## Retry behavior Because blocking events pause the user's auth flow, retries happen immediately rather than using exponential backoff. The user cannot wait minutes for a retry. The 15-second global timeout runs from the start of the first attempt. Each attempt uses the lesser of `timeout_seconds` or the remaining global time. If earlier attempts consume the budget, later attempts get reduced timeouts or are skipped. | Property | Value | | -------------- | -------------------------------------------------------------------------------------------- | | Max attempts | 3 (1 initial + 2 retries, no backoff) | | Global timeout | 15 seconds across all attempts | | Retryable | 5xx, 429, 408, network errors (ECONNREFUSED, ETIMEDOUT, ECONNRESET, ENOTFOUND, ECONNABORTED) | | Non-retryable | 4xx (except 408 and 429) | ## Testing and debugging Neon Auth does not currently support test events, event logs, or redelivery. To test webhooks during development, expose a local server using a tunneling tool (for example, ngrok) and configure it as your webhook URL. Neon Auth rejects webhook URLs that point to localhost or private IP addresses. --- [Document source](https://neon.com/docs/auth/migrate/from-legacy-auth.md) --- # Migrate to Neon Auth with Better Auth Update from the legacy Stack Auth-based implementation **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). This guide shows you the code differences between legacy Neon Auth (Stack Auth) and Neon Auth with Better Auth. Use it as a reference to understand what changes if you decide to upgrade. **Important: Legacy Neon Auth (Stack Auth) is no longer accepting new users** If you're using legacy Neon Auth with Stack Auth, you can continue using it. We'll keep supporting it for existing users. But we encourage you to try Neon with Better Auth instead. ## Why Neon Auth with Better Auth? - **Native Branching Support** Authentication branches automatically with your database. Each branch gets isolated users, sessions, and auth configuration, perfect for preview environments and testing. - **Database as Source of Truth** Your Neon database is the single source of truth for authentication data. No webhooks, no sync delays, no external dependencies. Query users directly with SQL. - **Simplified Configuration** One environment variable instead of four. Easier setup, fewer moving parts. - **Open-Source Foundation** Built on Better Auth, enabling faster development of new features and better community support. ## Environment variables Update your environment variables to use Better Auth's configuration. ```env filename=".env (before - Stack Auth)" NEXT_PUBLIC_STACK_PROJECT_ID=your-project-id NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=your-client-key STACK_SECRET_SERVER_KEY=your-server-secret ``` ```env filename=".env (after - Better Auth)" NEON_AUTH_BASE_URL=https://ep-xxx.neonauth.us-east-2.aws.neon.build/neondb/auth NEON_AUTH_COOKIE_SECRET=your-secret-at-least-32-characters-long ``` **Note:** For React SPAs, use VITE_NEON_AUTH_URL instead. The NEON_AUTH_COOKIE_SECRET is only needed for Next.js (generate with openssl rand -base64 32). You can find your Auth URL in the Neon Console under **Auth** → **Configuration**. **What changed** You replace multiple Stack Auth-specific keys with a single Better Auth URL that points at your Neon project. For Next.js, you also need a cookie secret for session caching. ## Next.js migration ### Install packages (#nextjs-install-packages) Uninstall Stack Auth packages and install `@neondatabase/auth` ```bash filename="Terminal" npm uninstall @stackframe/stack npm install @neondatabase/auth@latest ``` **What changed** Your app now depends on Neon Auth's Next.js SDK and UI package instead of the Stack Auth SDK. ### Update SDK initialization (#nextjs-sdk-initialization) **Before (Stack Auth)** ```tsx // stack.ts import { StackServerApp } from '@stackframe/stack'; export const stackServerApp = new StackServerApp({ tokenStore: 'nextjs-cookie', }); ``` **After (Better Auth)** ```tsx // ./lib/auth/client.ts 'use client'; import { createAuthClient } from '@neondatabase/auth/next'; // to use in react client components export const authClient = createAuthClient(); // ./lib/auth/server.ts import { createNeonAuth } from '@neondatabase/auth/next/server'; // to use in react server components, server actions, and API routes export const auth = createNeonAuth({ baseUrl: process.env.NEON_AUTH_BASE_URL!, cookies: { secret: process.env.NEON_AUTH_COOKIE_SECRET!, }, }); ``` **What changed** You initialize the Neon Auth client with `createAuthClient` for client components and with `createNeonAuth()` for server-side auth. The unified `auth` instance provides `.handler()`, `.middleware()`, `.getSession()`, and all Better Auth server methods. ### Replace components (#nextjs-replace-components) #### Sign in page **Before (Stack Auth)** ```tsx import { SignIn } from '@stackframe/stack'; export default function SignInPage() { return ; } ``` **After (Better Auth)** ```tsx import { AuthView } from '@neondatabase/auth/react'; export default function SignInPage() { return ; } ``` **What changed** You render Neon Auth's `AuthView` client component and tell it which flow to show using the `pathname` prop. #### Sign up page **Before (Stack Auth)** ```tsx import { SignUp } from '@stackframe/stack'; export default function SignUpPage() { return ; } ``` **After (Better Auth)** ```tsx import { AuthView } from '@neondatabase/auth/react'; export default function SignUpPage() { return ; } ``` **What changed** You swap the dedicated `` component for the same `AuthView` component, configured with the `"sign-up"` pathname. #### User button **Before (Stack Auth)** ```tsx import { UserButton } from '@stackframe/stack'; export function Header() { return ; } ``` **After (Better Auth)** ```tsx import { UserButton } from '@neondatabase/auth/react'; export function Header() { return ; } ``` **What changed** You keep the same `UserButton` API but import it from the Neon Auth UI package and mark the component as client-side. ### Replace hooks (#nextjs-replace-hooks) **Before (Stack Auth)** ```tsx 'use client'; import { useUser } from '@stackframe/stack'; export function MyComponent() { const user = useUser(); return
{user ? `Hello, ${user.displayName}` : 'Not logged in'}
; } ``` **After (Better Auth)** ```tsx 'use client'; import { useSession } from '@/lib/auth/client'; export function MyComponent() { const { data } = useSession(); const user = data?.user; return
{user ? `Hello, ${user.name || user.email}` : 'Not logged in'}
; } ``` **What changed** Instead of `useUser()`, you call `useSession()` hook from `authClient` and read the user & session data from response. ### Update provider setup (#nextjs-update-provider) **Before (Stack Auth)** ```tsx import { StackProvider, StackTheme } from '@stackframe/stack'; import { stackServerApp } from './stack'; export default function RootLayout({ children }) { return ( {children} ); } ``` **After (Better Auth)** ```tsx 'use client'; import { NeonAuthUIProvider } from '@neondatabase/auth/react'; import '@neondatabase/auth/ui/css'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { authClient } from '@/lib/auth/client'; export default function RootLayout({ children }) { const router = useRouter(); return ( {children} ); } ``` **What changed** You wrap your app in `NeonAuthUIProvider`, pass it the `authClient`, and import the Neon Auth UI styles. **Tip: Styling options** To learn more about applying styles to the Auth UI components, including plain CSS and Tailwind CSS v4 options, see [UI Component Styles](https://neon.com/docs/auth/reference/ui-components#styling). ### Replace auth handler route **Before (Stack Auth)** ```tsx // app/handler/[...stack]/page.tsx import { StackHandler } from '@stackframe/stack'; import { stackServerApp } from '@/stack'; export default function Handler(props: any) { return ; } ``` **After (Better Auth)** ```tsx // app/api/auth/[...path]/route.ts import { auth } from '@/lib/auth/server'; export const { GET, POST } = auth.handler(); ``` **What changed** You proxy Neon Auth APIs from your Next.js application. The `auth.handler()` method forwards all API requests to the Neon Auth server. ### Protect routes #### Component-level protection **Before (Stack Auth)** ```tsx 'use client'; import { useUser } from '@stackframe/stack'; export default function ProtectedPage() { const user = useUser({ or: 'redirect' }); return
Protected content
; } ``` **After (Better Auth)** ```tsx 'use client'; import { SignedIn, RedirectToSignIn } from '@neondatabase/auth/react'; export default function ProtectedPage() { return (
Protected content
); } ``` **What changed** You switch from hook-based redirects to declarative UI helpers that show content only when the user is signed in. #### Middleware-based protection ```tsx filename="proxy.ts (new)" import { auth } from '@/lib/auth/server'; export default auth.middleware({ // Redirects unauthenticated users to sign-in page loginUrl: '/auth/sign-in', }); export const config = { matcher: [ // Protected routes requiring authentication '/dashboard/:path*', '/settings/:path*', // Do not run the middleware for the static resources '/((?!_next/static|_next/image|favicon.ico).*)', ], }; ``` **What changed** You can optionally add middleware to enforce auth at the edge for specific paths. ### Server-side user access **Before (Stack Auth)** ```tsx import { stackServerApp } from '@/stack'; export default async function ServerComponent() { const user = await stackServerApp.getUser(); return
{user?.displayName}
; } ``` **After (Better Auth)** ```tsx import { auth } from '@/lib/auth/server'; // Server components using auth methods must be rendered dynamically export const dynamic = 'force-dynamic'; export default async function ServerComponent() { const { data: session } = await auth.getSession(); return
{session?.user?.name || session?.user?.email}
; } ``` **What changed** Server components now call `auth.getSession()` and read the user from the returned session. Components using auth methods must set `dynamic = 'force-dynamic'`. ## React SPA migration ### Install packages (#react-install-packages) Uninstall Stack Auth packages and install `@neondatabase/auth` ```bash filename="Terminal" npm uninstall @stackframe/stack npm install @neondatabase/auth@latest ``` **What changed** You use the framework-agnostic Neon JS SDK plus the shared UI package instead of the Stack Auth client SDK. ### Update SDK initialization (#react-sdk-initialization) **Before (Stack Auth)** ```tsx // src/stack.ts import { StackClientApp } from '@stackframe/stack'; export const stackClientApp = new StackClientApp({ urls: { signIn: '/sign-in', signUp: '/sign-up', }, }); ``` **After (Better Auth)** ```tsx // src/auth.ts import { createAuthClient } from '@neondatabase/auth'; export const authClient = createAuthClient(import.meta.env.VITE_NEON_AUTH_URL); const { useSession } = authClient; ``` **What changed** You replace the Stack Auth client app with a Neon Auth `authClient` wired to your Neon Auth URL. ### Replace components (#react-replace-components) Components are the same as Next.js. Use ``, ``, ``, and `` from `@neondatabase/neon-auth-ui`. **What changed** The UI building blocks are shared across frameworks, so you can reuse the same auth components in SPAs. ### Replace hooks (#react-replace-hooks) **Before (Stack Auth)** ```tsx import { useUser } from '@stackframe/stack'; export function MyComponent() { const user = useUser(); return
{user ? `Hello, ${user.displayName}` : 'Not logged in'}
; } ``` **After (Better Auth)** ```tsx import { useSession } from './auth'; export function MyComponent() { const { data: session } = useSession(); const user = data?.user; return
{user ? `Hello, ${user.name || user.email}` : 'Not logged in'}
; } ``` **What changed** Instead of a React hook from Stack Auth, you call `authClient.getSession()` and manage the session in your own component state. ### Update provider setup (#react-update-provider) **Before (Stack Auth)** ```tsx import { StackProvider, StackTheme } from '@stackframe/stack'; import { stackClientApp } from './stack'; function App() { return ( {/* Your app */} ); } ``` **After (Better Auth)** ```tsx import { NeonAuthUIProvider } from '@neondatabase/auth/react'; import '@neondatabase/auth/ui/css'; import { authClient } from './auth'; function App() { return {/* Your app */}; } ``` **What changed** You drop the Stack Auth provider/theme and wrap your app in `NeonAuthUIProvider` with the Neon Auth UI styles. **Tip: Styling options** To learn more about applying styles to the Auth UI components, including plain CSS and Tailwind CSS v4 options, see [UI Component Styles](https://neon.com/docs/auth/reference/ui-components#styling). ### Remove auth handler route Delete any `StackHandler` routes. Create custom pages for sign-in and sign-up using ``. ```tsx filename="src/pages/SignIn.tsx" import { AuthView } from '@neondatabase/auth/react'; export default function SignIn() { return ; } ``` **What changed** Routing is fully controlled by your SPA, and the `AuthView` component just renders the appropriate view for each path. ### React Router integration If you're using React Router, pass navigation helpers to the provider. ```tsx filename="src/App.tsx (React Router)" import { NeonAuthUIProvider } from '@neondatabase/auth/react'; import { useNavigate, Link } from 'react-router-dom'; import { authClient } from './auth'; function App() { const navigate = useNavigate(); return ( {/* Your app */} ); } ``` **What changed** You let Better Auth reuse your router's navigation and Link components so redirects and links stay in sync with your SPA. ## Eject to Stack Auth If you prefer to continue using Stack Auth independently instead of migrating to Better Auth, you can claim your Stack Auth project and manage it directly. ## Claim your project via the Neon Console 1. Go to your project's **Auth** page, **Configuration** tab in the Neon Console. 2. Click **Claim project** in the Claim project section. 3. Follow the prompts to select the Stack Auth account that should receive ownership. After claiming, you'll have direct access to manage your project in the Stack Auth dashboard. ## Update your environment variables Once claimed, update your environment variables to use Stack Auth's direct configuration. Your existing code will continue to work without changes since you're already using the Stack Auth SDK (`@stackframe/stack`). ## Manage independently After claiming, you can: - Manage OAuth providers directly in Stack Auth. - Configure production security settings. - Access Stack Auth's dashboard and features. **Important:** Ejecting to Stack Auth means you'll manage authentication independently from Neon. You'll need to handle updates, support, and infrastructure yourself. Your authentication data will no longer be managed through the Neon Console. --- [Document source](https://neon.com/docs/auth/migrate/from-supabase.md) --- # Migrate from Supabase to Neon Switch from Supabase Auth and Database to Neon in a few steps **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth provides a Supabase-compatible API, and Neon Data API provides PostgreSQL database access. This guide shows how to migrate from Supabase to Neon. **About user migration:** Existing password-based users cannot migrate due to different hashing algorithms. They'll need to create new accounts or re-authenticate via OAuth. This guide works best for new projects, early development, or rebuilding your app. ## Prerequisites - A Neon project ([create one here](https://console.neon.tech)) - Data API enabled (Neon Auth is enabled by default when you enable Data API): - Go to **Data API** in the Neon Console and enable it - In **Data API → Configuration**, verify it's configured with **Neon Auth** - Copy your Data API base URL and Auth URL from the Console - you'll need both ## Install Neon SDK Replace the Supabase SDK with Neon's: ```bash filename="Terminal" npm uninstall @supabase/supabase-js npm install @neondatabase/neon-js@latest ``` ## Update environment variables Replace your Supabase credentials with your Neon Auth and Data API URLs: ```env filename=".env" # Remove these: # VITE_SUPABASE_URL=https://your-project.supabase.co # VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... # Add these: VITE_NEON_AUTH_URL=https://ep-xxx.neonauth.us-east-2.aws.neon.build/neondb/auth VITE_NEON_DATA_API_URL=https://ep-xxx.us-east-2.aws.neon.build/neondb/rest/v1 ``` **Get your URLs:** 1. **Auth URL**: Go to **Auth → Configuration** in the Neon Console 2. **Data API URL**: Go to **Data API** in the Neon Console **Note:** The `VITE_` prefix is for Vite. Use `NEXT_PUBLIC_` for Next.js, or no prefix for Node.js. ## Update client initialization Find your Supabase client file, typically `src/supabase.ts` or `src/lib/supabase.ts`, and update it: **Before (Supabase):** ```typescript filename="src/supabase.ts" import { createClient } from '@supabase/supabase-js'; export const supabase = createClient( process.env.VITE_SUPABASE_URL!, process.env.VITE_SUPABASE_ANON_KEY! ); ``` **After (Neon):** ```typescript filename="src/auth.ts" import { createClient, SupabaseAuthAdapter } from '@neondatabase/neon-js'; export const client = createClient({ auth: { url: import.meta.env.VITE_NEON_AUTH_URL, adapter: SupabaseAuthAdapter(), }, dataApi: { url: import.meta.env.VITE_NEON_DATA_API_URL, }, }); ``` This example renames the file to `auth.ts` and the variable to `client` for a provider-agnostic setup. You can use any naming, just stay consistent throughout your codebase. Find **all files that import this client** and update them: ```typescript // Before import { supabase } from './supabase'; // After import { client } from './auth'; ``` ## Your auth code stays the same After updating imports, use find/replace to change all instances throughout your codebase: **`supabase.` → `client.`** Your authentication methods, parameters, and responses work identically. Your components, hooks, and auth flows don't change. ```typescript filename="Auth methods" // Sign up await client.auth.signUp({ email, password }); // Sign in with password await client.auth.signInWithPassword({ email, password }); // OAuth sign in await client.auth.signInWithOAuth({ provider: 'google' }); // Get current user const { data: { user }, } = await client.auth.getUser(); // Get session const { data: { session }, } = await client.auth.getSession(); // Sign out await client.auth.signOut(); ``` ## Your database queries stay the same Your existing `client.from()` queries work without any code changes: ```typescript // Same as Supabase - no changes needed const { data: posts } = await client.from('posts').select('*'); const { data: user } = await client.from('users').select('*').eq('id', userId).single(); ``` **Note:** For production apps, use Row Level Security (RLS) to secure your data. See our [RLS with Drizzle guide](https://neon.com/docs/guides/rls-drizzle) for the recommended setup. ## Test the migration Run your app: ```bash filename="Terminal" npm run dev ``` **Test your app:** 1. Sign up a new user 2. Sign in with that user 3. Verify the session persists across page reloads 4. If you enabled Data API: Test user actions that involve database queries (creating posts, loading lists, etc.) Your authentication and database queries should work the same as they did with Supabase. **Verify users in Neon Console:** Go to **Auth → Users** in the Neon Console to see your newly created users, or query directly: ```sql filename="SQL Editor" SELECT id, email, "createdAt" FROM neon_auth.user ORDER BY "createdAt" DESC; ``` ## What changed? | Feature | Supabase | Neon | | ------------------------- | ----------------------------------- | ------------------------------------ | | **User ID type** | `UUID` | `UUID` | | **Client config** | URL + anon key | Auth URL + Data API URL | | **Environment variables** | `SUPABASE_URL`, `SUPABASE_ANON_KEY` | `NEON_AUTH_URL`, `NEON_DATA_API_URL` | | **SDK package** | `@supabase/supabase-js` | `@neondatabase/neon-js` | ## API compatibility Neon Auth supports most Supabase Auth methods including sign up, sign in (password and OAuth), session management, user updates, and email verification. See the [Neon TypeScript SDK](https://neon.com/docs/reference/javascript-sdk) for the complete API. **Not supported:** | Method | Details | | ------------------- | ----------------------------------- | | `signInWithPhone()` | Phone authentication (SMS/WhatsApp) | | SAML SSO methods | Enterprise SAML authentication | | Web3 authentication | Blockchain wallet sign-in | **Different behavior:** | Method | Notes | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `updateUser()` | Does not support `password` or `email` parameters | | Email verification | Unlike Supabase's automatic verification flow, Neon Auth requires you to build the verification UI in your app. See [Email Verification](https://neon.com/docs/auth/guides/email-verification) for implementation details. | If you're using any unsupported methods or verification flows, you'll need to adjust your implementation. --- [Document source](https://neon.com/docs/auth/overview.md) --- # Neon Auth Managed authentication that branches with your database **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth is a managed authentication service that stores users, sessions, and auth configuration directly in your Neon database. When you branch your database, your entire auth state branches with it. This lets you test real authentication workflows in preview environments. ## Quick start guides Choose your framework to get started: - [Next.js](https://neon.com/docs/auth/quick-start/nextjs-api-only): Quick start with API methods - [React](https://neon.com/docs/auth/quick-start/react): Quick start with API methods - [TanStack Router](https://neon.com/docs/auth/quick-start/tanstack-router): With UI components **Tip: Using an AI coding tool?** Run [`neonctl init`](https://neon.com/docs/reference/cli-init) to configure your editor with the Neon MCP server and agent skills, including Neon Auth setup guidance: ```bash npx neonctl@latest init ``` ## Why Neon Auth? - **Identity lives in your database** All authentication data is stored in the `neon_auth` schema. It's queryable with SQL and compatible with Row Level Security (RLS) policies. - **Zero server management** Neon Auth runs as a managed REST API service. Configure settings in the Console; use the [client SDK](https://neon.com/docs/reference/javascript-sdk) or [server SDK](https://neon.com/docs/auth/reference/nextjs-server) in your app. No infrastructure to maintain. - **Auth that branches with your data** Test sign-up, login, password reset, and OAuth flows in isolated branches without touching production data. ## Built on Better Auth Neon Auth is powered by [Better Auth](https://www.better-auth.com/), which means you get familiar APIs. You can use Better Auth UI components or call auth methods directly to build your own UI. Neon Auth currently supports Better Auth version **1.4.18**. ### When to use Neon Auth vs. self-hosting Better Auth Neon Auth is a managed authentication service that integrates seamlessly with Neon's architecture and offerings: - **Branch-aware authentication**: Every Neon branch gets its own isolated auth environment, so you can test authentication features without affecting your production branch. - **Built-in Data API integration**: JWT token validation for the Data API has native support for Neon Auth. - **No infrastructure to manage**: Neon Auth is deployed in the same region as your database, reducing latency without requiring you to run auth infrastructure. - **Shared OAuth credentials for testing**: Get started quickly with out-of-the-box Google OAuth credentials, eliminating the setup complexity for testing and prototyping. Self-hosting Better Auth makes sense if you need: - Flexibility in auth configuration: custom plugins, hooks, and options not yet supported by Neon Auth. - Full control over your auth code and the ability to run it inside your own infrastructure. For more details on the SDK differences between `@neondatabase/auth` and `better-auth/client`, see [Why use @neondatabase/auth over better-auth/client](https://github.com/neondatabase/neon-js/blob/main/packages/auth/neon-auth_vs_better-auth.md). As Neon Auth evolves, more Better Auth integrations and features will be added. Check the [roadmap](https://neon.com/docs/auth/roadmap) to see what's currently supported and what's coming next. ## Basic usage Enable Auth in your Neon project, then add authentication to your app. **For Next.js (server-side):** See the [Next.js Server SDK reference](https://neon.com/docs/auth/reference/nextjs-server) for complete API documentation. ```typescript filename="lib/auth/server.ts" import { createNeonAuth } from '@neondatabase/auth/next/server'; export const auth = createNeonAuth({ baseUrl: process.env.NEON_AUTH_BASE_URL!, cookies: { secret: process.env.NEON_AUTH_COOKIE_SECRET! }, }); ``` ```typescript filename="app/api/auth/[...path]/route.ts" import { auth } from '@/lib/auth/server'; export const { GET, POST } = auth.handler(); ``` **For React/Vite (client-side):** See the [Client SDK reference](https://neon.com/docs/reference/javascript-sdk) for complete API documentation. ```typescript filename="src/auth.ts" import { createAuthClient } from '@neondatabase/neon-js/auth'; export const authClient = createAuthClient(import.meta.env.VITE_NEON_AUTH_URL); ``` ```tsx filename="src/App.tsx" import { NeonAuthUIProvider, AuthView } from '@neondatabase/neon-js/auth/react/ui'; import { authClient } from './auth'; export default function App() { return ( ); } ``` ## Use cases - **Production authentication** Use Neon Auth as the identity system for your app. Store users, sessions, and OAuth configuration directly in Postgres, and pair with RLS for secure, database-centric access control. - **Preview environments** Test full authentication flows in Vercel previews with real users and sessions - **Multi-tenant SaaS** Test complex org and role hierarchies safely in isolated branches - **CI/CD workflows** Run end-to-end auth tests without touching production. The [Neon Create Branch GitHub Action](https://github.com/marketplace/actions/neon-create-branch-github-action) supports retrieving branch-specific auth URLs for testing authentication flows in GitHub Actions workflows. - **Development workflows** Spin up complete environments instantly with database and auth together See [Branching authentication](https://neon.com/docs/auth/branching-authentication) for details on how auth branches with your database. ## Example applications Beyond the quick starts on this site, the [neondatabase/neon-js](https://github.com/neondatabase/neon-js) monorepo ships **more runnable Neon Auth and `neon-js` samples** under [`examples/`](https://github.com/neondatabase/neon-js/tree/main/examples)—Next.js and React apps, cross-subdomain setups, the Organization plugin, alternative UI stacks, and Data API patterns. Each folder includes its own README (many workflows use **bun** from the repository root). Browse there when you want a full project to clone next to the guides here. ## Availability Neon Auth is currently available for AWS regions only. Azure support is not yet available. Neon Auth does not currently support projects with [IP Allow](https://neon.com/docs/manage/projects#configure-ip-allow) or [Private Networking](https://neon.com/docs/guides/neon-private-networking) enabled. ## Pricing Neon Auth is included in all Neon plans based on Monthly Active Users (MAU): - **Free**: Up to 60,000 MAU - **Launch**: Up to 1M MAU - **Scale**: Up to 1M MAU An MAU (Monthly Active User) is a unique user who authenticates at least once during a monthly billing period. If you need more than 1M MAU, request an increase in the [console feedback form](https://console.neon.tech/app/settings?modal=feedback\&modalparams=%22Neon%20auth%20limit%20increase%22). See [Neon plans](https://neon.com/docs/introduction/plans#auth) for more details. ## Migration from Stack Auth If you're using the previous Neon Auth implementation via Stack Auth, your version will continue to work. When you're ready to migrate to the new Better Auth implementation, see our [migration guide](https://neon.com/docs/auth/migrate/from-legacy-auth). --- [Document source](https://neon.com/docs/auth/production-checklist.md) --- # Auth production checklist Required configuration before launching with Neon Auth **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Complete these steps before taking your application to production with Neon Auth. ## Auth production checklist - [ ] [1. Configure trusted domains](https://neon.com/docs/auth/guides/configure-domains) Add your production domain(s) to enable OAuth and email verification redirects. See [Configure trusted domains](https://neon.com/docs/auth/guides/configure-domains). - [ ] [2. Set up custom email provider](https://neon.com/docs/auth/production-checklist#email-provider) Replace shared SMTP (`auth@mail.myneon.app`) with your own email service for reliable delivery and higher limits. A custom email provider is also required if you want to use verification links instead of verification codes. See [Email provider configuration](https://neon.com/docs/auth/production-checklist#email-provider) below. - [ ] [3. Configure OAuth credentials (if using OAuth)](https://neon.com/docs/auth/guides/setup-oauth#production-setup) Set up your own Google and GitHub OAuth apps to replace shared development keys. See [OAuth production setup](https://neon.com/docs/auth/guides/setup-oauth#production-setup). - [ ] [4. Enable email verification (recommended)](https://neon.com/docs/auth/guides/email-verification) **Email verification is not enabled by default.** Since anyone can sign up for your application, enabling email verification adds an important verification step to ensure users own their email address. See [Email verification guide](https://neon.com/docs/auth/guides/email-verification). - [ ] [5. Disable localhost access](https://neon.com/docs/auth/production-checklist#localhost-access) Disable the "Allow Localhost" setting in your project's **Settings** → **Auth** page. This setting is enabled by default for development but should be disabled in production to improve security. See [Localhost access](https://neon.com/docs/auth/production-checklist#localhost-access) below. ## Email provider (#email-provider) Neon Auth uses a shared SMTP provider (`auth@mail.myneon.app`) by default for development and testing. For production, configure your own email provider for better deliverability and higher sending limits. ### Configure custom SMTP In your project's **Settings** → **Auth** page, configure your email provider: 1. Select **Custom SMTP provider** 2. Enter your SMTP credentials: - **Host**: Your SMTP server hostname (for example, `smtp.gmail.com`) - **Port**: SMTP port (typically `465` for SSL or `587` for TLS) - **Username**: Your SMTP username - **Password**: Your SMTP password or app-specific password - **Sender email**: Email address to send from - **Sender name**: Display name for sent emails 3. Click **Save** ### Email provider requirements - **Verification links**: Require a custom email provider - **Verification codes**: Work with shared or custom email providers - **Password reset**: Works with shared or custom email providers **Note:** The shared email provider (`auth@mail.myneon.app`) is suitable for development and testing. For production applications, use a custom email provider for better deliverability and to avoid rate limits. ## Localhost access (#localhost-access) The "Allow Localhost" setting in your project's **Settings** → **Auth** page is enabled by default to allow authentication requests from localhost during development. ### Disable for production For production environments, disable this setting to improve security: 1. Go to **Settings** → **Auth** in your Neon project 2. Find the **Allow Localhost** toggle 3. Disable the toggle **Important:** Only enable "Allow Localhost" for local development. Disabling this setting in production prevents unauthorized authentication requests from localhost, improving your application's security posture. --- [Document source](https://neon.com/docs/auth/quick-start/nextjs-api-only.md) --- # Use Neon Auth with Next.js (API methods) Build your own auth UI using SDK methods **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). **Tip: Using an AI coding tool?** Run [`neonctl init`](https://neon.com/docs/reference/cli-init) to configure your editor with the Neon MCP server and agent skills, including Neon Auth setup guidance: ```bash npx neonctl@latest init ``` This guide shows you how to integrate Neon Auth into a [Next.js](https://nextjs.org) (App Router) project using SDK methods directly. For pre-built UI components, see the [UI components reference](https://neon.com/docs/auth/reference/ui-components) and the [neon-js examples](https://github.com/neondatabase/neon-js/tree/main/examples). Upgrading from v0.1? See the [migration guide](https://neon.com/docs/auth/migrate/from-auth-v0.1). ## Enable Auth in your Neon project Enable Auth in your [Neon project](https://console.neon.tech) and copy your Auth URL from Configuration. **Console path:** Project → Branch → Auth → Configuration ## Install the Neon SDK Install the Neon SDK into your Next.js app.
_If you don't have a Next.js project_ ```bash npx create-next-app@latest my-app --yes cd my-app ```
```bash filename="Terminal" npm install @neondatabase/auth@latest ``` ## Set up environment variables Create a `.env.local` file in your project root and add your Auth URL and a cookie secret: **Note:** Replace the Auth URL with your actual Auth URL from the Neon Console. Generate a secure cookie secret with `openssl rand -base64 32`. ```bash filename=".env.local" NEON_AUTH_BASE_URL=https://ep-xxx.neonauth.us-east-1.aws.neon.tech/neondb/auth NEON_AUTH_COOKIE_SECRET=your-secret-at-least-32-characters-long ``` ## Create auth server instance Create a unified auth instance in `lib/auth/server.ts`. This single instance provides all server-side auth functionality: - `.handler()` for API routes - `.middleware()` for route protection - `.getSession()` and all Better Auth server methods See the [Next.js Server SDK reference](https://neon.com/docs/auth/reference/nextjs-server) for complete API documentation. ```typescript filename="lib/auth/server.ts" import { createNeonAuth } from '@neondatabase/auth/next/server'; export const auth = createNeonAuth({ baseUrl: process.env.NEON_AUTH_BASE_URL!, cookies: { secret: process.env.NEON_AUTH_COOKIE_SECRET!, }, }); ``` ## Set up auth API routes Create an API route handler that proxies auth requests. All Neon Auth APIs will be routed through this handler. Create a route file inside `/api/auth/[...path]` directory: In `app/api/auth/[...path]/route.ts`: ```typescript filename="app/api/auth/[...path]/route.ts" import { auth } from '@/lib/auth/server'; export const { GET, POST } = auth.handler(); ``` ## Add authentication middleware The middleware ensures users are authenticated before accessing protected routes. Create `proxy.ts` file in your project root: ```typescript filename="proxy.ts" import { auth } from '@/lib/auth/server'; export default auth.middleware({ // Redirects unauthenticated users to sign-in page loginUrl: '/auth/sign-in', }); export const config = { matcher: [ // Protected routes requiring authentication '/account/:path*', ], }; ``` **Note:** Your Next.js project is now fully configured to use Neon Auth. Now, lets proceed with setting up the auth clients. ## Configure the auth client Create the auth client in `lib/auth/client.ts` for client-side auth operations (form submissions, hooks, etc.). **Note:** The server-side `auth` instance was already created in a previous step. The client is separate and handles browser-side auth operations. ```tsx filename="lib/auth/client.ts" 'use client'; import { createAuthClient } from '@neondatabase/auth/next'; export const authClient = createAuthClient(); ``` ## Create Sign up form Lets create a sign-up form and action in `app/auth/sign-up/page.tsx` and `app/auth/sign-up/actions.ts` files respectively using the auth instance we created in previous step - To create user with email and password, we will use `auth.signUp.email()` with user name, email address, and password - You can optionally add business logic before invoking the API, for example restrict signups to emails ending with `@my-company.com` **Signup action** Copy and paste following code in `app/auth/sign-up/actions.ts` file: ```ts 'use server'; import { auth } from '@/lib/auth/server'; import { redirect } from 'next/navigation'; export async function signUpWithEmail( _prevState: { error: string } | null, formData: FormData ) { const email = formData.get('email') as string; if (!email) { return { error: "Email address must be provided." } } // Optionally restrict sign ups based on email address // if (!email.trim().endsWith("@my-company.com")) { // return { error: 'Email must be from my-company.com' }; // } const { error } = await auth.signUp.email({ email, name: formData.get('name') as string, password: formData.get('password') as string, }); if (error) { return { error: error.message || 'Failed to create account' }; } redirect('/'); } ``` **Signup form** Copy and paste following code in `app/auth/sign-up/page.tsx` file: ```tsx 'use client'; import { useActionState } from 'react'; import { signUpWithEmail } from './actions'; export default function SignUpForm() { const [state, formAction, isPending] = useActionState(signUpWithEmail, null); return (

Create new account

{state?.error && (
{state.error}
)}
); } ``` ## Create Sign in form Lets create a sign-in form and action in `app/auth/sign-in/page.tsx` and `app/auth/sign-in/actions.ts` files respectively. - To sign-in the user we will use `auth.signIn.email()` with user's email address and password. **Sign In:** **Sign-in action** ```ts 'use server'; import { auth } from '@/lib/auth/server'; import { redirect } from 'next/navigation'; export async function signInWithEmail( _prevState: { error: string } | null, formData: FormData ) { const { error } = await auth.signIn.email({ email: formData.get('email') as string, password: formData.get('password') as string, }); if (error) { return { error: error.message || 'Failed to sign in. Try again' }; } redirect('/'); } ``` **Sign-in form** ```tsx 'use client'; import { useActionState } from 'react'; import { signInWithEmail } from './actions'; export default function SignInForm() { const [state, formAction, isPending] = useActionState(signInWithEmail, null); return (

Sign in to your account

{state?.error && (
{state.error}
)}
); } ``` ## Create home page In last step, lets create the home page and display authenticated user status: ```typescript filename="app/page.tsx" import { auth } from '@/lib/auth/server'; import Link from 'next/link'; // Server components using auth methods must be rendered dynamically export const dynamic = 'force-dynamic'; export default async function Home() { const { data: session } = await auth.getSession(); if (session?.user) { return (

Logged in as {session.user.name}

); } return (

Not logged in

Sign-up Sign-in
); } ``` ## Start your app Start the development server: Open your browser to [http://localhost:3000](http://localhost:3000) and test sign-up and sign-in. **Note: Safari users** Safari blocks third-party cookies on non-HTTPS connections. Use `npm run dev -- --experimental-https` and open `https://localhost:3000` instead. ```bash filename="Terminal" npm run dev ``` ## Available SDK methods Both `authClient` and `auth` expose similar API methods. Use `authClient` for client components and `auth` for server components, server actions, and API routes. - [authClient.signUp.email()](https://neon.com/docs/reference/javascript-sdk#auth-signup) / `auth.signUp.email()` - Create a new user account - [authClient.signIn.email()](https://neon.com/docs/reference/javascript-sdk#auth-signinwithpassword) / `auth.signIn.email()` - Sign in with email and password - [authClient.signOut()](https://neon.com/docs/reference/javascript-sdk#auth-signout) / `auth.signOut()` - Sign out the current user - [authClient.getSession()](https://neon.com/docs/reference/javascript-sdk#auth-getsession) / `auth.getSession()` - Get the current session - `authClient.updateUser()` / `auth.updateUser()` - Update user details The `auth` instance also includes `.handler()` for API routes and `.middleware()` for route protection. ## Next steps - [Add email verification](https://neon.com/docs/auth/guides/email-verification) - [Branching authentication](https://neon.com/docs/auth/branching-authentication) - [More example apps](https://neon.com/docs/auth/overview#example-applications) in the **neon-js** `examples/` directory --- [Document source](https://neon.com/docs/auth/quick-start/react.md) --- # Use Neon Auth with React (API methods) Build your own auth UI **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). **Tip: Using an AI coding tool?** Run [`neonctl init`](https://neon.com/docs/reference/cli-init) to configure your editor with the Neon MCP server and agent skills, including Neon Auth setup guidance: ```bash npx neonctl@latest init ``` ## Create a Neon project with Auth enabled If you don't have a Neon project yet, create one at [console.neon.tech](https://console.neon.tech). Go to the **Auth** page in your project dashboard and click **Enable Auth**. You can then find your Auth **Base URL** on the Configuration tab. Copy this URL - you'll need it in the next step. ## Create a React app Create a React app using Vite. ```bash filename="Terminal" npm create vite@latest my-app -- --template react ``` ## Install the Neon SDK The Neon SDK provides authentication methods like `signUp()`, `getSession()`, and `signOut()` for your React app. ```bash filename="Terminal" cd my-app npm install @neondatabase/neon-js@latest ``` ## Set up environment variables Create a `.env` file in your project root and add your Auth Base URL: **Note:** Replace the URL with your actual Auth Base URL from the Neon Console. ```bash filename=".env" VITE_NEON_AUTH_URL=https://ep-xxx.neonauth.us-east-2.aws.neon.build/neondb/auth ``` ## Configure the Neon client Create a `src/auth.js` file to configure your auth client: ```javascript filename="src/auth.js" import { createAuthClient } from '@neondatabase/neon-js/auth'; export const authClient = createAuthClient(import.meta.env.VITE_NEON_AUTH_URL); ``` ## Build your authentication UI Neon JS uses a programmatic approach for managing auth state. You'll use React hooks like `useEffect` to check the session and handle auth changes. Replace the contents of `src/App.jsx` with the following code to implement [sign-up](https://neon.com/docs/reference/javascript-sdk#auth-signup), [sign-in](https://neon.com/docs/reference/javascript-sdk#auth-signinwithpassword), and [sign-out](https://neon.com/docs/reference/javascript-sdk#auth-signout): ```jsx filename="src/App.jsx" import { useState, useEffect } from 'react'; import { authClient } from './auth'; import './App.css'; export default function App() { const [session, setSession] = useState(null); const [user, setUser] = useState(null); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [isSignUp, setIsSignUp] = useState(true); const [loading, setLoading] = useState(true); useEffect(() => { authClient.getSession().then((result) => { if (result.data?.session && result.data?.user) { setSession(result.data.session); setUser(result.data.user); } setLoading(false); }); }, []); const handleSubmit = async (e) => { e.preventDefault(); const result = isSignUp ? await authClient.signUp.email({ name: email.split('@')[0] || 'User', email, password }) : await authClient.signIn.email({ email, password }); if (result.error) { alert(result.error.message); return; } const sessionResult = await authClient.getSession(); if (sessionResult.data?.session && sessionResult.data?.user) { setSession(sessionResult.data.session); setUser(sessionResult.data.user); } }; const handleSignOut = async () => { await authClient.signOut(); setSession(null); setUser(null); }; if (loading) return
Loading...
; if (session && user) { return (

Logged in as {user.email}

); } return (

{isSignUp ? 'Sign Up' : 'Sign In'}

setEmail(e.target.value)} required /> setPassword(e.target.value)} required />

{isSignUp ? ( <> Already have an account?{' '} { e.preventDefault(); setIsSignUp(false); }} > Sign in ) : ( <> Don't have an account?{' '} { e.preventDefault(); setIsSignUp(true); }} > Sign up )}

); } ``` ## Start your app Start the development server: Open your browser to `http://localhost:5173` and create a test user. ```bash filename="Terminal" npm run dev ``` ## See your users in the database As users sign up, their profiles are synced to your Neon database in the `neon_auth.user` table. Query your users table in the SQL Editor to see your new users: ```sql filename="SQL Editor" SELECT * FROM neon_auth.user; ``` ## Next steps - [Learn about Neon Auth concepts](https://neon.com/docs/auth/overview) - [More example apps](https://neon.com/docs/auth/overview#example-applications) in the **neon-js** `examples/` directory - [Explore the Neon Data API](https://neon.com/docs/data-api/get-started) to build a REST API for your data - [View complete SDK reference](https://neon.com/docs/reference/javascript-sdk) --- [Document source](https://neon.com/docs/auth/quick-start/tanstack-router.md) --- # Use Neon Auth with TanStack Router Set up authentication using pre-built UI components **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). **Tip: Using an AI coding tool?** Run [`neonctl init`](https://neon.com/docs/reference/cli-init) to configure your editor with the Neon MCP server and agent skills, including Neon Auth setup guidance: ```bash npx neonctl@latest init ``` ## Create a Neon project with Auth enabled If you don't have a Neon project yet, create one at [console.neon.tech](https://console.neon.tech). Go to the **Auth** page in your project dashboard and click **Enable Auth**. You can then find your Auth URL on the Configuration tab. Copy this URL - you'll need it in the next step. ## Create a TanStack Router app Create a new TanStack Router app using the file-router template. ```bash filename="Terminal" npx create-tsrouter-app@latest my-app --template file-router --tailwind ``` ## Install the Neon Auth SDK Install the Neon Auth SDK and UI library: ```bash filename="Terminal" cd my-app && npm install @neondatabase/neon-js@latest ``` ## Set up environment variables Create a `.env` file in your project root and add your Auth URL: **Note:** Replace the URL with your actual Auth URL from the Neon Console. ```bash filename=".env" VITE_NEON_AUTH_URL=https://ep-xxx.neonauth.us-east-1.aws.neon.tech/neondb/auth ``` ## Add Neon Auth styles Open your existing `src/styles.css` file and add this import at the **top**, right after the Tailwind import: **Tip: Not using Tailwind?** See [UI Component Styles](https://neon.com/docs/auth/reference/ui-components#styling) for alternative setup options. In `Add to src/styles.css`: ```css @import '@neondatabase/neon-js/ui/tailwind'; ``` ## Configure the auth client Create a `src/auth.ts` file to initialize the auth client: ```typescript filename="src/auth.ts" import { createAuthClient } from '@neondatabase/neon-js/auth'; import { BetterAuthReactAdapter } from '@neondatabase/neon-js/auth/react'; export const authClient = createAuthClient(import.meta.env.VITE_NEON_AUTH_URL, { adapter: BetterAuthReactAdapter() }); ``` ## Create the Auth Provider Wrap your application with the `NeonAuthUIProvider` in `src/routes/__root.tsx`. This makes the auth state available to the UI components used throughout your app. Pass props to `NeonAuthUIProvider` for any features you want to use. Only the `authClient` prop is required.
Example: Adding optional props ```tsx {children} ```
```tsx filename="src/routes/__root.tsx" {4-5,9,22} import { Outlet, createRootRoute } from '@tanstack/react-router'; import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'; import { TanStackDevtools } from '@tanstack/react-devtools'; import { NeonAuthUIProvider } from '@neondatabase/neon-js/auth/react'; import { authClient } from '../auth'; export const Route = createRootRoute({ component: () => ( , }, ]} /> ), }); ``` ## Create the Auth page Create a route to handle authentication views (sign in, sign up, etc.). Create `src/routes/auth.$pathname.tsx`: ```tsx filename="src/routes/auth.$pathname.tsx" import { createFileRoute } from '@tanstack/react-router'; import { AuthView } from '@neondatabase/neon-js/auth/react/ui'; export const Route = createFileRoute('/auth/$pathname')({ component: Auth, }); function Auth() { const { pathname } = Route.useParams(); return (
); } ``` ## Create the Account page Create a route to handle account management views. Create `src/routes/account.$pathname.tsx`: ```tsx filename="src/routes/account.$pathname.tsx" import { createFileRoute } from '@tanstack/react-router'; import { AccountView } from '@neondatabase/neon-js/auth/react/ui'; export const Route = createFileRoute('/account/$pathname')({ component: Account, }); function Account() { const { pathname } = Route.useParams(); return (
); } ``` ## Protect your routes You can protect your routes using the `SignedIn` and `RedirectToSignIn` components. Access the user's session and profile data using the `useSession` hook. Update `src/routes/index.tsx` to protect the home page: ```tsx filename="src/routes/index.tsx" import { createFileRoute } from '@tanstack/react-router'; import { SignedIn, UserButton, RedirectToSignIn } from '@neondatabase/neon-js/auth/react/ui'; import { authClient } from '@/auth'; export const Route = createFileRoute('/')({ component: Home, }); function Home() { const { data } = authClient.useSession(); return ( <>

Welcome!

You're successfully authenticated.

Session and User Data:

              
                {JSON.stringify({ session: data?.session, user: data?.user }, null, 2)}
              
            
); } ``` ## Start your app Start the development server, then open [http://localhost:3000](http://localhost:3000). You'll be redirected to the sign-in page. ```bash filename="Terminal" npm run dev ``` ## See your users in the database As users sign up, their profiles are stored in your Neon database in the `neon_auth.user` table. Query your users table in the SQL Editor to see your new users: ```sql filename="SQL Editor" SELECT * FROM neon_auth.user; ``` ## Next steps - [Add email verification](https://neon.com/docs/auth/guides/email-verification) - [Learn how to branch your auth](https://neon.com/docs/auth/branching-authentication) - [More example apps](https://neon.com/docs/auth/overview#example-applications) in the **neon-js** `examples/` directory --- [Document source](https://neon.com/docs/auth/reference/nextjs-server.md) --- # Next.js Server SDK Reference Server-side authentication API for Next.js with Neon Auth Reference documentation for the Neon Auth Next.js server SDK (`@neondatabase/auth/next/server`). This package provides server-side authentication for Next.js applications using React Server Components, API routes, middleware, and server actions. For client-side authentication, see the [Client SDK reference](https://neon.com/docs/reference/javascript-sdk). For UI components, see the [UI Components reference](https://neon.com/docs/auth/reference/ui-components). ## Installation Install the Neon Auth package in your Next.js project using npm, yarn, pnpm, or bun. ```bash npm install @neondatabase/auth@latest ``` ## Environment variables Configure these environment variables in your `.env.local` file: - **NEON_AUTH_BASE_URL** (required): Your Neon Auth server URL from the Neon Console - **NEON_AUTH_COOKIE_SECRET** (required): Secret for signing session cookies (must be 32+ characters for HMAC-SHA256 security) Generate a secure secret with: `openssl rand -base64 32` ```bash # Required: Your Neon Auth server URL NEON_AUTH_BASE_URL=https://your-neon-auth-url.neon.tech # Required: Cookie secret for session data signing (32+ characters) NEON_AUTH_COOKIE_SECRET=your-secret-at-least-32-characters-long ``` ## createNeonAuth() Method: `createNeonAuth(config)` Creates a unified auth instance that provides all server-side authentication functionality. Returns an `auth` object with: - `handler()` - Creates API route handlers - `middleware()` - Creates Next.js middleware for route protection - `getSession()` - Retrieves current session - All Better Auth server methods (signIn, signUp, signOut, etc.) ### Parameters | Parameter | Type | Required | Default | | ---------------------- | ------ | -------- | ------- | | baseUrl | string | ✓ | - | | cookies.secret | string | ✓ | - | | cookies.sessionDataTtl | number | | 300 | | cookies.domain | string | | - | ```typescript // lib/auth/server.ts import { createNeonAuth } from '@neondatabase/auth/next/server'; export const auth = createNeonAuth({ baseUrl: process.env.NEON_AUTH_BASE_URL!, cookies: { secret: process.env.NEON_AUTH_COOKIE_SECRET!, }, }); ``` ## auth.handler() Method: `auth.handler()` Creates GET and POST route handlers for the Neon Auth API proxy. Create a catch-all route at `app/api/auth/[...path]/route.ts`. This handles all authentication API calls from your client, including: - Sign in/sign up requests - OAuth callbacks - Session management - Email verification - Password reset ### Returns Object with `GET` and `POST` Next.js route handlers. ```typescript // app/api/auth/[...path]/route.ts import { auth } from '@/lib/auth/server'; export const { GET, POST } = auth.handler(); ``` ## auth.middleware() Method: `auth.middleware(options)` Creates Next.js middleware for session validation and route protection. The middleware automatically: - Validates session cookies on each request - Provides session data to server components - Redirects unauthenticated users to the login page - Refreshes session tokens when needed ### Parameters
View parameters | Parameter | Type | Required | Default | | --------- | ------ | -------- | --------------- | | loginUrl | string | | `/auth/sign-in` |
```typescript // middleware.ts import { auth } from '@/lib/auth/server'; export default auth.middleware({ loginUrl: '/auth/sign-in' }); export const config = { matcher: [ // Match all paths except static files "/((?!_next/static|_next/image|favicon.ico).*)", ], }; ``` ## auth.getSession() Method: `auth.getSession()` Retrieves the current session in Server Components, Server Actions, and API Routes. - Returns cached session if available (fast) - Automatically refreshes expired tokens - Returns null if no active session Server Components that use `auth.getSession()` must export `dynamic = 'force-dynamic'` because session data depends on cookies. ### Returns | Field | Type | Description | | ------- | --------------- | ---------------------------------------------------- | | `data` | Session \| null | Session with user data, or null if not authenticated | | `error` | Error \| null | Error object if session retrieval failed | **Server Component** ```typescript // app/dashboard/page.tsx import { auth } from '@/lib/auth/server'; export const dynamic = 'force-dynamic'; export default async function DashboardPage() { const { data: session } = await auth.getSession(); if (!session?.user) { return
Not authenticated
; } return

Welcome, {session.user.name}

; } ``` **Server Action** ```typescript // app/actions.ts 'use server'; import { auth } from '@/lib/auth/server'; import { redirect } from 'next/navigation'; export async function updateProfile(formData: FormData) { const { data: session } = await auth.getSession(); if (!session?.user) { redirect('/auth/sign-in'); } // Update user profile... } ``` **API Route** ```typescript // app/api/user/route.ts import { auth } from '@/lib/auth/server'; export async function GET() { const { data: session } = await auth.getSession(); if (!session?.user) { return Response.json({ error: 'Unauthorized' }, { status: 401 }); } return Response.json({ user: session.user }); } ``` ## auth.signIn.email() Method: `auth.signIn.email(credentials)` Sign in with email and password. Use in Server Actions for form-based authentication. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | email | string | ✓ | | password | string | ✓ |
```typescript 'use server'; import { auth } from '@/lib/auth/server'; import { redirect } from 'next/navigation'; export async function signIn(formData: FormData) { const { data, error } = await auth.signIn.email({ email: formData.get('email') as string, password: formData.get('password') as string, }); if (error) return { error: error.message }; redirect('/dashboard'); } ``` ## auth.signIn.social() Method: `auth.signIn.social(options)` Sign in with OAuth provider like Google, GitHub, etc. ### Parameters
View parameters | Parameter | Type | Required | | ----------- | ------ | -------- | | provider | string | ✓ | | callbackURL | string | |
```typescript const { data, error } = await auth.signIn.social({ provider: 'google', callbackURL: '/dashboard', }); ``` ## auth.signIn.emailOtp() Method: `auth.signIn.emailOtp(credentials)` Sign in with email OTP (one-time password). First call `emailOtp.sendVerificationOtp()` to send the code. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | email | string | ✓ | | otp | string | ✓ |
```typescript const { data, error } = await auth.signIn.emailOtp({ email: 'user@example.com', otp: '123456', }); ``` ## auth.signUp.email() Method: `auth.signUp.email(credentials)` Create a new user account with email and password. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | email | string | ✓ | | password | string | ✓ | | name | string | ✓ |
```typescript const { data, error } = await auth.signUp.email({ email: 'user@example.com', password: 'secure-password', name: 'John Doe', }); ``` ## auth.signOut() Method: `auth.signOut()` Sign out the current user. Clears session and authentication tokens. ```typescript 'use server'; import { auth } from '@/lib/auth/server'; import { redirect } from 'next/navigation'; export async function signOut() { await auth.signOut(); redirect('/auth/sign-in'); } ``` ## auth.updateUser() Method: `auth.updateUser(data)` Update the current user's profile. Password updates require the password reset flow for security. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------------------- | -------- | | name | string \| undefined | | | image | string \| undefined | |
```typescript const { data, error } = await auth.updateUser({ name: 'Jane Doe', image: 'https://example.com/avatar.jpg', }); ``` ## auth.changePassword() Method: `auth.changePassword(passwords)` Change the current user's password. ### Parameters
View parameters | Parameter | Type | Required | | ------------------- | ------- | -------- | | currentPassword | string | ✓ | | newPassword | string | ✓ | | revokeOtherSessions | boolean | |
```typescript const { data, error } = await auth.changePassword({ currentPassword: 'old-password', newPassword: 'new-password', revokeOtherSessions: true, }); ``` ## auth.sendVerificationEmail() Method: `auth.sendVerificationEmail(options)` Send email verification to the current user. ### Parameters
View parameters | Parameter | Type | Required | | ----------- | ------ | -------- | | callbackURL | string | |
```typescript const { data, error } = await auth.sendVerificationEmail({ callbackURL: '/dashboard', }); ``` ## auth.deleteUser() Method: `auth.deleteUser()` Delete the current user account. This action is irreversible. ```typescript const { data, error } = await auth.deleteUser(); ``` ## auth.emailOtp.sendVerificationOtp() Method: `auth.emailOtp.sendVerificationOtp(options)` Send a one-time password via email. Available when Email OTP authentication is enabled. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | email | string | ✓ |
```typescript const { data, error } = await auth.emailOtp.sendVerificationOtp({ email: 'user@example.com', }); ``` ## auth.emailOtp.verifyEmail() Method: `auth.emailOtp.verifyEmail(credentials)` Verify email with OTP code. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | email | string | ✓ | | otp | string | ✓ |
```typescript const { data, error } = await auth.emailOtp.verifyEmail({ email: 'user@example.com', otp: '123456', }); ``` ## auth.listSessions() Method: `auth.listSessions()` List all active sessions for the current user. ```typescript const { data, error } = await auth.listSessions(); ``` ## auth.revokeSession() Method: `auth.revokeSession(options)` Revoke a specific session by ID. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | sessionId | string | ✓ |
```typescript const { data, error } = await auth.revokeSession({ sessionId: 'session-id', }); ``` ## auth.revokeOtherSessions() Method: `auth.revokeOtherSessions()` Revoke all sessions except the current one. ```typescript const { data, error } = await auth.revokeOtherSessions(); ``` ## auth.organization.create() Method: `auth.organization.create(data)` Create a new organization. Available when the organizations plugin is enabled. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | name | string | ✓ | | slug | string | |
```typescript const { data, error } = await auth.organization.create({ name: 'My Organization', slug: 'my-org', }); ``` ## auth.organization.list() Method: `auth.organization.list()` List the current user's organizations. ```typescript const { data, error } = await auth.organization.list(); ``` ## auth.organization.inviteMember() Method: `auth.organization.inviteMember(options)` Invite a member to an organization. ### Parameters
View parameters | Parameter | Type | Required | | -------------- | ------ | -------- | | organizationId | string | ✓ | | email | string | ✓ | | role | string | |
```typescript const { data, error } = await auth.organization.inviteMember({ organizationId: 'org-id', email: 'member@example.com', role: 'member', }); ``` ## auth.admin.listUsers() Method: `auth.admin.listUsers(options)` List all users. Available for users with admin role. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | limit | number | | | offset | number | |
```typescript const { data, error } = await auth.admin.listUsers({ limit: 100, offset: 0, }); ``` ## auth.admin.banUser() Method: `auth.admin.banUser(options)` Ban a user. Available for users with admin role. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | userId | string | ✓ | | reason | string | |
```typescript const { data, error } = await auth.admin.banUser({ userId: 'user-id', reason: 'Violation of terms', }); ``` ## auth.admin.setRole() Method: `auth.admin.setRole(options)` Set a user's role. Available for users with admin role. ### Parameters
View parameters | Parameter | Type | Required | | --------- | ------ | -------- | | userId | string | ✓ | | role | string | ✓ |
```typescript const { data, error } = await auth.admin.setRole({ userId: 'user-id', role: 'admin', }); ``` ## Performance features ### Session caching Session data is automatically cached in a signed, HTTP-only cookie to reduce API calls to the Auth Server by 95-99%. - Default cache TTL: 5 minutes (300 seconds) - Configurable via `cookies.sessionDataTtl` - Automatic expiration based on JWT `exp` claim - Synchronous cache clearing on sign-out - Secure HMAC-SHA256 signing ### Request deduplication Multiple concurrent `getSession()` calls are automatically deduplicated: - Single network request for concurrent calls - 10x faster cold starts - Reduces server load ```typescript // First call: Fetches from Auth Server const { data: session } = await auth.getSession(); // Subsequent calls within TTL: Uses cached data (no API call) const { data: session2 } = await auth.getSession(); ``` ## Configuration reference Complete configuration options for `createNeonAuth()`: | Option | Type | Required | Default | | ------------------------ | ------ | -------- | --------- | | `baseUrl` | string | Yes | - | | `cookies.secret` | string | Yes | - | | `cookies.sessionDataTtl` | number | No | 300 | | `cookies.domain` | string | No | undefined | - **baseUrl**: Your Neon Auth server URL from the Neon Console - **cookies.secret**: Secret for HMAC-SHA256 signing (32+ characters) - **cookies.sessionDataTtl**: Cache TTL in seconds - **cookies.domain**: For cross-subdomain sessions (for example, ".example.com") ```typescript import { createNeonAuth } from '@neondatabase/auth/next/server'; export const auth = createNeonAuth({ baseUrl: process.env.NEON_AUTH_BASE_URL!, cookies: { secret: process.env.NEON_AUTH_COOKIE_SECRET!, }, }); ``` ## Project structure Recommended file structure for Next.js with Neon Auth: - `app/api/auth/[...path]/route.ts` - Auth API handlers - `app/auth/[path]/page.tsx` - Auth views (sign-in, sign-up) - `app/dashboard/page.tsx` - Protected pages - `lib/auth/server.ts` - Server auth instance - `lib/auth/client.ts` - Client auth instance - `middleware.ts` - Next.js middleware ``` app/ ├── api/ │ └── auth/ │ └── [...path]/ │ └── route.ts ├── auth/ │ └── [path]/ │ └── page.tsx ├── dashboard/ │ └── page.tsx ├── actions.ts └── layout.tsx lib/ └── auth/ ├── server.ts └── client.ts middleware.ts .env.local ``` ## Migration from v0.1 If you're upgrading from Neon Auth SDK v0.1, see the [migration guide](https://neon.com/docs/auth/migrate/from-auth-v0.1) for step-by-step instructions. ## Related documentation - [Client SDK Reference](https://neon.com/docs/reference/javascript-sdk) - Client-side authentication - [UI Components Reference](https://neon.com/docs/auth/reference/ui-components) - Pre-built auth UI - [Next.js quick start](https://neon.com/docs/auth/quick-start/nextjs-api-only) - Getting started guide - [Migration Guide](https://neon.com/docs/auth/migrate/from-auth-v0.1) - Upgrading from v0.1 --- [Document source](https://neon.com/docs/auth/reference/ui-components.md) --- # UI Components Reference Quick reference for Neon Auth UI components **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Quick reference for Neon Auth UI components from `@neondatabase/neon-js`. These components are built with [Better Auth UI](https://better-auth-ui.com/) and work with Neon Auth. ## Installation ```bash npm install @neondatabase/neon-js@latest ``` ## Provider Setup Wrap your app with `NeonAuthUIProvider` to enable the UI components. The provider accepts configuration props that control which features are available. ### Basic Setup ```tsx import { NeonAuthUIProvider } from '@neondatabase/neon-js/auth/react'; import '@neondatabase/neon-js/ui/css'; import { authClient } from './auth'; function App() { return ( {/* Your app components */} ); } ``` ### Common Props | Prop | Type | Description | Example | | ---------------------------- | ------------------------ | ------------------------------------------------------------------------ | -------------------------------------------------------- | | `authClient` | `NeonAuthPublicApi` | **Required.** Your Neon Auth client instance | `authClient={authClient}` | | `social.providers` | `SocialProvider[]` | Array of OAuth providers to enable (for example, Google, GitHub, Vercel) | `social={{ providers: ['google', 'github', 'vercel'] }}` | | `navigate` | `(href: string) => void` | Navigation function for React Router | `navigate={navigate}` | | `Link` | `ComponentType` | Custom Link component for routing | `Link={RouterLink}` | | `localization` | `AuthLocalization` | Customize text labels throughout the UI | See example below | | `avatar` | `AvatarOptions` | Avatar upload and display configuration | `avatar={{ size: 256, extension: 'webp' }}` | | `additionalFields` | `AdditionalFields` | Custom fields for sign-up and account settings | See example below | | `credentials.forgotPassword` | `boolean` | Enable forgot password flow | `credentials={{ forgotPassword: true }}` | ### Enable OAuth Providers To enable Google sign-in (or other OAuth providers), add the `social` prop to the provider: ```tsx import { NeonAuthUIProvider } from '@neondatabase/neon-js/auth/react'; import { authClient } from './auth'; function App() { return ( {/* Your app */} ); } ``` **Note:** Google OAuth works with shared credentials for development. GitHub OAuth requires custom credentials. The `social.providers` prop controls which provider buttons are displayed in the UI. For production, configure your own OAuth credentials in the Neon Console (Settings → Auth). See the [OAuth setup guide](https://neon.com/docs/auth/guides/setup-oauth) for details. ### React Router Integration If using React Router, pass the `navigate` function and a custom `Link` component: ```tsx import { NeonAuthUIProvider } from '@neondatabase/neon-js/auth/react'; import { useNavigate, Link as RouterLink } from 'react-router-dom'; import { authClient } from './auth'; function App() { const navigate = useNavigate(); return ( {/* Your app */} ); } ``` ### Customization Examples **Custom localization:** ```tsx ``` **Custom sign-up fields:** ```tsx ``` For complete prop documentation, see the TypeScript types exported from `@neondatabase/neon-js/auth/react`. ## Core Components ### Authentication Components | Component | Purpose | Key Props | Docs | | ------------ | ------------------------------------------------- | ---------- | ------------------------------------------------------------ | | `` | All-in-one auth UI with sign-in and sign-up forms | `pathname` | [auth-view](https://better-auth-ui.com/components/auth-view) | **Form Components:** ``, ``, ``, ``, and `` are also available. `` includes sign-in and sign-up functionality with a "create account" link to switch between forms. Use the form components separately if you need more control over layout. **OAuth Provider Buttons:** OAuth provider buttons (Google, GitHub, Vercel, etc.) appear automatically in `` when configured via the `social.providers` prop. OAuth buttons do not appear in standalone `` or `` components. ### User Management Components | Component | Purpose | Key Props | Docs | | -------------------- | ------------------------------------- | ---------------------- | -------------------------------------------------------------------------------- | | `` | User menu dropdown with avatar | - | [user-button](https://better-auth-ui.com/components/user-button) | | `` | Profile picture with Gravatar support | `user`, `size` | [user-avatar](https://better-auth-ui.com/components/user-avatar) | | `` | Conditional rendering when signed in | `children`, `fallback` | [signed-in](https://better-auth-ui.com/components/signed-in) | | `` | Conditional rendering when signed out | `children`, `fallback` | [signed-out](https://better-auth-ui.com/components/signed-out) | | `` | Redirect helper to sign-in page | `redirectTo` | [redirect-to-sign-in](https://better-auth-ui.com/components/redirect-to-sign-in) | | `` | Redirect helper to sign-up page | `redirectTo` | [redirect-to-sign-up](https://better-auth-ui.com/components/redirect-to-sign-up) | ## Styling Choose the import method based on your project setup: ### Without Tailwind CSS If your project doesn't use Tailwind CSS, import the pre-built CSS bundle: ```typescript // In your root layout or app entry point import '@neondatabase/neon-js/ui/css'; ``` This includes all necessary styles (~47KB minified) with no additional configuration required. ### With Tailwind CSS v4 If your project already uses Tailwind CSS v4, import the Tailwind-ready CSS to avoid duplicate styles: ```css /* In your main CSS file (for example, globals.css) */ @import 'tailwindcss'; @import '@neondatabase/neon-js/ui/tailwind'; ``` This imports only the theme variables. Your Tailwind build generates the utility classes. **Warning:** Never import both paths. This causes duplicate styles. For customization options, see **Styling** details within each Better Auth UI component docs page. Example: [Auth View styling](https://better-auth-ui.com/components/auth-view#styling). ## Example Usage ### Basic Auth Flow ```tsx import { AuthView } from '@neondatabase/neon-js/auth/react/ui'; import '@neondatabase/neon-js/ui/css'; function App() { return ; } ``` ### User Menu ```tsx import { UserButton } from '@neondatabase/neon-js/auth/react/ui'; import { authClient } from './auth'; function Header() { return (
); } ``` ### Protected Route ```tsx import { SignedIn, SignedOut, RedirectToSignIn } from '@neondatabase/neon-js/auth/react/ui'; function Dashboard() { return ( <>

Dashboard

); } ``` ## Next Steps - See the [neon-js examples](https://github.com/neondatabase/neon-js/tree/main/examples) for runnable apps that use these components - Check the [Neon TypeScript SDK](https://neon.com/docs/reference/javascript-sdk) for programmatic auth methods --- [Document source](https://neon.com/docs/auth/roadmap.md) --- # Neon Auth roadmap What's supported today and what's coming next **Note: Beta** The **Neon Auth with Better Auth** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). Neon Auth is in active development. This page shows what's currently supported and what we're working on next. ## General availability Neon Auth is targeting general availability this quarter. We're actively working on additional plugins and features to bring Neon Auth out of beta. ## Frameworks ### Supported | Framework | Status | Quick start | | ----------------------- | ----------- | --------------------------------------------------------------------- | | Next.js | ✅ Supported | [Get started](https://neon.com/docs/auth/quick-start/nextjs-api-only) | | Vite + React | ✅ Supported | [Get started](https://neon.com/docs/auth/quick-start/react) | | React + React Router | ✅ Supported | [Get started](https://neon.com/docs/auth/quick-start/react) | | React + TanStack Router | ✅ Supported | [Get started](https://neon.com/docs/auth/quick-start/tanstack-router) | The [React quick start](https://neon.com/docs/auth/quick-start/react) steps through a **Vite** + React app. If you use **React Router** for URLs and layouts, use the same Neon Auth URL, `@neondatabase/neon-js` client, and SDK calls from that guide, then integrate them into your router and route components. For file-based routing with TanStack, use the TanStack Router quick start instead. ### On the roadmap | Framework | Status | | ----------------------------- | --------------- | | Standalone frontend + backend | 🔜 Coming soon | | Other frameworks | Based on demand | **Note: Standalone architectures** Architectures where frontend and backend are separate deployments (for example, Create-React-App with a separate Node/Express backend) are not yet supported. Neon Auth uses HTTP-only cookies for secure session management, and these cookies cannot be securely shared between frontend and backend applications on different domains. We're actively working on this. ## Better Auth plugins Neon Auth is built on [Better Auth](https://www.better-auth.com/). Not all Better Auth plugins are currently supported. ### Supported | Plugin | Status | | ---------------------------------------------------------------------------------- | ----------------------------------------------- | | [Email & password](https://www.better-auth.com/docs/authentication/email-password) | ✅ Supported | | Social OAuth (Google, GitHub, Vercel) | ✅ Supported | | [Email OTP](https://neon.com/docs/auth/guides/plugins/email-otp) | ✅ Supported | | [Admin](https://neon.com/docs/auth/guides/plugins/admin) | ✅ Supported | | [Organization](https://neon.com/docs/auth/guides/plugins/organization) | ⚠️ Partial (JWT token claims under development) | | [JWT](https://neon.com/docs/auth/guides/plugins/jwt) | ✅ Supported | | [Open API](https://neon.com/docs/auth/guides/plugins/openapi) | ✅ Supported | See [Set up OAuth](https://neon.com/docs/auth/guides/setup-oauth) for Neon-specific OAuth configuration (including Vercel). Email flows such as verification and password reset are covered in [Email verification](https://neon.com/docs/auth/guides/email-verification), [Password reset](https://neon.com/docs/auth/guides/password-reset), and [User management](https://neon.com/docs/auth/guides/user-management). ### Platform (Console, API, webhooks) These capabilities are documented in Neon Auth guides but are not Better Auth plugins you enable through the SDK. | Capability | Status | Documentation | | ------------------------------------ | --------- | -------------------------------------------------------------------------------- | | Trusted domains (redirect allowlist) | Supported | [Configure trusted domains](https://neon.com/docs/auth/guides/configure-domains) | | Webhooks (auth events) | Supported | [Webhooks](https://neon.com/docs/auth/guides/webhooks) | | Manage Auth via Neon API | Supported | [Manage Auth in the Neon API](https://neon.com/docs/auth/guides/manage-auth-api) | Branch-aware auth (separate auth state per Neon branch) is supported; see [Branching authentication](https://neon.com/docs/auth/branching-authentication) and [Authentication flow](https://neon.com/docs/auth/authentication-flow). ### On the roadmap | Plugin | Status | | ----------------------------------------------------------------------------- | --------------- | | [Magic link](https://www.better-auth.com/docs/plugins/magic-link) | 🔜 Coming soon | | Phone number (bring your own SMS provider) | 🔜 Coming soon | | MFA support | 🔜 Coming soon | | [Admin](https://neon.com/docs/auth/guides/plugins/admin) plugin customization | 🔜 Coming soon | | Other plugins | Based on demand | ## SDK and UI references | Surface | Documentation | | --------------------------------- | ------------------------------------------------------------------------ | | TypeScript client SDK (`neon-js`) | [Neon TypeScript SDK](https://neon.com/docs/reference/javascript-sdk) | | Next.js server SDK | [Next.js Server SDK](https://neon.com/docs/auth/reference/nextjs-server) | | Pre-built UI components | [UI components](https://neon.com/docs/auth/reference/ui-components) | ## Migration guides - [From Neon Auth SDK v0.1](https://neon.com/docs/auth/migrate/from-auth-v0.1) - [From Stack Auth (legacy)](https://neon.com/docs/auth/migrate/from-legacy-auth) - [From Supabase](https://neon.com/docs/auth/migrate/from-supabase) ## Production and checklist For launch readiness, see the [production checklist](https://neon.com/docs/auth/production-checklist). ## Let us know We prioritize based on demand. If you need a specific framework or plugin, let us know through our [Discord](https://discord.com/invite/92vNTzKDGp) or [GitHub Discussions](https://github.com/orgs/neondatabase/discussions). --- [Document source](https://neon.com/docs/auth/troubleshooting.md) --- # Auth troubleshooting Common issues when implementing Neon Auth and how to fix them This page covers common issues when integrating [Neon Auth](https://neon.com/docs/auth/overview) with `@neondatabase/auth` (Next.js) or `@neondatabase/neon-js` (React SPAs). ## Missing NEON_AUTH_COOKIE_SECRET If your Next.js app throws an error about a missing or invalid cookie secret at startup, the `NEON_AUTH_COOKIE_SECRET` environment variable is either unset or too short. The secret must be at least 32 characters for HMAC-SHA256. Generate one with: ```bash openssl rand -base64 32 ``` See the [Next.js Server SDK reference](https://neon.com/docs/auth/reference/nextjs-server) for the full list of required environment variables. ## Missing force-dynamic on server components If `next build` fails with a "Dynamic server usage" error on a page that calls `auth.getSession()`, the server component needs to opt out of static rendering: ```typescript export const dynamic = 'force-dynamic'; export default async function Page() { const { data: session } = await auth.getSession(); // ... } ``` This is required because `getSession()` reads cookies, which are only available at request time. See [getSession](https://neon.com/docs/auth/reference/nextjs-server#get-session) in the Next.js Server SDK reference. ## Using v0.1 API patterns If you are upgrading from v0.1, use `createNeonAuth()` + `auth.handler()` instead of the old standalone `authApiHandler()`. See the [migration guide](https://neon.com/docs/auth/migrate/from-auth-v0.1) for details. ## Using useSession() without adapter in React SPA In a React SPA, `createAuthClient(url)` without an adapter returns a vanilla client with no React hooks. Calling `useSession()` on this client will fail. Either pass `BetterAuthReactAdapter()` or use UI components (`SignedIn`, `SignedOut`, `UserButton`) which do not require an adapter. ```typescript import { createAuthClient } from '@neondatabase/neon-js/auth'; import { BetterAuthReactAdapter } from '@neondatabase/neon-js/auth/react'; const authClient = createAuthClient(url, { adapter: BetterAuthReactAdapter(), }); ``` The [TanStack Router quick start](https://neon.com/docs/auth/quick-start/tanstack-router) shows a complete setup with the React adapter. ## Wrong BetterAuthReactAdapter import The adapter must be imported from a subpath and called as a function: ```typescript // Wrong import { BetterAuthReactAdapter } from '@neondatabase/neon-js'; // Correct import { BetterAuthReactAdapter } from '@neondatabase/neon-js/auth/react'; const client = createAuthClient(url, { adapter: BetterAuthReactAdapter() }); ``` ## CSS import conflicts Choose one CSS import method. Never use both, as this causes duplicate styles: - **Without Tailwind:** `import '@neondatabase/auth/ui/css'` (Next.js) or `import '@neondatabase/neon-js/ui/css'` (React SPA) - **With Tailwind v4:** `@import '@neondatabase/auth/ui/tailwind'` (Next.js) or `@import '@neondatabase/neon-js/ui/tailwind'` (React SPA) See the [UI Components reference](https://neon.com/docs/auth/reference/ui-components) for complete setup instructions. ## Missing "use client" directive In Next.js, any component that uses `useSession()` or other React hooks must include `"use client"` at the top of the file. Without it, Next.js treats the file as a server component where React hooks are not available. ## Wrong createAuthClient signature The `createAuthClient` function has different signatures depending on the import path: ```typescript // React SPA — import from @neondatabase/neon-js/auth import { createAuthClient } from '@neondatabase/neon-js/auth'; createAuthClient(url); createAuthClient(url, { adapter: BetterAuthReactAdapter() }); // Next.js — import from @neondatabase/auth/next (no arguments, uses proxy) import { createAuthClient } from '@neondatabase/auth/next'; createAuthClient(); ``` See the [Next.js quick start](https://neon.com/docs/auth/quick-start/nextjs-api-only) and the [React quick start](https://neon.com/docs/auth/quick-start/react) for complete client setup examples. --- [Document source](https://neon.com/docs/community/ai-tools.md) --- # AI tools for documentation Claude and Cursor rules and commands used when working on Neon docs The Neon website repo includes rules and commands for **Claude** and **Cursor** so that documentation work follows the same standards no matter which tool you use. All changes are reviewed by humans via pull requests. ## Cursor (`.cursor/`) | Tool | Description | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Neon changelog** | Generate next Friday's changelog draft or a changelog for a specific date; template with titled dropdown sections. Rule: `neon-changelog.mdc`. | | **Docs editing** | When editing docs/guides/changelog: apply Neon voice (see [contribution guide](https://neon.com/docs/community/contribution-guide)), use shared content, keep cross-doc consistency, link to glossary, follow redirect workflow. Rule: `docs-editing.mdc`. | | **Docs use Claude** | Points to `.claude/` agents and commands so Cursor can load them for style and workflows. Rule: `docs-use-claude.mdc`. | | **Docs PR report** | Weekly doc review: report of merged PRs across monitored repos (default: since last Friday), grouped by category with docs-impact indicators. Reports to \~/docs-reviews/. Rule: `docs-pr-report.mdc`. | | **Consistency check** | Find other places that say the same thing; suggest a single source or aligned wording. Skill: `.cursor/skills/consistency-check.md`. | | **Docs glossary updater** | Compare a doc to the glossary; list missing or review terms. Skill: `.cursor/skills/docs-glossary-updater.md`. | | **Redirect and links** | After moving a file, ensure redirectFrom and suggest link/nav updates. Skill: `.cursor/skills/redirect-and-links.md`. | | **Docs Roadmap updater** | Review changelog (default: past 1 month) and sync the introduction roadmap with "What we've shipped recently." Skill: `.cursor/skills/docs-roadmap-updater.md`. | ## Claude (`.claude/`) | Tool | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | **golden-corpus** | Exemplary doc files by type; use for style, tone, and structure. | | **write-content** | Full workflow: IA → drafter → refiner → syntax-validator, then git/PR. | | **simple-content** | Lighter workflow with user confirmation at each step. | | **review-content** | One-off review for style, standards, and technical accuracy. | | **redirect-update** | Step-by-step for moves/renames: redirectFrom, links, navigation. (Same workflow as Cursor **Redirect and links**.) | | **triage-changelog** | Extract PRs from Console/CLI/MCP/Storage/Compute and draft changelog. | | **docs-prime** | Project structure and key paths for the doc ecosystem. | | **improve-intro** | Improve the first paragraph of a doc page. | | **navigation-principles** | How `navigation.yaml` works (nav, subnav, items). | | **doc-context-bridge** | Points to `.cursor/` rules and skills so Claude can use Consistency check, Docs glossary updater, Redirect and links, Docs Roadmap updater. | | **list-doc-ai-tools** | Prints the list of doc AI tools when run or when asked (for example "what doc AI tools are available?"). | | **Agents** | content-drafter, content-planner, content-refiner, ia-specialist, syntax-validator, supervisor; extract-analyze-\* for changelog. | For full details and when to use each tool, see the files under `.cursor/` and `.claude/commands/` or `.claude/agents/` in the [website repository](https://github.com/neondatabase/website). --- [Document source](https://neon.com/docs/community/community-intro.md) --- # Neon community Learn how to get involved in the Neon community Neon is [open source](https://neon.com/docs/get-started/why-neon#neon-is-open-source) and has an enthusiastic user community worldwide. Here's how you can get involved: ## Contribute There are many ways to contribute to the Neon community: - **Neon Docs**: Share suggestions, contribute content, or write new guides to help others working with Neon. Check out our [Documentation Contribution Guide](https://neon.com/docs/community/contribution-guide) to get started. You can also create visual diagrams using our [Mermaid Diagrams Guide](https://neon.com/docs/community/mermaid-diagrams). If you want to consume our docs as markdown (e.g. for LLMs or APIs), see [Using Neon docs as markdown](https://neon.com/docs/community/llms-markdown-guide). - **Community Guides**: Share your knowledge by writing a guide about Neon or Postgres. These guides can help developers learn new technologies, techniques, and best practices. Submit your guide to our [Community Guides](https://neon.com/guides) page by forking the [Neon website repository](https://github.com/neondatabase/website) and creating a PR to add it to the [/content/guides](https://github.com/neondatabase/website/tree/main/content/guides) directory. - **Examples and applications**: Share examples and applications that demonstrate how to integrate Neon with different tools and platforms. Post your examples on our [Discord Server](https://discord.gg/92vNTzKDGp) or contribute to the [Neon examples repository](https://github.com/neondatabase/examples). - **Code contributions**: Contribute to Neon's development by fixing bugs, proposing new features, or submitting code to [Neon's GitHub repositories](https://github.com/neondatabase). It's a great way to learn about Neon's architecture. ## Join the Discussion Connect with the community and share your insights on our Discord Server and X (Twitter). Also, subscribe to the Neon YouTube channel for videos and presentations. - [Neon Discord Server](https://discord.gg/92vNTzKDGp) - [X (Twitter)](https://twitter.com/neondatabase) - [Neon YouTube](https://www.youtube.com/@neondatabase) --- [Document source](https://neon.com/docs/community/component-architecture.md) --- # Component architecture Technical implementation details for developers and maintainers A technical reference for developers and maintainers who need to understand, modify, or extend the Neon documentation component system. This guide covers the architecture, file structure, and implementation details. **What you will learn:** - How MDX components are structured and organized - Technical implementation details for each component type - Development workflows for adding and modifying components - File organization and build process details **Related topics** - [Component Guide](https://neon.com/docs/community/component-guide) - [Component Specialized Guide](https://neon.com/docs/community/component-specialized) - [Component Icon Guide](https://neon.com/docs/community/component-icon-guide) - [Documentation Contribution Guide](https://neon.com/docs/community/contribution-guide) ## Quick navigation - [Component architecture](https://neon.com/docs/community/component-architecture#component-architecture) - How MDX components are integrated - [File structure](https://neon.com/docs/community/component-architecture#file-structure) - Directory organization and component locations - [Component registration](https://neon.com/docs/community/component-architecture#component-registration) - How components are registered in MDX - [Icon systems](https://neon.com/docs/community/component-architecture#icon-systems) - Technical implementation of icon loading - [Shared content](https://neon.com/docs/community/component-architecture#shared-content) - How shared templates work - [Development workflow](https://neon.com/docs/community/component-architecture#development-workflow) - How to add, modify, or debug components - [Build process](https://neon.com/docs/community/component-architecture#build-process) - How components are processed during build - [Troubleshooting](https://neon.com/docs/community/component-architecture#troubleshooting) - Common issues and debugging techniques --- ## Component architecture ### MDX integration Neon documentation uses MDX (Markdown + JSX) to enable React components within markdown content. The component system is built on top of this foundation. ### Component hierarchy ``` MDX Content ↓ Component Registry (sharedMdxComponents) ↓ React Components (src/components/pages/doc/) ↓ Rendered HTML ``` ### Key files - `sharedMdxComponents.js` - Main component registry - `src/components/pages/doc/` - Component implementations - `content/docs/` - MDX content files - `content/docs/shared-content/` - Shared template content --- ## File structure ### Component directory organization Most MDX components follow a consistent pattern in the Neon website repository: ``` src/components/pages/doc/ ├── {component-name}/ │ ├── {component-name}.jsx # Main component implementation │ ├── index.js # Export file │ └── images/ # Component-specific assets │ └── {icons}.inline.svg # Inline SVG icons ├── shared/ # Shared components │ ├── request-form/ │ └── ... └── ... ``` ### Example: DocsList component Let's explore the DocsList component as an example: ``` src/components/pages/doc/docs-list/ ├── docs-list.jsx # Main component implementation ├── index.js # Export: export { default } from './docs-list' └── images/ # Component-specific images ├── docs.inline.svg └── repo.inline.svg ``` ### Key files to check When exploring a component, look for these files: - **Component Definition** (`{component-name}.jsx`) - Contains the React component code - Defines props and their types - Implements the rendering logic - May include styled components or CSS modules - **Index File** (`index.js`) - Usually a simple export: `export { default } from './component-name';` - Makes the component importable from the directory - **Images Directory** (`images/`) - Contains SVGs, icons, or other assets used by the component - For icon-based components, this is where you'll find available icons --- ## Component registration ### Registration process Components are registered in `sharedMdxComponents.js` to make them available in MDX files: ```js // sharedMdxComponents.js import DocsList from '../src/components/pages/doc/docs-list'; import TechCards from '../src/components/pages/doc/tech-cards'; // ... other imports export const sharedMdxComponents = { DocsList, TechCards, // ... other components }; ``` ### MDX Integration The `sharedMdxComponents` object is passed to the MDX processor, making all registered components available in markdown files: ```jsx // In MDX files Node.js Guide ``` ### Component availability - **Global**: Components registered in `sharedMdxComponents` are available in all MDX files - **Local**: Some components may be registered locally for specific pages - **Conditional**: Components may be conditionally available based on page context --- ## Icon systems ### TechCards icon loading TechCards icons are loaded dynamically from the public directory. If the file exists, it will be rendered. Otherwise, the image will be broken or missing. **File Requirements:** - Icons must exist in `/public/images/technology-logos/` - Format: `{icon-name}.svg` and `{icon-name}-dark.svg` - Case-sensitive naming ### DetailIconCards icon loading DetailIconCards icons are statically imported and mapped: ```js // DetailIconCards component implementation import Prisma from './images/prisma.inline.svg'; import NodeJs from './images/node-js.inline.svg'; // ... other imports const icons = { prisma: Prisma, 'node-js': NodeJs, // ... other mappings }; const Icon = icons[icon]; ``` **File Requirements:** - Icons must be imported as inline SVGs - Must be explicitly mapped in the component code - Case-sensitive naming ### Icon system differences | Aspect | TechCards | DetailIconCards | | --------------- | ---------------------------------- | ---------------------------- | | **Loading** | Dynamic file loading | Static imports | | **Location** | `/public/images/technology-logos/` | Component-specific `images/` | | **Format** | Standard SVG files | Inline SVG imports | | **Dark Mode** | Separate `-dark.svg` files | Single file with CSS | | **Flexibility** | Easy to add new icons | Requires code changes | --- ## Shared content ### Shared templates Shared content components load from templates in `content/docs/shared-content/`: ``` content/docs/shared-content/ ├── feature-beta.md ├── public-preview.md ├── early-access.md └── ... ``` ### Template registration Templates are registered in `shared-content/index.js`: ```js // shared-content/index.js export const sharedContent = { 'feature-beta': () => import('./feature-beta.md'), 'public-preview': () => import('./public-preview.md'), // ... other templates }; ``` ### Usage in components Shared content is loaded dynamically: ```jsx // In MDX files // Loads content from feature-beta.md // Loads content from public-preview.md ``` ### Template structure Shared templates are standard MDX files with frontmatter: ```mdx --- title: Feature Beta description: Beta feature announcement --- This feature is currently in beta... ``` --- ## Development workflow ### Adding a new component 1. **Create Component Directory** ```bash mkdir src/components/pages/doc/my-component ``` 2. **Create Component File** ```jsx // src/components/pages/doc/my-component/my-component.jsx import React from 'react'; const MyComponent = ({ title, children }) => { return (

{title}

{children}
); }; export default MyComponent; ``` 3. **Create Index File** ```js // src/components/pages/doc/my-component/index.js export { default } from './my-component'; ``` 4. **Register Component** ```js // sharedMdxComponents.js import MyComponent from '../src/components/pages/doc/my-component'; export const sharedMdxComponents = { // ... existing components MyComponent, }; ``` 5. **Test Component** ```mdx This is a test of my new component. ``` ### Modifying existing components 1. **Locate Component** - Find the component in `src/components/pages/doc/{component-name}/` - Check the main `.jsx` file for implementation 2. **Make Changes** - Modify the component logic in the `.jsx` file - Update props, styling, or functionality as needed 3. **Test Changes** - Check that the component renders correctly - Verify that existing usage still works - Test edge cases and error conditions ### Debugging components 1. **Check Console Errors** - Look for JavaScript errors in browser console - Check for missing imports or undefined variables 2. **Verify Registration** - Ensure component is properly exported from `index.js` - Confirm component is registered in `sharedMdxComponents` 3. **Check File Paths** - Verify all imports resolve correctly - Check that asset files exist in expected locations 4. **Test in Isolation** - Create a simple test page with just the component - Remove other components to isolate issues --- ## Build process ### MDX processing 1. **Content Loading**: MDX files are loaded from `content/docs/` 2. **Component Resolution**: `sharedMdxComponents` are injected into MDX context 3. **Transformation**: MDX is transformed to React components 4. **Rendering**: Components are rendered to HTML ### Build pipeline ``` MDX Files → MDX Processor → React Components → HTML Output ↓ ↓ ↓ ↓ content/docs/ → sharedMdxComponents → src/components/ → public/ ``` ### Optimization - **Code Splitting**: Components are code-split for performance - **Asset Optimization**: Images and SVGs are optimized during build - **Caching**: Static assets are cached for faster loading --- ## Troubleshooting ### Common issues 1. **Component Not Found** - Check that component is registered in `sharedMdxComponents` - Verify the component name matches exactly (case-sensitive) - Ensure the component is properly exported 2. **Icon Not Displaying** - For TechCards: Check if SVG file exists in `/public/images/technology-logos/` - For DetailIconCards: Check if icon is mapped in component code - Verify icon name spelling and case 3. **Build Errors** - Check for syntax errors in component files - Verify all imports resolve correctly - Ensure all required dependencies are installed 4. **Styling Issues** - Check CSS class names and specificity - Verify that styles are properly imported - Test in different browsers and screen sizes ### Debugging techniques 1. **Console Logging** ```jsx const MyComponent = (props) => { console.log('MyComponent props:', props); return
...
; }; ``` 2. **React DevTools** - Use React DevTools to inspect component hierarchy - Check props and state values - Verify component rendering 3. **Network Tab** - Check for failed asset requests - Verify that images and SVGs load correctly - Look for 404 errors 4. **Source Maps** - Enable source maps for better error tracking - Use browser dev tools to step through code ### Performance considerations 1. **Bundle Size** - Keep components lightweight - Use code splitting for large components - Optimize images and assets 2. **Rendering Performance** - Avoid expensive operations in render methods - Use React.memo for expensive components - Implement proper key props for lists 3. **Asset Loading** - Optimize image sizes and formats - Use appropriate loading strategies - Consider lazy loading for non-critical components --- ## Summary This guide provides technical details for working with Neon's MDX component system. Key points: - **Components** are organized in `src/components/pages/doc/` - **Registration** happens in `sharedMdxComponents.js` - **Icons** use different loading strategies for TechCards vs DetailIconCards - **Shared content** loads from templates in `content/docs/shared-content/` - **Development** follows standard React patterns with MDX integration For component usage examples, see the [Component Guide](https://neon.com/docs/community/component-guide) and [Component Specialized Guide](https://neon.com/docs/community/component-specialized). --- [Document source](https://neon.com/docs/community/component-guide.md) --- # Component guide Most commonly used components for documentation writers A practical guide for the most commonly used MDX components in Neon documentation. This guide focuses on components you'll use most frequently when writing documentation. **What you will learn:** - How to use common MDX components in Neon docs - How to choose between different components - Best practices - Proper syntax and prop usage **Related topics** - [Component Specialized Guide](https://neon.com/docs/community/component-specialized) - [Component Icon Guide](https://neon.com/docs/community/component-icon-guide) - [Component Architecture](https://neon.com/docs/community/component-architecture) - [Documentation Contribution Guide](https://neon.com/docs/community/contribution-guide) ## Quick navigation - [Essential components](https://neon.com/docs/community/component-guide#essential-components) - Most commonly used - [Tabbed content](https://neon.com/docs/community/component-guide#tabbed-content) - CodeTabs and Tabs for organized content - [Content organization](https://neon.com/docs/community/component-guide#content-organization) - Structure and navigation components - [Interactive elements](https://neon.com/docs/community/component-guide#interactive-elements) - UI elements and forms - [Common shared components](https://neon.com/docs/community/component-guide#common-shared-components) - Reusable content --- ## Essential components These are the most frequently used components in Neon docs. ### Admonition Callouts for notes, warnings, and tips. There are six types available: `note` (default), `important`, `tip`, `info`, `warning`, `comingSoon`. ```mdx Critical information requiring immediate attention. ``` **Live preview:** **Warning: Important** Critical information requiring immediate attention. **All Admonition types:** **Note: Note** Highlights information that users should take into account. **Important:** Crucial information necessary for users to succeed. **Tip: Pro tip** Optional information to help a user be more successful. **Info:** Information that helps users understand things better. **Warning:** Critical content demanding immediate user attention due to potential risks. **Coming Soon:** Information about features that are coming soon. --- ### Steps Numbered step-by-step instructions split by `h2` headings. ```mdx ## Get a Glass Take a clean glass from the cabinet or dish rack. ## Turn on Tap Adjust the faucet to your preferred temperature and flow rate. ## Fill and Drink Fill the glass to desired level and enjoy your water. ``` **Live preview:** ## Get a Glass Take a clean glass from the cabinet or dish rack. ## Turn on Tap Adjust the faucet to your preferred temperature and flow rate. ## Fill and Drink Fill the glass to desired level and enjoy your water. --- ## Tabbed content Components for organizing content into tabs. ### CodeTabs Multi-language code examples with tabs. ````mdx ```javascript const { Client } = require('pg'); const client = new Client({ connectionString: process.env.DATABASE_URL, }); await client.connect(); ``` ```python import psycopg2 import os conn = psycopg2.connect(os.environ["DATABASE_URL"]) cur = conn.cursor() ``` ```go import ( "database/sql" _ "github.com/lib/pq" ) db, err := sql.Open("postgres", os.Getenv("DATABASE_URL")) ``` ```` **Live preview:** **JavaScript** ```javascript const { Client } = require('pg'); const client = new Client({ connectionString: process.env.DATABASE_URL, }); await client.connect(); ``` **Python** ```python import psycopg2 import os conn = psycopg2.connect(os.environ["DATABASE_URL"]) cur = conn.cursor() ``` **Go** ```go import ( "database/sql" _ "github.com/lib/pq" ) db, err := sql.Open("postgres", os.Getenv("DATABASE_URL")) ``` --- ### Tabs General tabbed content (not just code). For code-specific tabs, use [CodeTabs](https://neon.com/docs/community/component-guide#codetabs) instead. ````mdx Create a database using the Neon Console by navigating to your project dashboard and clicking "Create Database". Use the Neon CLI to create a database: ```bash neon databases create --name my-database ``` Use the API to create a database: ```bash curl -X POST https://console.neon.tech/api/v2/projects/my-project/databases \ -H "Authorization: Bearer $NEON_API_KEY" ``` ```` **Live preview:** **Console** Create a database using the Neon Console by navigating to your project dashboard and clicking "Create Database". **CLI** Use the Neon CLI to create a database: ```bash neon databases create --name my-database ``` **API** Use the API to create a database: ```bash curl -X POST https://console.neon.tech/api/v2/projects/my-project/databases \ -H "Authorization: Bearer $NEON_API_KEY" ``` --- ## Content organization Components for structuring and organizing page content. ### TechCards / DetailIconCards Technology cards with icons, titles, and descriptions. These components use different [icon systems](https://neon.com/docs/community/component-icon-guide) - see the [comparison table](https://neon.com/docs/community/component-guide#techcards-vs-detailiconcards-vs-docslist) below to choose the right one. #### TechCards Standard technology cards layout using [TechCards icons](https://neon.com/docs/community/component-icon-guide#techcards-icons): ```mdx Node.js Python Next.js ``` **Live preview:** - [Node.js](https://neon.com/docs/guides/node): Connect Node.js applications to Neon - [Python](https://neon.com/docs/guides/python): Connect Python applications to Neon - [Next.js](https://neon.com/docs/guides/nextjs): Build Next.js apps with Neon --- #### DetailIconCards Alternative layout using [DetailIconCards icons](https://neon.com/docs/community/component-icon-guide#detailiconcards-icons): ```mdx OpenAI Integration LangChain Integration Code Development AWS Integration ``` **Live preview:** - [OpenAI Integration](https://neon.com/docs/ai/openai): Build AI features with OpenAI - [LangChain Integration](https://neon.com/docs/ai/langchain): Create AI workflows with LangChain - [Code Development](https://neon.com/docs/development): Development tools and practices - [AWS Integration](https://neon.com/docs/cloud/aws): Deploy and scale with AWS --- > DetailIconCards uses a different [icon system](https://neon.com/docs/community/component-icon-guide#detailiconcards-icons) than [TechCards](https://neon.com/docs/community/component-icon-guide#techcards-icons), which is why different icons are available._ #### TechCards vs DetailIconCards vs DocsList Quick comparison to help you choose the right component: | Component | Use For | Icon System | Layout | | -------------------------------------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------- | ----------- | | **[TechCards](https://neon.com/docs/community/component-guide#techcards)** | Technology/framework showcases | [Technology logos](https://neon.com/docs/community/component-icon-guide#techcards-icons) (colorful) | Card grid | | **[DetailIconCards](https://neon.com/docs/community/component-guide#detailiconcards)** | Feature/service showcases | [Detail icons](https://neon.com/docs/community/component-icon-guide#detailiconcards-icons) (monochrome) | Card grid | | **[DocsList](https://neon.com/docs/community/component-guide#docslist)** | Documentation links | Checkbox (default), docs, or repo icon | Simple list | ### DefinitionList Accessible term/definition lists for defining technical terms and concepts. ```mdx Database URL : Connection string for your Neon database : Format: `postgresql://user:password@host:port/database` Connection Pool : A cache of database connections : Improves performance by reusing connections Branch : An isolated copy of your database : Used for development and testing ``` **Live preview:** Database URL : Connection string for your Neon database : Format: `postgresql://user:password@host:port/database` Connection Pool : A cache of database connections : Improves performance by reusing connections Branch : An isolated copy of your database : Used for development and testing --- ### DocsList Simple, clean lists for documentation links with optional theming. DocsList provides a lightweight alternative to card-based components for presenting navigation links or content summaries. **Props:** - `title` (string) - Optional title for the list section - `theme` (string) - Visual theme: `"docs"` (document icon), `"repo"` (repository icon), or default (checkbox icon) **Default Theme (Checkbox Icon):** **MDX Code:** ```mdx Node.js Connection Guide Python Connection Guide API Reference CLI Documentation ``` **Live preview:** **Related documentation** - [Node.js Connection Guide](https://neon.com/docs/guides/node) - [Python Connection Guide](https://neon.com/docs/guides/python) - [API Reference](https://neon.com/docs/api-reference) - [CLI Documentation](https://neon.com/docs/cli) --- ### InfoBlock InfoBlock creates a multi-column layout for organizing related content sections. It's particularly useful for creating "at-a-glance" summaries at the top of documentation pages, combining learning objectives with related resources. Use two columns. **Key Features:** - Commonly paired with DocsList for structured content presentation - Ideal for page introductions and overview sections **Basic Two-Column Layout:** ```mdx

How to view and modify data in the console

Create an isolated database copy per developer

Reset your branch to production when ready to start new work

About branching Branching workflows Connect Neon to your stack
``` **Renders as:** **What you will learn:** - How to view and modify data in the console - Create an isolated database copy per developer - Reset your branch to production when ready to start new work **Related topics** - [About branching](https://neon.com/docs/introduction/branching) - [Branching workflows](https://neon.com/docs/get-started/workflow-primer) - [Connect Neon to your stack](https://neon.com/docs/get-started/connect-neon) --- ## Interactive elements Components for user engagement and interaction. ### CheckList Interactive checklists for setup guides and tutorials. CheckList uses CheckItem components internally. ```mdx Sign up for a free Neon account at console.neon.tech Install the required packages for your project Set up your database connection string Verify your application can connect to Neon ``` **Live preview:** ## Setup checklist - [ ] [Create Neon account](https://neon.com/docs/community/component-guide#signup) Sign up for a free Neon account at console.neon.tech - [ ] [Install dependencies](https://neon.com/docs/community/component-guide#install) Install the required packages for your project - [ ] [Configure environment](https://neon.com/docs/community/component-guide#config) Set up your database connection string - [ ] [Test connection](https://neon.com/docs/community/component-guide#test) Verify your application can connect to Neon --- ### CheckItem Individual checklist items used within CheckList components. ```mdx Description of the task or requirement ``` --- **Usage Notes:** - Always used within a `` component - `title` prop is required - `href` prop is optional for anchor linking - Content is the description text ### CTA (Call to Action) Prominent call-to-action buttons for important actions. ```mdx ``` **Live preview:** > **Try Neon free** > > Start building with serverless Postgres today. No credit card required. > > [Sign Up](https://console.neon.tech/signup) --- ### NeedHelp Support widget for getting assistance. ```mdx ``` **Live preview:** --- --- ## Common shared components Reusable content components that load from shared templates. ### LinkAPIKey Link to API key management in the console. ```mdx ``` **Live preview:** **Note:** To learn more about the types of API keys you can create — personal, organization, or project-scoped — see [Manage API Keys](https://neon.com/docs/manage/api-keys). --- ### FeatureBetaProps Status indicator for beta features with custom feature name. ```mdx ``` **Live preview:** **Note: Beta** The **OpenTelemetry integration** is in Beta. Share your feedback on [Discord](https://discord.gg/92vNTzKDGp) or via the [Neon Console](https://console.neon.tech/app/projects?modal=feedback). --- --- ## Best practices ### Component selection - **[Admonition](https://neon.com/docs/community/component-guide#admonition)** for important callouts - **[Steps](https://neon.com/docs/community/component-guide#steps)** for sequential instructions - **[CodeTabs](https://neon.com/docs/community/component-guide#codetabs)** for multi-language examples - **[TechCards](https://neon.com/docs/community/component-guide#techcards)** for technology showcases - **[CheckList](https://neon.com/docs/community/component-guide#checklist)** for setup guides - **[InfoBlock](https://neon.com/docs/community/component-guide#infoblock)** for page introductions with multiple content sections - **[DocsList](https://neon.com/docs/community/component-guide#docslist)** for simple navigation lists with theming options ### Content organization tips - Use [InfoBlock](https://neon.com/docs/community/component-guide#infoblock) at the top of pages to provide quick orientation - For [TechCards](https://neon.com/docs/community/component-guide#techcards), always check that the SVG file exists in `/public/images/technology-logos/` - For [DetailIconCards](https://neon.com/docs/community/component-guide#detailiconcards), use only icon names that are mapped in the component code - Choose [DocsList](https://neon.com/docs/community/component-guide#docslist) themes based on content type: default for tasks, `docs` for documentation, `repo` for code - Use the [comparison table](https://neon.com/docs/community/component-guide#techcards-vs-detailiconcards-vs-docslist) to choose between [TechCards](https://neon.com/docs/community/component-guide#techcards), [DetailIconCards](https://neon.com/docs/community/component-guide#detailiconcards), and [DocsList](https://neon.com/docs/community/component-guide#docslist) --- ## Component summary This guide covers the most commonly used MDX components in Neon documentation. Each component includes: - **MDX syntax**: Copy-paste ready code examples - **Live rendering**: See exactly how components appear - **Props documentation**: Available parameters and options - **Best practices**: When and how to use each component ### Component categories | **Category** | **Components** | **Use Case** | | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | | **[Essential](https://neon.com/docs/community/component-guide#essential-components)** | [Admonition](https://neon.com/docs/community/component-guide#admonition), [Steps](https://neon.com/docs/community/component-guide#steps) | Most commonly used components | | **[Tabbed Content](https://neon.com/docs/community/component-guide#tabbed-content)** | [CodeTabs](https://neon.com/docs/community/component-guide#codetabs), [Tabs](https://neon.com/docs/community/component-guide#tabs) | Organizing content into tabs | | **[Content organization](https://neon.com/docs/community/component-guide#content-organization)** | [TechCards](https://neon.com/docs/community/component-guide#techcards--detailiconcards), [DetailIconCards](https://neon.com/docs/community/component-guide#techcards--detailiconcards), [DefinitionList](https://neon.com/docs/community/component-guide#definitionlist), [DocsList](https://neon.com/docs/community/component-guide#docslist), [InfoBlock](https://neon.com/docs/community/component-guide#infoblock) | Structure and navigation | | **[Interactive elements](https://neon.com/docs/community/component-guide#interactive-elements)** | [CheckList](https://neon.com/docs/community/component-guide#checklist), [CheckItem](https://neon.com/docs/community/component-guide#checkitem), [CTA](https://neon.com/docs/community/component-guide#cta-call-to-action), [NeedHelp](https://neon.com/docs/community/component-guide#needhelp) | User engagement and interaction | | **[Common shared components](https://neon.com/docs/community/component-guide#common-shared-components)** | [LinkAPIKey](https://neon.com/docs/community/component-guide#linkapikey), [FeatureBetaProps](https://neon.com/docs/community/component-guide#featurebetaprops) | Reusable content and status indicators | For specialized components and specific use cases, see the [Component Specialized Guide](https://neon.com/docs/community/component-specialized). --- [Document source](https://neon.com/docs/community/component-icon-guide.md) --- # Component icon guide Complete reference for all icon systems and usage A comprehensive guide to all icon systems used in Neon documentation. This guide helps you understand which icon system to use and how to implement them correctly. **What you will learn:** - How to choose the right icon system for your use case - Which icons are available in each system - How to implement icons correctly in components - Best practices for icon usage and troubleshooting **Related topics** - [Component Guide](https://neon.com/docs/community/component-guide) - [Component Specialized Guide](https://neon.com/docs/community/component-specialized) - [Component Architecture](https://neon.com/docs/community/component-architecture) - [Documentation Contribution Guide](https://neon.com/docs/community/contribution-guide) ## Quick navigation - [Icon system overview](https://neon.com/docs/community/component-icon-guide#icon-system-overview) - Understanding the different icon systems - [TechCards Icons](https://neon.com/docs/community/component-icon-guide#techcards-icons) - Technology logos and frameworks - [DetailIconCards Icons](https://neon.com/docs/community/component-icon-guide#detailiconcards-icons) - Feature and service icons - [Icon usage guidelines](https://neon.com/docs/community/component-icon-guide#icon-usage-guidelines) - Best practices and conventions - [Icon Decision Tree](https://neon.com/docs/community/component-icon-guide#icon-decision-tree) - Which icon system to use when --- ## Icon system overview Neon documentation uses multiple icon systems for different components. Understanding which system to use is crucial for proper implementation. ### Available icon systems 1. **TechCards Icons** - Colorful technology logos and framework icons 2. **DetailIconCards Icons** - Monochrome feature and service icons 3. **DocsList Icons** - Simple navigation and action icons ### Component compatibility | Component | Icon System | Use Case | | ------------------- | --------------------- | ------------------------------ | | **TechCards** | TechCards Icons | Technology/framework showcases | | **DetailIconCards** | DetailIconCards Icons | Feature/service showcases | | **DocsList** | Built-in themes | Documentation links | ### Usage example ```mdx OpenAI Integration LangChain Integration ``` _For technical implementation details, see the [Icon Systems section](https://neon.com/docs/community/component-architecture#icon-systems) in the Component Architecture guide._ --- ## Icon usage guidelines ### Choosing the right icon system - **TechCards**: Use for technology and framework showcases - **DetailIconCards**: Use for feature and service showcases - **DocsList**: Use built-in themes for documentation links ### Icon naming conventions - **TechCards**: Use kebab-case (for example, `node-js`, `next-js`) - **DetailIconCards**: Use camelCase or kebab-case as defined in the mapping - **File Requirements**: Ensure SVG files exist in the correct directories ### Best practices 1. **Test your icons**: Always verify that your chosen icon works in the target component 2. **Check both systems**: If an icon doesn't work in one system, try the other 3. **Use descriptive names**: Choose icon names that clearly represent the technology or feature 4. **Document your choices**: Keep track of which icons work in which components 5. **Fallback gracefully**: Provide alternative text or descriptions when icons aren't available --- ### Component selection guide | Use Case | Component | Icon System | Example | | ------------------- | --------------- | --------------------- | ---------------------- | | Technology showcase | TechCards | TechCards Icons | Node.js, React, Python | | Feature showcase | DetailIconCards | DetailIconCards Icons | OpenAI, AWS, Database | | Documentation links | DocsList | Built-in themes | Guides, API docs | | Simple navigation | DocsList | Built-in themes | Related topics | --- ## Troubleshooting ### Common issues 1. **Icon not displaying**: Check that the icon name exists in the correct system 2. **Wrong icon system**: Verify you're using the right component for your icon 3. **Missing files**: Ensure SVG files exist in the correct directories 4. **Case sensitivity**: Icon names are case-sensitive ### Testing icons To test if an icon works: 1. **TechCards**: Check if `{icon}.svg` exists in `/public/images/technology-logos/` 2. **DetailIconCards**: Check if the icon is mapped in the component code 3. **DocsList**: Use the built-in themes (default, docs, repo) ### Getting help - Review the [Component Architecture](https://neon.com/docs/community/component-architecture) for technical details - Test icons in a development environment before using in production --- ## Complete icon showcase ### TechCards Icons (Technology Logos) #### Programming Languages & Frameworks - [Bun](https://neon.com/docs/community/component-icon-guide#): JavaScript runtime (icon: bun) - [Express](https://neon.com/docs/community/component-icon-guide#): Node.js web framework (icon: express) - [Hasura](https://neon.com/docs/community/component-icon-guide#): GraphQL API engine (icon: hasura) - [Hono](https://neon.com/docs/community/component-icon-guide#): Web framework (icon: hono) - [Java](https://neon.com/docs/community/component-icon-guide#): Programming language (icon: java) - [JavaScript](https://neon.com/docs/community/component-icon-guide#): Programming language (icon: javascript) - [Laravel](https://neon.com/docs/community/component-icon-guide#): PHP framework (icon: laravel) - [Nest.js](https://neon.com/docs/community/component-icon-guide#): Node.js framework (icon: nest-js) - [Next.js](https://neon.com/docs/community/component-icon-guide#): React framework (icon: next-js) - [Node.js](https://neon.com/docs/community/component-icon-guide#): JavaScript runtime (icon: node-js) - [Nuxt](https://neon.com/docs/community/component-icon-guide#): Vue framework (icon: nuxt) - [Phoenix](https://neon.com/docs/community/component-icon-guide#): Elixir framework (icon: phoenix) - [Python](https://neon.com/docs/community/component-icon-guide#): Programming language (icon: python) - [Quarkus](https://neon.com/docs/community/component-icon-guide#): Java framework (icon: quarkus) - [Rails](https://neon.com/docs/community/component-icon-guide#): Ruby framework (icon: rails) - [React](https://neon.com/docs/community/component-icon-guide#): JavaScript library (icon: react) - [Redwood SDK](https://neon.com/docs/community/component-icon-guide#): Full-stack framework (icon: redwoodsdk) - [Reflex](https://neon.com/docs/community/component-icon-guide#): Python framework (icon: reflex) - [Remix](https://neon.com/docs/community/component-icon-guide#): React framework (icon: remix) - [Ruby](https://neon.com/docs/community/component-icon-guide#): Programming language (icon: ruby) - [Rust](https://neon.com/docs/community/component-icon-guide#): Programming language (icon: rust) - [Solid](https://neon.com/docs/community/component-icon-guide#): JavaScript library (icon: solid) - [Svelte](https://neon.com/docs/community/component-icon-guide#): JavaScript framework (icon: svelte) - [Symfony](https://neon.com/docs/community/component-icon-guide#): PHP framework (icon: symfony) - [Vue](https://neon.com/docs/community/component-icon-guide#): JavaScript framework (icon: vue) #### Cloud & Infrastructure - [AWS S3 Bucket](https://neon.com/docs/community/component-icon-guide#): Cloud storage service (icon: aws-s3-bucket) - [Backblaze](https://neon.com/docs/community/component-icon-guide#): Cloud storage and backup (icon: backblaze) - [Heroku](https://neon.com/docs/community/component-icon-guide#): Cloud platform (icon: heroku) - [Koyeb](https://neon.com/docs/community/component-icon-guide#): Cloud platform (icon: koyeb) - [Netlify](https://neon.com/docs/community/component-icon-guide#): Web hosting platform (icon: netlify) - [Railway](https://neon.com/docs/community/component-icon-guide#): Deployment platform (icon: railway) - [Render](https://neon.com/docs/community/component-icon-guide#): Cloud platform (icon: render) - [Vercel](https://neon.com/docs/community/component-icon-guide#): Deployment platform (icon: vercel) #### Databases & Data - [Convex](https://neon.com/docs/community/component-icon-guide#): Backend platform (icon: convex) - [Entity](https://neon.com/docs/community/component-icon-guide#): Data modeling tool (icon: entity) - [Estuary](https://neon.com/docs/community/component-icon-guide#): Data streaming platform (icon: estuary) - [Exograph](https://neon.com/docs/community/component-icon-guide#): GraphQL framework (icon: exograph) - [Ferret](https://neon.com/docs/community/component-icon-guide#): Data processing tool (icon: ferret) - [Kafka](https://neon.com/docs/community/component-icon-guide#): Stream processing platform (icon: kafka) - [Knex](https://neon.com/docs/community/component-icon-guide#): SQL query builder (icon: knex) - [Liquibase](https://neon.com/docs/community/component-icon-guide#): Database migration tool (icon: liquibase) - [Materialize](https://neon.com/docs/community/component-icon-guide#): Streaming database (icon: materialize) - [Neon](https://neon.com/docs/community/component-icon-guide#): Serverless Postgres (icon: neon) - [Outerbase](https://neon.com/docs/community/component-icon-guide#): Database interface (icon: outerbase) - [Draxlr](https://neon.com/docs/community/component-icon-guide#): Database interface (icon: draxlr) - [Polyscale](https://neon.com/docs/community/component-icon-guide#): Database proxy (icon: polyscale) - [PostgreSQL](https://neon.com/docs/community/component-icon-guide#): Database system (icon: postgresql) - [Prisma](https://neon.com/docs/community/component-icon-guide#): ORM for Node.js (icon: prisma) - [Sequelize](https://neon.com/docs/community/component-icon-guide#): ORM for Node.js (icon: sequelize) - [Sequin](https://neon.com/docs/community/component-icon-guide#): Data synchronization (icon: sequin) - [Snowflake](https://neon.com/docs/community/component-icon-guide#): Data warehouse (icon: snowflake) - [SQLAlchemy](https://neon.com/docs/community/component-icon-guide#): Python ORM (icon: sqlalchemy) - [StepZen](https://neon.com/docs/community/component-icon-guide#): GraphQL platform (icon: stepzen) - [Supabase](https://neon.com/docs/community/component-icon-guide#): Backend platform (icon: supabase) - [TypeORM](https://neon.com/docs/community/component-icon-guide#): TypeScript ORM (icon: typeorm) - [Drizzle](https://neon.com/docs/community/component-icon-guide#): TypeScript ORM (icon: drizzle) #### Services & APIs - [Cloudinary](https://neon.com/docs/community/component-icon-guide#): Cloud media management (icon: cloudinary) - [ImageKit](https://neon.com/docs/community/component-icon-guide#): Image optimization (icon: imagekit) - [Inngest](https://neon.com/docs/community/component-icon-guide#): Background job platform (icon: inngest) - [Micronaut](https://neon.com/docs/community/component-icon-guide#): Java framework (icon: micronaut) - [Neo Tax](https://neon.com/docs/community/component-icon-guide#): Tax calculation service (icon: neo-tax) - [OAuth](https://neon.com/docs/community/component-icon-guide#): Authentication protocol (icon: oauth) - [Okta](https://neon.com/docs/community/component-icon-guide#): Identity platform (icon: okta) - [Uploadcare](https://neon.com/docs/community/component-icon-guide#): File handling service (icon: uploadcare) - [WunderGraph](https://neon.com/docs/community/component-icon-guide#): API development platform (icon: wundergraph) ### DetailIconCards Icons (Feature Icons) #### AI & Machine Learning - [LangChain](https://neon.com/docs/community/component-icon-guide#): AI framework (icon: langchain) - [LlamaIndex](https://neon.com/docs/community/component-icon-guide#): Data framework for LLMs (icon: llamaindex) - [Ollama](https://neon.com/docs/community/component-icon-guide#): Local LLM framework (icon: ollama) - [OpenAI](https://neon.com/docs/community/component-icon-guide#): AI platform (icon: openai) #### Development Tools - [Atom](https://neon.com/docs/community/component-icon-guide#): Code editor (icon: atom) - [Binary Code](https://neon.com/docs/community/component-icon-guide#): Programming and code (icon: binary-code) - [Bug](https://neon.com/docs/community/component-icon-guide#): Debugging and testing (icon: bug) - [CLI](https://neon.com/docs/community/component-icon-guide#): Command line interface (icon: cli) - [CLI Cursor](https://neon.com/docs/community/component-icon-guide#): Terminal cursor (icon: cli-cursor) - [Code](https://neon.com/docs/community/component-icon-guide#): Programming and development (icon: code) - [GitHub](https://neon.com/docs/community/component-icon-guide#): Code repository (icon: github) - [Hook](https://neon.com/docs/community/component-icon-guide#): Development hooks (icon: hook) - [Ladder](https://neon.com/docs/community/component-icon-guide#): Development progression (icon: ladder) - [Laptop](https://neon.com/docs/community/component-icon-guide#): Development machine (icon: laptop) - [Setup](https://neon.com/docs/community/component-icon-guide#): Configuration and setup (icon: setup) - [Wrench](https://neon.com/docs/community/component-icon-guide#): Tools and utilities (icon: wrench) #### Business & Analytics - [A Chart](https://neon.com/docs/community/component-icon-guide#): Analytics and charts (icon: a-chart) - [App Store](https://neon.com/docs/community/component-icon-guide#): Application marketplace (icon: app-store) - [Calendar Day](https://neon.com/docs/community/component-icon-guide#): Scheduling and time (icon: calendar-day) - [Cards](https://neon.com/docs/community/component-icon-guide#): Card-based interface (icon: cards) - [Check](https://neon.com/docs/community/component-icon-guide#): Verification and approval (icon: check) - [Cheque](https://neon.com/docs/community/component-icon-guide#): Payment processing (icon: cheque) - [Chart Bar](https://neon.com/docs/community/component-icon-guide#): Data visualization (icon: chart-bar) - [CSV](https://neon.com/docs/community/component-icon-guide#): Data format (icon: csv) - [Data](https://neon.com/docs/community/component-icon-guide#): Data and information (icon: data) - [Metrics](https://neon.com/docs/community/component-icon-guide#): Performance metrics (icon: metrics) - [Performance](https://neon.com/docs/community/component-icon-guide#): System performance (icon: performance) - [Queries](https://neon.com/docs/community/component-icon-guide#): Database queries (icon: queries) - [Research](https://neon.com/docs/community/component-icon-guide#): Development research (icon: research) - [Stopwatch](https://neon.com/docs/community/component-icon-guide#): Timing and performance (icon: stopwatch) - [Table](https://neon.com/docs/community/component-icon-guide#): Data tables (icon: table) - [Todo](https://neon.com/docs/community/component-icon-guide#): Task management (icon: todo) - [Transactions](https://neon.com/docs/community/component-icon-guide#): Database transactions (icon: transactions) - [Trend Up](https://neon.com/docs/community/component-icon-guide#): Growth and trends (icon: trend-up) #### UI & Design - [Audio Jack](https://neon.com/docs/community/component-icon-guide#): Audio connectivity (icon: audio-jack) - [Cards](https://neon.com/docs/community/component-icon-guide#): Card-based interface (icon: cards) - [Gamepad](https://neon.com/docs/community/component-icon-guide#): Gaming and interaction (icon: gamepad) - [GUI](https://neon.com/docs/community/component-icon-guide#): Graphical user interface (icon: gui) - [Screen](https://neon.com/docs/community/component-icon-guide#): Display and interface (icon: screen) - [Sparkle](https://neon.com/docs/community/component-icon-guide#): Visual effects (icon: sparkle) - [User](https://neon.com/docs/community/component-icon-guide#): User interface (icon: user) #### Infrastructure & Services - [Autoscaling](https://neon.com/docs/community/component-icon-guide#): Automatic scaling (icon: autoscaling) - [AWS](https://neon.com/docs/community/component-icon-guide#): Cloud computing platform (icon: aws) - [Branching](https://neon.com/docs/community/component-icon-guide#): Database branching (icon: branching) - [Database](https://neon.com/docs/community/component-icon-guide#): Database systems (icon: database) - [Discord](https://neon.com/docs/community/component-icon-guide#): Communication platform (icon: discord) - [Download](https://neon.com/docs/community/component-icon-guide#): File download (icon: download) - [Enable](https://neon.com/docs/community/component-icon-guide#): Feature enablement (icon: enable) - [Filter](https://neon.com/docs/community/component-icon-guide#): Data filtering (icon: filter) - [Find Replace](https://neon.com/docs/community/component-icon-guide#): Search and replace (icon: find-replace) - [Gear](https://neon.com/docs/community/component-icon-guide#): Settings and configuration (icon: gear) - [Globe](https://neon.com/docs/community/component-icon-guide#): Global connectivity (icon: globe) - [Handshake](https://neon.com/docs/community/component-icon-guide#): Partnerships and agreements (icon: handshake) - [Heroku](https://neon.com/docs/community/component-icon-guide#): Cloud platform (icon: heroku) - [Hourglass](https://neon.com/docs/community/component-icon-guide#): Time and waiting (icon: hourglass) - [Import](https://neon.com/docs/community/component-icon-guide#): Data import (icon: import) - [Invert](https://neon.com/docs/community/component-icon-guide#): Data transformation (icon: invert) - [Lock Landscape](https://neon.com/docs/community/component-icon-guide#): Security and access control (icon: lock-landscape) - [Neon](https://neon.com/docs/community/component-icon-guide#): Serverless Postgres (icon: neon) - [Network](https://neon.com/docs/community/component-icon-guide#): Network connectivity (icon: network) - [Postgres](https://neon.com/docs/community/component-icon-guide#): Database system (icon: postgres) - [Prisma](https://neon.com/docs/community/component-icon-guide#): ORM for Node.js (icon: prisma) - [Privacy](https://neon.com/docs/community/component-icon-guide#): Data privacy (icon: privacy) - [Puzzle](https://neon.com/docs/community/component-icon-guide#): Problem solving (icon: puzzle) - [Refresh](https://neon.com/docs/community/component-icon-guide#): Data refresh (icon: refresh) - [Respond Arrow](https://neon.com/docs/community/component-icon-guide#): Response and feedback (icon: respond-arrow) - [Scale Up](https://neon.com/docs/community/component-icon-guide#): Scaling and growth (icon: scale-up) - [Search](https://neon.com/docs/community/component-icon-guide#): Search functionality (icon: search) - [Split Branch](https://neon.com/docs/community/component-icon-guide#): Code branching (icon: split-branch) - [SQL](https://neon.com/docs/community/component-icon-guide#): Database queries (icon: sql) - [Unlock](https://neon.com/docs/community/component-icon-guide#): Access control (icon: unlock) - [Vercel](https://neon.com/docs/community/component-icon-guide#): Deployment platform (icon: vercel) - [Wallet](https://neon.com/docs/community/component-icon-guide#): Payment and billing (icon: wallet) - [Warning](https://neon.com/docs/community/component-icon-guide#): Alerts and warnings (icon: warning) - [X](https://neon.com/docs/community/component-icon-guide#): Close or cancel (icon: x) --- ## Summary This guide provides an overview of Neon's icon systems and usage guidelines. For complete icon listings and detailed examples, refer to the [Component Guide](https://neon.com/docs/community/component-guide). ### Key takeaways - **TechCards** uses colorful technology logos from `/public/images/technology-logos/` - **DetailIconCards** uses monochrome feature icons mapped in component code - **DocsList** uses built-in themes for simple navigation - Always test icons in the target component before using - Choose the right component based on your use case, not just the icon availability --- [Document source](https://neon.com/docs/community/component-specialized.md) --- # Component specialized guide Specialized and less common components for specific use cases A comprehensive reference for specialized and less commonly used MDX components in Neon documentation. This guide covers components used in specific scenarios, specialized workflows, and edge cases. **Note: Component status** Some components in this guide are currently unused or may be deprecated in future updates. These are included for reference but may be removed from the codebase. **What you will learn:** - How to use specialized MDX components for specific use cases - When to choose specialized components over common ones - Technical requirements and dependencies for specialized components - Best practices for complex component implementations **Related topics** - [Component Guide](https://neon.com/docs/community/component-guide) - [Component Icon Guide](https://neon.com/docs/community/component-icon-guide) - [Component Architecture](https://neon.com/docs/community/component-architecture) - [Documentation Contribution Guide](https://neon.com/docs/community/contribution-guide) ## Quick navigation - [Code display](https://neon.com/docs/community/component-specialized#code-display) - Enhanced code blocks and external code - [Media components](https://neon.com/docs/community/component-specialized#media-components) - Video and multimedia content - [Specialized shared components](https://neon.com/docs/community/component-specialized#specialized-shared-components) - Feature status indicators - [SDK components](https://neon.com/docs/community/component-specialized#sdk-components) - Auto-generated SDK documentation - [Utility components](https://neon.com/docs/community/component-specialized#utility-components) - Forms and specialized UI elements - [Specialized components](https://neon.com/docs/community/component-specialized#specialized-components) - Complex and specialized functionality - [Community components](https://neon.com/docs/community/component-specialized#community-components) - Community engagement features --- ## Code display Enhanced code blocks and external code embedding. ### ExternalCode Embed code from external sources or files. ```mdx ``` **Live preview:** _[External code loaded from GitHub README.md with syntax highlighting]_ Example of external code loading (mocked for showcase): ```markdown # Neon Database Serverless Postgres built for the cloud. ## Key Features - **Instant provisioning**: Create databases in seconds - **Autoscaling**: Scale compute up and down automatically - **Branching**: Create database branches like Git - **Scale to zero**: Save costs when inactive ## Quick Start 1. Sign up at console.neon.tech 2. Create your first project 3. Connect using your preferred client ``` --- ## Media components Components for embedding and displaying multimedia content. ### YoutubeIframe Embedded YouTube video player. ```mdx ``` **Live preview:** [Watch on YouTube](https://youtube.com/watch?v=IcoOpnAcO1Y) --- ### Video Native video player component. ```mdx