Part 0.6 — Preparing for Part 1: Project Structure and Mindset
You now have the fundamentals: a configured environment, a Git workflow, and a grasp of how the web works.
Before jumping into Part 1, we’ll focus on something often skipped but absolutely crucial — how to structure your full-stack project and how to think like a full-stack engineer.
1. The Mindset of a Professional Full-Stack Developer
Full-stack work isn’t about doing “frontend and backend” separately — it’s about connecting them meaningfully.
Here’s how pros think:
End-to-end flow awareness: Every UI interaction maps to a backend operation and a database query.
Ownership: You own the experience, from button click to database write.
Consistency: Naming conventions, error formats, and DTOs should match across the stack.
Maintainability over speed: Your future self (and your teammates) should be able to read your code easily.
Automation over repetition: If you do something twice manually, script it once.
The mindset is: architect first, code second.
2. The Monorepo Approach (One Workspace to Rule Them All)
We’ll build our app as a monorepo — a single codebase containing both frontend (React) and backend (NestJS), managed by pnpm workspaces.
Why?
Shared code (types, DTOs, utils) between client and server
One
pnpm installfor everythingEasier CI/CD setup
Simplified version control
3. Directory Structure Overview
Here’s the structure we’ll use throughout the entire series:
fullstack-app/
├── apps/
│ ├── web/ # React + TypeScript frontend
│ └── api/ # NestJS + Prisma backend
├── packages/
│ ├── types/ # Shared TypeScript types/interfaces
│ ├── utils/ # Common helper functions
│ └── config/ # Shared environment and constants
├── prisma/ # Central Prisma schema + migrations
├── .github/ # CI/CD workflows
├── docker/ # Dockerfiles and compose configs
├── .env.example # Environment template
├── package.json
├── pnpm-workspace.yaml
└── README.mdEach folder will evolve in later parts — React components in web, REST controllers and DTOs in api, and shared contracts in packages/types.
4. Setting Up pnpm Workspaces
Inside your root folder:
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"This tells pnpm to treat all subfolders as part of one workspace.
Now, when you run pnpm install, dependencies are linked intelligently — no duplication, no drift.
5. Environment Variables and Config Management
A consistent .env file strategy is essential across the stack.
We’ll use dotenv in both React and NestJS.
Example .env.example:
DATABASE_URL="postgresql://postgres:password@localhost:5432/fullstack_dev"
API_URL="http://localhost:3000"
NODE_ENV="development"
JWT_SECRET="supersecret"Each environment (development, staging, production) will have its own .env file, but all share the same structure.
6. Shared Types for End-to-End Consistency
Avoid backend–frontend drift by sharing TypeScript definitions.
In /packages/types:
// packages/types/src/user.ts
export interface User {
id: number;
name: string;
email: string;
}Use it everywhere:
// apps/web/hooks/useUsers.ts
import { User } from "@types/user";This ensures React, NestJS, and Prisma always agree on what a “User” means — reducing bugs and integration issues.
7. Versioning, Linting, and Formatting Standards
Your workspace should enforce one style and one version of key tools.
Install and configure:
pnpm add -D eslint prettier @typescript-eslint/eslint-plugin @typescript-eslint/parserAdd shared configs in /packages/config/eslint.config.js and /packages/config/prettier.config.js.
Example ESLint config:
module.exports = {
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
root: true,
};Then apply it across apps using:
pnpm dlx eslint .8. Developer Mindset: Automation and Documentation
Document every architectural decision as you go.
Use a docs/ folder or your README to capture:
Project purpose
API routes
Deployment steps
Environment variables
And automate what you repeat:
Database resets
Lint + format on commit (using Husky + lint-staged)
Pre-push tests in CI
When your setup automates quality, you focus on features.
9. Preparing for Part 1
You’re now set for the exciting part — building the frontend.
In Part 1, we’ll:
Spin up a new React + TypeScript app
Explore components, state, and events
Connect frontend interactions to real backend calls later
Before moving on, ensure:
PostgreSQL is running (Docker or local)
pnpm workspace is linked
.envand.gitignoreare in placeGitHub repo is connected
Your project skeleton is now complete — production-minded, scalable, and clean.
10. Key Takeaway
Structure isn’t overhead — it’s velocity insurance.
By organizing your project early, you’ll build faster later with fewer surprises.
Next, we’ll officially start Part 1: React + TypeScript Basics: Components, State, Events — the hands-on beginning of your full-stack build.
Related
Part 2.5 — Consuming REST APIs with Shared Types & Error Handling Strategies
Real-world frontends need safety: typed API responses, predictable errors, and consistent client logic. This post teaches you the patterns professionals use to integrate React with REST cleanly.
Part 2.4 — UX State: Loading, Error, and Empty States in React
A React app becomes truly usable when it handles all states gracefully: loading, error, empty, and success. In this post, you’ll learn the UX patterns professionals rely on.
Part 2.3 — Handling Forms, Validation & Submission to REST Endpoints
Forms connect users to data. In this post, you’ll learn how to validate input, manage UX states, and submit cleanly typed data to real REST endpoints.
Comments