Step 1: Setting Up the Root Directory
1. Initialize the Project Root Folder
First, create the project folder, navigate into it, and initialize package.json.
mkdir fullstack-ts-app
cd fullstack-ts-app
npm init -y2. Install Root-Level Dev Dependencies
We’ll add concurrently to run both frontend and backend servers in parallel, and typescript to manage shared TypeScript configurations.
npm install -D concurrently typescriptStep 2: Setting Up the Backend
1. Create Backend Folder
Inside the project root, create a backend folder and navigate into it.
mkdir backend
cd backend2. Initialize Backend with TypeScript
Run npm init in backend and install backend-specific dependencies.
npm init -y
npm install express prisma @prisma/client dotenv
npm install -D typescript ts-node @types/node @types/express nodemon3. Initialize Prisma
Set up Prisma and create a basic database schema.
npx prisma init4. Configure .env
In the backend directory, Prisma has created a .env file. Update the DATABASE_URL as needed for your database.
DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public"5. Define Database Schema in schema.prisma
Update backend/prisma/schema.prisma as follows:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String
userId Int
user User @relation(fields: [userId], references: [id])
}6. Run Prisma Migration
Now, migrate the database:
npx prisma migrate dev --name init7. Set Up TypeScript
In backend, create tsconfig.json:
{
"compilerOptions": {
"target": "ESNext",
"module": "commonjs",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src"],
"exclude": ["node_modules"]
}8. Organize Code Structure
In backend, create the following structure and files:
backend
├── prisma
├── src
│ ├── controllers
│ │ └── userController.ts
│ ├── routes
│ │ └── userRoutes.ts
│ ├── models
│ ├── server.ts
└── tsconfig.json9. Create Server (src/server.ts)
import express from 'express'
import dotenv from 'dotenv'
import userRoutes from './routes/userRoutes'
dotenv.config()
const app = express()
const port = process.env.PORT || 5000
app.use(express.json())
app.use('/api/users', userRoutes)
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`)
})10. Controllers (src/controllers/userController.ts)
import { Request, Response } from 'express'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
export const getUsers = async (req: Request, res: Response) => {
const users = await prisma.user.findMany({
include: {
posts: true,
},
})
res.json(users)
}
export const createUser = async (req: Request, res: Response) => {
const { name, email } = req.body
const user = await prisma.user.create({
data: { name, email },
})
res.json(user)
}11. Routes (src/routes/userRoutes.ts)
import { Router } from 'express'
import { getUsers, createUser } from '../controllers/userController'
const router = Router()
router.get('/', getUsers)
router.post('/', createUser)
export default router12. Configure Backend Scripts
In backend/package.json, add the following scripts:
"scripts": {
"build": "tsc",
"dev": "nodemon src/server.ts"
}Step 3: Setting Up the Frontend
1. **Create Frontend Vite React App **
Go back to the project root
cd ..
npm create vite@latest frontend -- --template react-ts --swc2. Configure Frontend Scripts
Add a proxy in frontend/package.json:
"proxy": "http://localhost:5000",Step 4: Root-Level Configuration for Development Scripts
1. Update Root package.json
Go to the root package.json and add scripts to run frontend and backend servers independently:
"scripts": {
"dev:backend": "npm --prefix backend run dev",
"dev:frontend": "npm --prefix frontend run dev",
"dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"",
"build:backend": "npm --prefix backend run build",
"build:frontend": "npm --prefix frontend run build",
"build": "concurrently \"npm run build:backend\" \"npm run build:frontend\""
}2. Run the Servers
Use these commands to start each server independently or concurrently:
npm run dev:backend # Starts the backend
npm run dev:frontend # Starts the frontend
npm run dev # Runs both concurrentlyConclusion
This setup organizes the backend into models, controllers, and routes and connects it seamlessly with a React frontend. Now you have a robust, maintainable, full-stack TypeScript application!
