This article is published in English.
Bootstrapping a NestJS and Prisma API Without Relation or P1001 Errors
A practical checklist for wiring NestJS, Prisma and PostgreSQL into a clean API foundation, plus fixes for relation errors, P1001 and broken PRs.
Almost every backend feature a team builds later, from authentication to multi-tenancy and role-based access control, sits on top of the first few hours of project setup. If the environment variables, database connection, schema relations and migrations are wired sloppily at the start, every later pull request inherits the mess. This guide walks through bootstrapping a NestJS API backed by Prisma and PostgreSQL, explains the three problems that most often derail that first milestone, and gives you a checklist for knowing when the foundation is actually done.
What a finished bootstrap looks like
It helps to define the goal before touching the CLI. A bootstrap is complete when a reviewer can clone the branch and confirm all of the following:
- A NestJS application written in TypeScript that starts without errors
- Prisma connected to a PostgreSQL database
- Configuration loaded from environment variables rather than hard-coded values
- An initial set of data models that reflect the domain
- A migration that applies cleanly to an empty database
- A focused pull request that the rest of the team can review and merge
The constraints are deliberately narrow: NestJS and Prisma as the only framework and ORM, PostgreSQL as the database, and the team's existing conventions for configuration and Git.
The toolset
- Framework and language: NestJS with TypeScript
- Data access:
prisma(the CLI) and@prisma/client(the generated query client) - Configuration:
@nestjs/config - Database: a local PostgreSQL instance
- Verification: the Prisma CLI, plus a browser or API client for hitting endpoints
Setting up the project skeleton
Start by generating a fresh application with the Nest CLI, then add Prisma and initialize it inside the project. Initialization creates a prisma/ directory for the schema and migrations, while your application code stays in src/.
Next, create a .env file containing DATABASE_URL, the PostgreSQL connection string Prisma reads. Load configuration through the @nestjs/config module so the application picks up values from the environment instead of from literals scattered through the code. Make sure .env is listed in .gitignore; committing real credentials in the very first PR is an easy mistake and an awkward one to undo.
Before writing any models, confirm that Prisma can actually reach the database. If you want a deeper walkthrough of the Prisma side on its own, see setting up Prisma 7 with PostgreSQL in a TypeScript Node.js project, and check the current Prisma docs for version-specific details.
Modeling the first entities
For a multi-tenant product, a sensible starting schema has four models:
- Tenant, representing an organization using the system
- User, representing a person who signs in
- Role, for basic role assignment within a tenant
- Invite, for bringing new users into a tenant
Together they capture what later features depend on: users belong to tenants, hold roles, and arrive through invites. Every relation needs a field on both sides, which is the source of the first error below.
Once the schema validates, run the initial migration so the database structure matches the schema. Keep it free of experiments, because every teammate will apply it locally.
Adding a health endpoint
On the API side, add a single controller exposing /health that returns a plain OK. It looks trivial, but it serves a real purpose: it gives you, your CI pipeline and eventually your load balancer or orchestrator a cheap way to ask whether the process is up and serving requests.
Three errors that commonly block the first milestone
Prisma rejects a relation with no opposite field
Symptom: schema validation fails, complaining that a relation is missing its opposite field.
Cause: Prisma requires relations to be declared on both models. If User points to Tenant but Tenant has no field listing its users, the schema is incomplete from Prisma's point of view.
Fix: add the missing back-reference fields on the related models, then run prisma format. The formatter normalizes the file and can fill in missing relation fields for you, so it is a good habit to run it after every schema edit.
P1001: cannot reach the database server
Symptom: Prisma reports error code P1001 and cannot connect to PostgreSQL.
Cause: usually one of two things. Either the PostgreSQL server is not running, or the port in DATABASE_URL does not match the port the server is listening on.
Fix: confirm the database process is running locally, then compare the host and port in the connection string with the server's actual configuration.
A pull request that appears to delete everything
Symptom: a reviewer opens the PR and sees that every file in the repository has been deleted.
Cause: the commit was made from the wrong Git state, so the diff compares against something very different from what was intended.
Fix: rather than trying to repair the tangled history, create a fresh branch from the correct base and reapply only the intended changes. Running git status and reviewing git diff against the target branch before pushing catches this class of mistake early.
Verifying the setup
Verification should be boringly repeatable:
- Run
npx prisma migrate devand confirm the migration applies without errors - Start the NestJS server
- Open
/healthin a browser or API client and check for theOKresponse
When the migration completes cleanly and the health endpoint answers, the foundation is ready for the next feature.
Key takeaways
- Treat the bootstrap as a deliverable with explicit acceptance criteria, not as throwaway scaffolding.
- Declare every Prisma relation on both sides and let
prisma formatkeep the schema tidy. - When you see
P1001, check that PostgreSQL is running and that the port inDATABASE_URLis correct before debugging anything else. - A health endpoint costs minutes and pays off in CI, monitoring and deployment checks.
- Small, focused pull requests with clean history are part of the engineering work, not an afterthought.