Modern web development expertise covering React, Node.js, databases, and full-stack architecture. Use when: building web applications, developing APIs, creating frontends, setting up databases, deploying web apps, or when user mentions React, Next.js, Express, REST API, GraphQL, MongoDB, PostgreSQL, or full-stack development.
Act as a senior full-stack engineer. Write type-safe, production-ready code with proper error handling, validation, and separation of concerns.
Follow these steps when building any API endpoint.
// No validation, raw any types, inconsistent errors
app.post("/api/posts", async (req, res) => {
const post = await db.post.create({ data: req.body });
res.json(post);
});
import { z } from "zod";
const CreatePostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
published: z.boolean().default(false),
});
type CreatePostInput = z.infer<typeof CreatePostSchema>;
app.post("/api/posts", async (req: Request, res: Response) => {
const result = CreatePostSchema.safeParse(req.body);
if (!result.success) {
return res.status(422).json({
error: "Validation failed",
details: result.error.flatten().fieldErrors,
});
}
const post = await prisma.post.create({ data: result.data });
return res.status(201).json({ data: post });
});
Every error response must follow this structure:
interface ApiError {
error: string;
code?: string;
details?: Record<string, string[]>;
}
Map status codes consistently:
201 after successful creation204 after successful deletion (no body)400 malformed request, 422 valid JSON but failed validation401 missing auth, 403 insufficient permissions409 conflict (duplicate unique field)429 rate limitedFollow these steps when building any component.
function UserProfile({ id }: { id: string }) {
const [user, setUser] = useState<any>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/api/users/${id}`)
.then((r) => r.json())
.then((d) => { setUser(d); setLoading(false); });
}, [id]);
if (loading) return <div>Loading...</div>;
return <div>{user.name}</div>;
}
interface User {
id: string;
name: string;
email: string;
}
function useUser(id: string) {
return useQuery<User>({
queryKey: ["user", id],
queryFn: async () => {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`Failed to fetch user: ${res.status}`);
const json = await res.json();
return json.data;
},
});
}
interface UserProfileProps {
id: string;
onEdit?: (user: User) => void;
}
function UserProfile({ id, onEdit }: UserProfileProps) {
const { data: user, isLoading, error } = useUser(id);
if (isLoading) return <UserProfileSkeleton />;
if (error) return <ErrorBanner message={error.message} />;
if (!user) return <EmptyState label="User not found" />;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
{onEdit && <button onClick={() => onEdit(user)}>Edit</button>}
</div>
);
}
Follow these steps when writing any database operation.
include or explicit joins// Fetches all fields, then fires N extra queries
const posts = await prisma.post.findMany();
for (const post of posts) {
const author = await prisma.user.findUnique({
where: { id: post.authorId },
});
post.author = author;
}
// Two related writes with no transaction
await prisma.order.create({ data: orderData });
await prisma.inventory.update({
where: { productId },
data: { stock: { decrement: 1 } },
});
// One query, only needed fields, author included
const posts = await prisma.post.findMany({
select: {
id: true,
title: true,
createdAt: true,
author: { select: { id: true, name: true } },
},
orderBy: { createdAt: "desc" },
take: 20,
});
// Atomic transaction for related writes
await prisma.$transaction([
prisma.order.create({ data: orderData }),
prisma.inventory.update({
where: { productId },
data: { stock: { decrement: 1 } },
}),
]);
When starting a new full-stack project, follow this order.
create-next-app --typescript --tailwind --appprisma, zod, @tanstack/react-queryprisma generatesrc/types/index.tsRun through these checks before any deployment.
DOMPurify if rendering user contentsrc/
app/ # Next.js App Router pages and layouts
api/ # API route handlers
components/
ui/ # Reusable primitives (Button, Input, Modal)
features/ # Domain components (PostCard, UserAvatar)
hooks/ # Custom React hooks (useUser, usePosts)
lib/ # Utilities, Prisma client, API helpers
types/ # Shared TypeScript interfaces
Place each feature's hook, component, and types together. Do not scatter related code across distant directories.