---
title: How to deploy a Python backend with Vercel Services
description: Deploy a Python backend with a Next.js frontend on Vercel. Learn how to configure both apps with Vercel Services, use a separate database for each environment, and review changes at a Preview URL.
url: "https://vercel.com/kb/guide/python-backend-on-vercel"
published: 2026-10-14
last_updated: 2026-10-15
authors: Anshuman Bhardwaj
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

Use [Vercel Services](https://vercel.com/docs/services) to deploy a Python backend and a Next.js frontend together in one project. In this bookstore template, Python owns the data and application logic while Next.js renders the interface and calls the API privately.

Separate databases support local work and preview testing. Shared request IDs let you follow calls across both apps.

Deploy the template now, or follow the steps below to learn how it works.

**Python + Next.js Starter**

Bookstore with a FastAPI backend, reader accounts, saved bookshelves, and an administrator catalog editor, deployed together with Vercel Services.

[Deploy now](https://vercel.link/python-nextjs-starter)

If you deployed with the button above, connect its Neon database only to Production before continuing. Create separate databases for Development and Preview, and reuse the existing Production database.

## Quick start with an AI coding agent

### Agent prompt

```txt
Set up a working copy of https://github.com/vercel-labs/python-nextjs-starter using Vercel CLI, with Next.js and FastAPI deployed as one Services project.

First confirm my local directory, GitHub repository destination/visibility, Vercel team/project name, and Neon region/plan. Do not guess the team or incur unapproved charges. Check repository access and CLI authentication; if access is missing, ask me to authorize it rather than changing upstream visibility. Create my own template copy and clone it. Read README.md, AGENTS.md, and ARCHITECTURE.md; CLAUDE.md only points to AGENTS.md. Follow their setup commands and installed framework documentation. Check Git, Node.js 22, Python 3.12–3.14, uv, and Vercel CLI 62.1 or newer. Continue independent setup while waiting for preferences or required integration authorization.

Link a new Vercel project from the repository root. Preserve vercel.json: frontend/ is the public Next.js service, backend/ is private FastAPI, and BACKEND_URL comes from their service binding. Keep authentication, database access, and authorization in Python. Provision three independent Neon resources with my chosen region/plan, using explicit --environment development, preview, or production, --metadata auth=false, and --no-env-pull. Confirm each resource is scoped only to its intended environment before using it.

Have me choose an ADMIN_EMAIL and a distinct ADMIN_PASSWORD of 15–128 characters for each environment, entered through CLI prompts or a private ignored file, not pasted into chat or hardcoded. Set them using the README commands: Development values may be readable for local setup; Preview and Production passwords must be Secrets. ADMIN_NAME is optional. Never print credentials, put secrets in command arguments, or commit environment files/project linkage. Pull only Development into root .env.local; restore any retained Development Secrets there securely. Do not pull Preview/Production credentials locally.

Install locked dependencies, run scripts.deploy_setup with the Development environment file, then start both services from the root with vercel dev -L. Verify schema, the 12-book catalog, and admin login/Manage shop. Bootstrap must create the configured admin once and preserve existing credentials, roles, and catalog edits/deletions on reruns. Do not grant admin through public signup or bypass a reader-email collision. Never reset an existing database silently.

Run npm run check, npm run build, and the documented backend tests. Exercise search/filter URLs, registration/login/logout, reader ownership and wrong-role rejection, bookmark save/remove/persistence and cross-tab sync, and admin CRUD using disposable Development records. Preserve public Cache Components/PPR, uncached streamed session controls, and post-write cache invalidation. Verify a browser x-request-id matches evlog/structlog events in both services without logging private data. Remove only records created for these checks.

Deploy both services to Preview and verify its admin, database isolation, CRUD/bookmarks, and production-build navigation/cache freshness. Keep Deployment Protection enabled and use authenticated checks. Enable Git previews for my own repository if authorized; do not connect or modify the upstream repository. Stop after a verified Preview unless I explicitly authorize Production. If authorized, deploy the reviewed version and run safe Production smoke checks. Report local/Preview/Production URLs, checks passed or failed, which databases and admins each environment uses, and any remaining human steps, without secrets.
```

## Project structure

### Two apps in one repository

The frontend and backend have separate directories, dependency files, and framework configuration. The root `vercel.json` defines builds and request routing.

```text
python-nextjs-starter/
├── frontend/
│   ├── app/page.tsx                  # Next.js home route
│   ├── app/api/backend/[...path]/route.ts
│   ├── lib/observability.ts          # Logged calls to Python
│   └── package.json
├── backend/
│   ├── app/main.py                   # FastAPI application
│   ├── migrations/                   # Versioned database schema
│   ├── pyproject.toml
│   └── uv.lock
└── vercel.json                       # Both services and public routing
```

`frontend/` and `backend/` are the service roots. The root configuration connects both apps within a single Vercel project.

### Configure both services in `vercel.json`

The template’s `vercel.json` declares the service roots, frameworks, Python entry point, and binding from Next.js to Python:

```json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "services": {
    "backend": {
      "root": "backend/",
      "framework": "fastapi",
      "entrypoint": "app.main:app"
    },
    "frontend": {
      "root": "frontend/",
      "framework": "nextjs",
      "bindings": [
        {
          "type": "service",
          "service": "backend",
          "format": "url",
          "env": "BACKEND_URL"
        }
      ]
    }
  },
  "rewrites": [
    { "source": "/(.*)", "destination": { "service": "frontend" } }
  ]
}
```

The Python entry point `app.main:app` tells Vercel to import the `app` object from `app/main.py`, relative to `backend/`. FastAPI routes attach to that object.

`backend/app/main.py` creates the FastAPI application:

```python
from fastapi import Depends, FastAPI, HTTPException, Request, Response

app = FastAPI(title="Margin Bookstore API", version="0.1.0")
```

At runtime, the binding supplies the backend’s internal URL as `BACKEND_URL` to Next.js server code. Vercel generates the URL for each deployment, and the rewrite sends public requests to the frontend.

### Private and public services

On Vercel, a service stays private unless a top-level routing rule exposes it. Other services can call it internally through a [service binding](https://vercel.com/docs/services/bindings).

Browser actions call the same-origin `/api/backend/*` Next.js Route Handler, which forwards allowlisted requests to FastAPI. Python validates sessions and permissions for protected operations. Because the browser calls Next.js on the app's origin, it does not need a separate CORS configuration for Python.

Keep Python private for business logic, database access, or an inference API used by your frontend.

Expose it when mobile apps, external clients, or webhook senders need direct access. Add a [routing rule](https://vercel.com/docs/services/routing) targeting Python before the catch-all rule. Your app must still check authentication, permissions, and webhook signatures.

## Shared backend and frontend project

### Review changes across both apps

Each [preview deployment](https://vercel.com/docs/deployments/environments) includes both services from the same project version. Its frontend connects to its Python backend, so you can review the UI and the corresponding API at a single URL.

For example, add a field to the Python book response and display it on the Next.js detail page in the same branch. In a Git-connected project, a push to a non-production branch creates a preview where reviewers can check the field, sign in, and save a book.

Deploying both apps together does not isolate their database. If previews share a database, a schema change can also affect older deployments that use it.

### Configure the database for each environment

Connect a database resource to each of Vercel's [Development, Preview, and Production environments](https://vercel.com/docs/deployments/environments) in your project's **Storage** settings. Python reads the connection variables, including `DATABASE_URL`, supplied for that environment.

For this template, use three distinct databases:

| Environment | Database purpose                          | Benefit                                             |
| ----------- | ----------------------------------------- | --------------------------------------------------- |
| Development | Local accounts, test data, and migrations | Experiment without changing review or live data     |
| Preview     | Review catalog, accounts, and bookmarks   | Test features without changing live data            |
| Production  | Live catalog and account records          | Keep live data separate from development and review |

All Preview deployments in this setup share one database. To isolate each pull request, use [Neon branches](https://neon.com/docs/introduction/branching) with Preview credentials scoped to its Git branch.

## Full-stack observability

### Follow requests across Next.js and Python

The template uses [evlog](https://www.evlog.dev/start/quick-start) in Next.js and [structlog](https://www.structlog.org/en/stable/contextvars.html) in FastAPI. Their JSON events appear in [Vercel Runtime Logs](https://vercel.com/docs/logs/runtime) without a separate logging resource or API key.

The Next.js bridge creates a `requestId`, forwards it to Python, and returns it in the browser response’s `x-request-id` header. Each Python call also receives a `callId` to distinguish calls that share a request ID.

| Field                                     | Meaning                                         |
| ----------------------------------------- | ----------------------------------------------- |
| `requestId`                               | Correlates frontend and Python events           |
| `callId`                                  | Identifies an individual call to Python         |
| `service`, `environment`, `deploymentId`  | Identifies the app, environment, and deployment |
| `method`, `route`, `status`, `durationMs` | Records the request, HTTP status, and duration  |

If saving a book fails in the preview environment:

1. In your browser's Network panel, select the `/api/backend/shelf/...` request.
   
2. Copy `x-request-id` from the response headers.
   
3. Search the deployment's **Logs** in Vercel for that UUID across both services.
   
4. Compare the events. If only the bridge logs a `403`, the request may have failed its Origin check. Python returns `401` for authentication failures; a Python `500` followed by a bridge `503` points to a backend failure.
   

Filter logs to the environment and deployment where the request failed. The template’s `requestId` and `callId` are application identifiers, separate from Vercel’s platform request IDs.

Python duration covers the ASGI response lifecycle. The frontend’s dependency duration ends when response headers arrive.

The template applies these caching rules and limits what each log event contains:

- Cached catalog reads do not call Python or produce Python request events.
  
- Cache misses receive their own operation IDs. Streamed bookmark reads share an operation ID across parallel account and bookshelf calls.
  
- Visitor identifiers never enter public cache keys.
  
- Application logs exclude bodies, cookies, credentials, search strings, arbitrary paths, and raw exception messages.
  

For a latency waterfall of parent and child spans, add OpenTelemetry to both runtimes and propagate `traceparent`. The template has no tracing setup.

### Understand usage with custom analytics events

[Vercel Web Analytics custom events](https://vercel.com/docs/analytics/custom-events) show how often readers save books and which part of the UI they use. Track after a successful save to measure completion, or on the initial click to measure an attempt.

The template includes `@vercel/analytics`, the `<Analytics />` component, and bookmark event tracking. Enable Web Analytics in your project’s Vercel Dashboard to collect events.

> Custom events require a Pro or Enterprise plan.

This helper records successful bookmark saves and removals:

```typescript
"use client";

import { track } from "@vercel/analytics";

export type BookmarkSource = "detail" | "bookshelf";

export function trackBookmark(
  action: "saved" | "removed",
  source: BookmarkSource,
) {
  if (process.env.NEXT_PUBLIC_VERCEL_ENV !== "production") return;

  try {
    track(
      action === "saved" ? "Bookmark saved" : "Bookmark removed",
      { source },
    );
  } catch {
    // Analytics must never interrupt a confirmed database operation.
  }
}
```

The template exposes the Vercel deployment environment as `NEXT_PUBLIC_VERCEL_ENV` through `frontend/next.config.ts`. This limits event tracking to Production deployments.

Keep event properties small and non-sensitive, such as the UI surface. Use runtime logs to debug requests, and keep request identifiers out of product analytics. Do not send account emails, passwords, session tokens, or search text.

## Run locally and deploy to Vercel

Run the bookstore locally with Development data, then verify the same workflows in a preview deployment.

### Step 1. Prepare your local environment

Before you begin, make sure you have:

- Git
  
- Node.js 22 with npm
  
- Python 3.12–3.14 with [uv](https://docs.astral.sh/uv/getting-started/installation/)
  
- A [Vercel account](https://vercel.com/signup)
  
- Vercel CLI installed (`npm i -g vercel`)
  

Clone the repository, install its locked dependencies, and link the Vercel project. Keep **Root Directory** at the repository root so Vercel finds both services:

```bash
git clone https://github.com/vercel-labs/python-nextjs-starter.git
cd python-nextjs-starter
npm ci --prefix frontend
cd backend
uv sync --extra dev --locked
cd ..
vercel link
```

### Step 2. Configure databases and administrators

From the linked repository root, use [`vercel integration`](https://vercel.com/docs/cli/integration) to provision a Neon resource for each environment.

> This is a one-time project setup: later deployments reuse these resources.

Run these commands in an interactive terminal, choose a plan and region when prompted, and use resource names unique to your project:

```bash
vercel integration add neon --name bookstore-development --metadata auth=false --environment development --no-env-pull
vercel integration add neon --name bookstore-preview --metadata auth=false --environment preview --no-env-pull
vercel integration add neon --name bookstore-production --metadata auth=false --environment production --no-env-pull
```

Complete any integration authorization prompts. Choose capacity, backups, and recovery settings appropriate to your Preview and Production workloads.

These flags control provisioning:

- `--environment` limits the connection to the specified environment. Without it, the resource connects to all three: Development, Preview, and Production.
  
- `--no-env-pull` skips the automatic environment-variable pull after provisioning.
  
- `--metadata auth=false` disables Neon Auth because the template handles authentication in Python.
  

Neon supplies pooled `DATABASE_URL` for runtime queries and direct `DATABASE_URL_UNPOOLED` for setup. The binding sets `BACKEND_URL`.

#### Add administrator credentials

Public signup creates reader accounts. To edit the catalog, configure an administrator for each environment before its first database setup. Use a valid email and a different password of 15–128 characters in each environment:

```bash
vercel env add ADMIN_EMAIL development --no-sensitive
vercel env add ADMIN_PASSWORD development --no-sensitive

vercel env add ADMIN_EMAIL preview --no-sensitive
vercel env add ADMIN_PASSWORD preview --sensitive

vercel env add ADMIN_EMAIL production --no-sensitive
vercel env add ADMIN_PASSWORD production --sensitive
```

Administrator initialization runs once per database. Changing `ADMIN_EMAIL` or `ADMIN_PASSWORD` afterward does not update the stored account.

Enter each value at the CLI prompt. These commands store Development values as Config variables you can read and pull locally.

Preview and Production passwords are stored as Secrets. Backend builds can use them, but `vercel env pull` cannot retrieve their values.

To set an administrator name, run `vercel env add ADMIN_NAME development --no-sensitive`. Repeat with `preview` or `production` for the other environments. If omitted, the name defaults to `Bookshop owner`.

#### Prepare the Development database

Initialize the Development database before its first local run because `vercel dev` starts the services without running Python's deployment build hook.

Pull Development variables into the ignored `.env.local`, then run setup:

```bash
vercel env pull .env.local --environment development
cd backend
uv run --env-file ../.env.local python -m scripts.deploy_setup
cd ..
```

The command applies pending migrations, seeds the demo catalog, and creates the configured administrator with a hashed password. Later runs preserve catalog edits and administrator credentials.

Rerun setup when migrations change or you replace or reset the Development database. You do not need to run it before every development session. Pull Development variables again whenever that environment’s configuration changes.

Keep `.env.local` private and limited to Development credentials. Preview and Production database setup runs during the backend builds in step 4, **Deploy to Preview, then Production**. You do not need to pull their secrets locally.

### Step 3. Run the app

Start both services from the repository root for each local development session:

```bash
vercel dev -L
```

The CLI starts Next.js and FastAPI using the Development variables in `.env.local` and injects `BACKEND_URL`. Open the URL it prints.

Verify the app with these checks:

- Search the catalog and open a book.
  
- Register a reader with a password of 15–128 characters. Save and remove a book, then sign out and back in to check persistence.
  
- Sign in at `/account` with the Development administrator credentials and select **Manage shop**. Add, edit, and delete a disposable book. Check the catalog and detail page after each change.
  
- Copy `x-request-id` from an `/api/backend/*` response in your browser’s Network panel and find the matching events in both local service logs.
  

Before deploying, stop the server with `Ctrl+C` and run these checks from the repository root. They check types and linting, build the frontend, and test backend behavior. Vercel CLI does not run the full set during deployment:

```bash
npm run check
npm run build
cd backend
uv run --extra dev pytest -q
cd ..
```

The API tests use temporary SQLite databases and leave Neon unchanged.

> Next.js disables `<Link>` prefetching in development mode. Check navigation and cache freshness in the preview deployment.

### Step 4. Deploy to Preview, then Production

Verify caching, navigation, and environment configuration in Preview before deploying to Production.

```text
[tool.vercel.scripts]
build = "python -m scripts.deploy_setup"
```

The hook runs on every Preview and Production build. It applies pending migrations with `alembic upgrade head` and uses an advisory lock over a direct database connection, so setup runs one at a time.

Separate bootstrap markers record catalog seeding and administrator creation. The first build prepares a fresh database; later builds apply pending migrations without overwriting catalog edits or account changes. If setup fails, the backend build fails.

Before deploying, check that migrations remain compatible with older deployments: the build changes the database before the new deployment serves traffic. Then deploy to Preview and test its database connection:

```bash
vercel deploy
vercel curl /api/backend/health --deployment YOUR_PREVIEW_URL
```

Replace `YOUR_PREVIEW_URL` with the deployment URL. The health response should report `"database": "connected"`.

Keep [Deployment Protection](https://vercel.com/docs/deployment-protection) enabled. [`vercel curl`](https://vercel.com/docs/cli/curl) handles protected CLI requests; browser reviewers must pass the project's protection checks.

Before accepting real users, complete the [starter's security requirements for user accounts](https://github.com/vercel-labs/python-nextjs-starter/blob/6605a486b9ad4d0b0ca93042c1f6ed14ae07a2c1/ARCHITECTURE.md#ui-and-extension-boundaries), including email verification, password recovery, and administrator MFA.

After testing Preview, deploy the same source with Production configuration:

```bash
vercel deploy --prod
vercel curl /api/backend/health
```

Without `--deployment`, this command checks the linked project's latest Production deployment. Open its URL and sign in as the Production administrator.

For a failed check, follow **Troubleshooting** using its environment and request ID.

## Troubleshooting

### Only the frontend builds

**Cause:** The project is configured as a standalone Next.js app or its Root Directory points at `frontend/`.

**Fix:** Set the project’s Root Directory to the repository root and select the Services framework preset. For CLI setup, run `vercel project update --framework services`. Confirm that the root `vercel.json` declares both services.

### The API returns 503

**Cause:** The bridge cannot reach Python, or Python returns a server error. Missing bindings, database failures, and request timeouts can produce this response.

**Fix:** Copy the response’s `x-request-id` and check the logs for both services in that deployment. Verify that the database variables point to the intended environment’s database and that its migrations have run. Let the service binding supply `BACKEND_URL`; do not set it manually.

### Preview changes affect live data

**Cause:** Preview and Production connect to the same database.

**Fix:** Connect one database to Preview and a different database to Production. Verify that each is connected only to its intended environment, then redeploy. Refresh Development variables before local work.

Add disposable Preview records and confirm they stay out of Production.

### A local account cannot sign in to Preview

**Cause:** Accounts are stored in each environment’s database.

**Fix:** Register a reader in Preview or sign in with its configured administrator credentials. If the administrator is missing, check the Preview variables and backend setup logs, then redeploy. Development accounts and sessions do not carry over to Preview.

### Catalog editing returns 403

**Cause:** The account is a reader, or the write request comes from a different origin.

**Fix:** Sign in with an administrator account from the same environment and use the app’s current URL. Python checks the role on every catalog change, and the bridge validates the request Origin.

### Custom analytics events do not appear

**Cause:** Web Analytics is disabled, the plan does not support custom events, the app is running outside Production, or the browser blocks collection.

**Fix:** Enable Web Analytics in the Vercel Dashboard and confirm that your plan supports [custom events](https://vercel.com/docs/analytics/custom-events). Verify that the event runs after a successful action in Production. Use runtime logs to check whether the backend operation succeeded.

## Next steps

- Read [Full-stack previews on Vercel](https://vercel.com/kb/guide/full-stack-preview-deployments-on-vercel) to review changes across multiple services from a single preview URL.
  

- See [Vercel Services](https://vercel.com/docs/services) for deployment and [service bindings](https://vercel.com/docs/services/bindings) for private calls.
  
- Explore [FastAPI on Vercel](https://vercel.com/docs/frameworks/backend/fastapi) to deploy other Python APIs.
  
- Learn about [Preview environments](https://vercel.com/docs/deployments/environments) and [Git deployments](https://vercel.com/docs/git).
  
- Add [Neon database branching](https://neon.com/docs/introduction/branching) when each review needs its own data.
  

## FAQ

### Can you deploy a Python backend on Vercel?

Yes. Vercel supports Python frameworks such as FastAPI and Flask. Use Vercel Services to deploy a Python backend and frontend within a single project, with separate service roots and dependencies. This starter template uses FastAPI with Neon Postgres.

### How do you deploy Next.js and FastAPI together on Vercel?

Define Next.js and FastAPI as [Vercel Services](https://vercel.com/docs/services) in the root `vercel.json`. Set a root directory for each service and an entry point for Python, select the Services framework preset, and deploy from the repository root. Both apps ship in one deployment.

### How does a Next.js frontend connect to a Python backend on Vercel?

Add a service binding from Next.js to Python. Vercel injects the backend’s internal URL into a server-side variable such as `BACKEND_URL`. Next.js server code calls that URL, while browser actions use the starter’s same-origin API bridge. Python stays private unless a public routing rule exposes it.

### Do Vercel preview deployments include the Python backend?

Yes. Each Vercel Services preview deployment includes both the Next.js frontend and Python backend. Its binding connects the frontend to the backend in that deployment, so you can test both at one preview URL.

### How can you test a Python API in Preview without changing Production data?

Connect a separate Neon database only to the Preview environment. The starter applies Alembic migrations during its backend build, then reads and writes Preview data without changing the Production database. To give each pull request its own data, add per-PR Neon branches and Preview credentials scoped to each Git branch.

### Where should authentication and database logic live in a Next.js and Python app?

In this starter, Python handles database queries, password verification, sessions, and authorization. Next.js renders the UI and forwards account requests through a server-side bridge.

Public catalog data can be cached. Account and bookmark reads stay uncached, and their controls stream without blocking the public content.

## More Vercel Services guides

- [Migrate a Python API from Railway to Vercel](/kb/guide/migrate-a-python-api-from-railway-to-vercel): Move a FastAPI, Flask, or Django API from Railway to Vercel Functions. Replace railway.json start commands, volumes, Celery workers, and cron services.
- [Migrate a Python API from Render to Vercel](/kb/guide/migrate-a-python-api-from-render-to-vercel): Move a FastAPI, Flask, or Django API from Render to Vercel Functions. Translate render.yaml and replace persistent disks, background workers, and cron jobs.
- [Migrate a Python API from Heroku to Vercel](/kb/guide/migrate-a-python-api-from-heroku-to-vercel): Move a FastAPI, Flask, or Django API from Heroku to Vercel Functions. Replace your Procfile, worker dynos, Heroku Scheduler jobs, and Heroku Postgres.