Introduction
Full-stack development has evolved significantly with the rise of powerful frameworks and tools that streamline both frontend and backend development. This blog explores how to combine Next.js, Prisma ORM, NestJS, and PostgreSQL to create a dynamic, efficient, and scalable full-stack application.
Why This Stack?
- Next.js: A React-based framework offering server-side rendering, static site generation, and powerful routing.
- NestJS: A progressive Node.js framework for building efficient, scalable server-side applications.
- Prisma ORM: A modern ORM that simplifies database interactions with type-safe queries and easy migrations.
- PostgreSQL: A powerful, open-source relational database system known for its reliability and advanced features.
Setting Up the Project
1. Initialize the Project
Create a new directory and initialize a monorepo structure using TurboRepo or manually separate frontend and backend folders.
mkdir fullstack-app && cd fullstack-app
npx create-next-app frontend
npm install -g @nestjs/cli
nestjs new backend
2. Configure PostgreSQL
Install PostgreSQL and set up a new database.
sudo apt update
sudo apt install postgresql postgresql-contrib
sudo -u postgres psql
CREATE DATABASE fullstack_db;
3. Set Up Prisma ORM
In the backend directory:
cd backend
npm install @prisma/client prisma
npx prisma init
Update prisma/schema.prisma:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
createdAt DateTime @default(now())
}
Run migrations:
npx prisma migrate dev --name init
Building the Backend with NestJS
1. Configure Prisma with NestJS
npm install @nestjs/config @nestjs/prisma
Create a PrismaService to handle database interactions.
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient {
constructor() {
super();
}
}
2. Create REST APIs
Generate a user module, service, and controller.
nestjs generate module user
nestjs generate service user
nestjs generate controller user
In user.service.ts:
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma.service';
@Injectable()
export class UserService {
constructor(private prisma: PrismaService) {}
async createUser(data: { name: string; email: string }) {
return this.prisma.user.create({ data });
}
async getUsers() {
return this.prisma.user.findMany();
}
}
Building the Frontend with Next.js
1. Set Up Axios
Create an API utility:
import axios from 'axios';
const API = axios.create({ baseURL: 'http://localhost:3000' });
export const fetchUsers = () => API.get('/users');
export const createUser = (user) => API.post('/users', user);
2. Create Pages and Components
Build a user list and a form to add new users.
import { useState, useEffect } from 'react';
import { fetchUsers, createUser } from '../api';
export default function Home() {
const [users, setUsers] = useState([]);
const [name, setName] = useState('');
const [email, setEmail] = useState('');
useEffect(() => {
fetchUsers().then((res) => setUsers(res.data));
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
await createUser({ name, email });
const res = await fetchUsers();
setUsers(res.data);
};
return (
<div>
<h1>User List</h1>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name} - {user.email}</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">Add User</button>
</form>
</div>
);
}
Deploying the Application
- Frontend: Deploy on Vercel for seamless integration with Next.js.
- Backend: Deploy on Heroku, Railway, or AWS.
- Database: Use services like Supabase or Railway for hosted PostgreSQL.
Conclusion
By leveraging Next.js, Prisma ORM, NestJS, and PostgreSQL, you can build scalable and efficient full-stack applications. This stack ensures strong type safety, optimized performance, and streamlined development workflows. Dive deeper into each tool to unlock more advanced features and build complex applications effortlessly.