Getting Started
Prerequisites before you begin:
- Java 17 or 21 installed
- Node.js 18 or higher installed
- PostgreSQL 15 running locally
- Maven 3.9 (or use the included
./mvnwwrapper)
Step 1 — Download and unzip
Download your generated ZIP from zovlyn.dev. Unzip it into your projects folder. You will see three things at the root: backend/, frontend/, and AGENTS.md. If you enabled Docker, docker-compose.yml is also there.
Step 2 — Create your database
This is required. The backend will refuse to start if the database does not exist. Run this once in your terminal before anything else:
psql -U postgres -c "CREATE DATABASE your_project_db;"Replace your_project_db with the name you entered during generation. Replace postgres with your local PostgreSQL username if it is different.
Not sure what your username is? Run:
psql -c "\du"The database name is also shown in backend/AGENTS.md and backend/src/main/resources/application-local.yml inside your downloaded ZIP.
Step 3 — Start the backend
cd backend
./mvnw spring-boot:runWait for: "Started Application in X seconds". Verify: GET http://localhost:8080/api/health returns OK.
Step 4 — Start the frontend
cd frontend
npm install
npm run devOpen http://localhost:3000 in your browser.
Step 5 — Verify the connection
The home page will confirm the backend is reachable. If you see any errors, check the troubleshooting section in the FAQ below.
Project Structure
Your generated project follows a strict separation of concerns. Every layer has one job and one job only.
Root level
AGENTS.md— Context file for AI coding agentsstack.md— Full technical referenceREADME.md— Setup instructionsdocker-compose.yml— Docker setup (if enabled)
backend/
src/main/java/your.package/
config/ # Spring configuration classes
controller/ # REST controllers — HTTP layer only
service/ # Business logic — no HTTP concerns
repository/ # JPA interfaces — data access only
model/ # JPA entity classes
dto/
request/ # Incoming request body classes
response/ # Outgoing response body classes
exception/ # Global exception handlersfrontend/
app/ # Next.js App Router pages and routes
api/ # Server-side proxy routes to backend
components/ # Reusable UI and layout components
hooks/ # Custom React hooks
lib/
api/ # All backend call functions live here
utils/ # Pure utility functions
types/ # TypeScript interfacesThe rule: never put business logic in a controller. Never call fetch() directly inside a React component. Always go through lib/api/ functions.
Running Locally
Running locally means PostgreSQL runs on your machine and both services run as normal processes. No Docker needed.
# Start the backend
cd backend
./mvnw spring-boot:run
# Start the frontend
cd frontend
npm install
npm run devThe frontend runs on port 3000. It calls the backend through Next.js API routes — never directly. This is why there are no CORS issues.
Running with Docker
Docker mode runs the backend and PostgreSQL as containers. The frontend still runs locally with npm run dev for hot reload during development.
Important: with Docker you do NOT need to create the database manually. Docker Compose creates it automatically using the POSTGRES_DB environment variable. Just run:
docker compose up --buildIf port 8080 is already in use on your machine, stop any running Spring Boot processes before running Docker. Run: lsof -i :8080 to find what is using it, then kill that process.
PostgreSQL will run on port 5433 to avoid conflicts with local instances. The backend will be available at http://localhost:8080.
Profile System
The backend uses Spring Profiles to switch database configuration without touching any Java code. The active profile is set in backend/src/main/resources/application.yml.
local: Connects to localhost:5432.docker: Connects to the postgres container.supabase: Connects to a hosted Supabase instance.
CORS Configuration
Zovlyn uses the Next.js proxy pattern to eliminate CORS. The browser calls Next.js at http://localhost:3000/api/*, which forwards calls to Spring Boot server-to-server.
Never call fetch('http://localhost:8080/...') anywhere in the frontend — it will break in production and cause CORS errors in development.
Environment Variables
Frontend (.env.local)
BACKEND_URL=http://localhost:8080
NEXT_PUBLIC_API_URL=http://localhost:8080Backend
Variables like SPRING_DATASOURCE_URL are typically handled via profiles or environment variables in production.
Adding a Feature
Backend order: Entity → Repository → DTOs → Service → Controller.
Frontend order: Type → Proxy route → API function → Component.
// Example Task Entity
@Entity
@Table(name = "tasks")
public class Task {
@Id @GeneratedValue
private Long id;
private String name;
private boolean completed;
}FAQ
Why does the backend fail with database does not exist?
You need to create the PostgreSQL database manually before running locally. Run: psql -U postgres -c 'CREATE DATABASE your_db;' replacing your_db with your project name. This is a one-time setup step. With Docker this is handled automatically.
Why does Docker fail with port already in use?
Something else is already running on port 8080 — usually a Spring Boot process you did not stop. Find it with: lsof -i :8080. Then kill it with: kill -9 <PID>. Then run docker compose up --build again.
Why does the frontend never call the backend directly?
Because of CORS. Browsers block requests from one origin (port 3000) to another (port 8080) by default. The Next.js proxy pattern routes all backend calls through the Next.js server, which has no CORS restrictions. This also means your backend URL never appears in the browser — it stays server-side only.
What are AGENTS.md files?
Markdown files written specifically for AI coding agents like Claude Code, Cursor, and Copilot. They explain the project architecture, naming conventions, how to add features, what profiles exist, and what each folder is for. When an AI agent opens your project it reads these files first and immediately understands how to help you — without you re-explaining anything.
Why are there three AGENTS.md files?
One at the root explains the overall system and how the two services connect. One in backend/ explains Spring Boot specifics. One in frontend/ explains Next.js specifics. Each AI agent reads the file closest to where it is working, so context is always precise and relevant.
Why Java 17 or 21 and not the latest?
17 and 21 are LTS (Long Term Support) releases. They receive security patches for years and are stable for production. Non-LTS releases are short-lived and not recommended for production projects. Always pick an LTS version.
Why does Docker use port 5433 for PostgreSQL?
Because your local machine likely already runs PostgreSQL on port 5432. Using 5433 for the Docker container avoids the conflict. Inside Docker the containers still talk to each other on port 5432 — only the host-facing port is 5433.
Why no Spring Security in v1?
Spring Security locks all endpoints by default. To keep the starter template "run out of the box" simple, it is intentionally left out for you to add once ready.
Can I add Spring Security myself?
Yes. Add spring-boot-starter-security to pom.xml and create a SecurityConfig class. Modern Spring uses the SecurityFilterChain bean approach. Your AGENTS.md files will help an AI agent add this correctly.
Why does the generated project use constructor injection?
Constructor injection is the recommended approach in modern Spring. It makes dependencies explicit, makes testing easier, and avoids null injection issues. Never use @Autowired field injection in new Spring Boot code. Lombok's @RequiredArgsConstructor generates the constructor automatically.
Can I rename the project after generating?
Yes, but it involves several steps across Java package names, pom.xml, package.json, and database configurations. It is generally easier to generate a fresh project with the correct name.
What is stack.md?
A comprehensive technical reference for the generated project. It lists every technology and version, the full folder structure with explanations, and common commands. Read it before starting development.
The backend starts but the frontend shows connection error.
Check that BACKEND_URL in .env.local points to http://localhost:8080. Ensure the backend actually started (look for "Started Application"). Try hitting /api/health directly in your browser.
Can I deploy this to production?
Yes. Deploy the backend to Railway and the frontend to Vercel. Set BACKEND_URL in Vercel to your Railway URL. Update the CORS allowed origins in CorsConfig.java to include your production URL.