We're removing seat fees and making pricing better for fast-growing teams

Learn more
Platform

Running Python, Go, Rust, and Ruby backends alongside a Next.js frontend

When this pattern makes sense

Sometimes the frontend and backend want different deployment environments. You might keep Next.js on Vercel, but run your backend in a language that better fits the rest of your system. That is common when your API depends on Python libraries, Go concurrency, Rust performance, or Rails conventions.

This setup also gives you a clear operational split. Vercel serves the frontend. Render runs the backend as a web service with its own health checks, environment variables, deploy lifecycle, and optional companion services such as background workers or private services.

This article focuses on the integration boundary between those two platforms. The pattern stays the same regardless of language:

  • Serve the Next.js frontend from Vercel
  • Expose a JSON API from Render
  • Pass the backend URL to the frontend with NEXT_PUBLIC_API_URL
  • Allow the frontend origin with CORS
  • Deploy the backend from a shared repo with Render Blueprints

Organize the repository

A monorepo is a practical default for this pattern. You can update the API and the frontend in one pull request, while still deploying them independently.

Use a layout similar to this:

In this arrangement, Vercel builds the frontend directory, and Render reads render.yaml from the repo root. In the Blueprint, rootDir points Render to the backend subdirectory you want to build.

Build the backend API

Regardless of language, the backend has the same job:

  • Bind to 0.0.0.0
  • Read the port from PORT
  • Expose a health endpoint
  • Return JSON from the application endpoint
  • Allow the Vercel origin with CORS

Use the snippets in this article as illustrations of the integration pattern.

They show the shape of a backend that works well with Next.js on Vercel and Render on the backend side. They are not complete production templates, and you should adapt dependency setup, auth, error handling, and CORS details to your framework and app.

Python with FastAPI

FastAPI works well when you want an async Python API with minimal boilerplate. This example is intentionally minimal and focuses on the integration points.

On Render, you typically start this service with uvicorn main:app --host 0.0.0.0 --port $PORT.

Go with net/http

Go's standard library is enough for a small API service. This example is intentionally minimal. The key details are handling the CORS preflight request and listening on PORT.

Render sets PORT for every web service, so the fallback exists only for local development.

Rust with Axum

Rust teams often want the same separation: Next.js on one side, a typed API on the other. This example uses Axum and follows the same CORS and health check pattern as the other runtimes. Treat it as a sketch of the service shape, not a complete Axum setup.

Render supports runtime: rust in render.yaml, so the deployment shape matches the Python, Go, and Ruby examples.

Ruby with Rails API mode

If your backend already lives in Rails, use API mode and keep the same public contract: one health endpoint, one application endpoint, and CORS driven by environment variables. This snippet shows the shape of the integration, not a full Rails app.

On Render, Rails typically runs with a bundle exec start command that binds to 0.0.0.0 and reads PORT.

Connect Next.js to the backend

On the frontend, the integration point is the backend base URL. In Next.js, environment variables prefixed with NEXT_PUBLIC_ are bundled for the browser, so NEXT_PUBLIC_API_URL is a good fit.

Set NEXT_PUBLIC_API_URL to the local URL your backend uses during development (for example, http://localhost:10000) and to your Render service URL in Vercel for production. Because Next.js inlines NEXT_PUBLIC_ values at build time, redeploy the frontend after you change it.

Deploy the backend with Render Blueprints

Render Blueprints let you define the backend service in render.yaml and keep the deployment config in the same repo as the app.

Here is an illustrative Blueprint for the FastAPI example:

The same structure works for the other runtimes:

  • Use runtime: go for Go
  • Use runtime: rust for Rust
  • Use runtime: ruby for Rails
  • Keep rootDir pointed at the backend directory
  • Set ALLOWED_ORIGINS to the exact Vercel origin you want to allow

When you sync the Blueprint in the Render Dashboard, Render creates the defined service and redeploys it when you push changes to the linked branch.

Handle the operational details

This pattern stays manageable when you keep a few operational rules consistent.

Keep CORS explicit

Set ALLOWED_ORIGINS to the exact frontend origin, including https://. Avoid * for authenticated APIs, and make sure your backend handles OPTIONS requests for browser preflights.

Treat health checks as part of the contract

Give the service a lightweight endpoint such as /api/health, then set healthCheckPath in the Blueprint or in the Render Dashboard. Render uses this endpoint during zero-downtime deploys and for ongoing health checks.

Keep public and private configuration separate

Use NEXT_PUBLIC_API_URL only for the backend base URL that the browser needs. Keep secrets, tokens, and database credentials in non-public environment variables on Render.

Add companion services when the backend grows

If the same backend also needs async jobs, add a background worker. If it needs an internal-only API for other Render services, add a private service. You do not need to move the frontend off Vercel to use either service type.

Frequently asked questions