Building Full-Stack Web Applications with Next.js: A Comprehensive Guide for Developers

Next.js is a powerful React framework that enables developers to build high-performance web applications with ease. It offers features like server-side rendering, static site generation, and API routes, making it an ideal choice for building full-stack applications.

With Next.js, developers can create applications that are both SEO-friendly and fast, while also enjoying a great developer experience.

Next.js 14 Folder Structure

Next.js 14 has introduced some enhancements and changes to its folder structure, designed to promote better organization and scalability of applications.

Here’s an overview of the key folders and their purposes in a typical Next.js 14 project:

  1. /app (New)
    🔺The primary directory for defining routes using the new App Router.
    🔺Supports file-based routing, allowing you to create a structured layout for pages.
    🔺Each folder within /app can contain components, layout files, and even nested routes.
  2. /pages (Legacy)
    🔺Still supported for backward compatibility, but the new /app directory is recommended for new projects.
    🔺Contains files that define routes for your application (e.g., index.js, about.js).
    🔺Each file corresponds to a route in your application.
  3. /public
    🔺A directory for static assets such as images, fonts, and other files that can be served directly.
    🔺Files placed here can be accessed via the root URL (e.g., /logo.png).
  4. /components
    🔺A common practice is to create a components directory to store reusable React components.
    🔺Helps in keeping your application modular and maintainable.
  5. /styles
    🔺Contains global stylesheets and CSS modules.
    🔺You can organize your CSS files here, especially if you’re using CSS Modules for component-level styles.
  6. /lib
    🔺This directory typically holds utility functions and libraries that can be used throughout your application.
    🔺Good for separating business logic from UI components.
  7. /hooks
    🔺A common directory to store custom React hooks that encapsulate reusable logic.
  8. /api
    🔺Contains API routes if you’re using the /pages directory. In the /app directory, API routes are typically defined as part of the /app/api structure.
    🔺Each file corresponds to an API endpoint.
  9. /middleware
    🔺Used for defining middleware functions that run before a request is processed.
    🔺Useful for tasks like authentication and logging.
  10. /scripts
    🔺A directory for scripts related to the project, such as build scripts or custom deployment scripts.
  11. /tests
    🔺Contains test files for unit and integration testing.
    🔺Helps in maintaining code quality and ensuring functionality.
  12. /types
    🔺If you’re using TypeScript, this directory can be used to define type definitions and interfaces.

Example Structure

Here’s an example of what a Next.js 14 project structure might look like:

my-fullstack-app/
├── app/
│ ├── page.js
│ ├── about/
│ │ └── page.js
│ └── api/
│ └── getMeals/
│ └── route.js
├── components/
│ └── Navbar.js
├── public/
│ └── logo.png
├── styles/
│ └── globals.css
├── lib/
│ └── api.js
├── hooks/
│ └── useFetch.js
├── middleware/
│ └── auth.js
├── scripts/
│ └── deploy.js
├── tests/
│ └── Navbar.test.js
└── types/
└── user.d.ts

Steps to Create a Full-Stack Next.js Application

Here’s a step-by-step guide to creating a full-stack application using Next.js:

1. Set Up Your Development Environment

  • Install Node.js: Ensure you have Node.js installed on your machine.
  • Create a New Next.js App.
npx create-next-app my-fullstack-app
cd my-fullstack-app

2. Set Up a Database

  • Choose a Database: Depending on your needs, you can use options like MongoDB, PostgreSQL, or MySQL.
  • We’ll be using MongoDB in this Blog.
npm install mongodb

3. Setting up MongoDB Client Connection in Your Application

  • This code can be placed in a file within the /lib directory, and it’s often named something like mongoClient.js or mongodb.js.
import { mongoUri } from '@/utils/mongoDBUri';
import { MongoClient } from 'mongodb';

const uri = mongoUri; //replace with your own URI
const options = {
    useNewUrlParser: true,
    useUnifiedTopology: true,
};
let client;
let clientPromise;

if (process.env.NODE_ENV === 'development') {
   if (!global._mongoClientPromise) {
       client = new MongoClient(uri, options);
       global._mongoClientPromise = client.connect();
   }
   clientPromise = global._mongoClientPromise;
} else {
    client = new MongoClient(uri, options);
    clientPromise = client.connect();
}
export default clientPromise;

Start Building a Scalable Full-Stack App with Next.js Today!

4. Create API Routes

  • Set Up API Endpoints: In the pages/api directory, create files to handle requests. For example, create app/api/getMeals/route.js:
import { NextResponse } from 'next/server';
import clientPromise from '@/lib/mongodb';

export async function GET() {
    const client = await clientPromise;
    const db = client.db('meals');
    const meals = await db.collection('foodies-app').find({}).toArray();

    const reversedMeals = meals.reverse();

    return NextResponse.json({ meals: reversedMeals });
}

5. Build the Frontend

  • Create Pages: Use the pages directory to create your application’s pages. For example, a homepage can be set up in app/page.js:
'use client';
import { useRouter } from "next/navigation";
import Image from "next/image";
import { useEffect, useState } from "react";
import { endpoint } from "@/utils/endpoint";
import { MealsPopup } from "./components/mealsPopup";
import "./globals.css";

export default function Home() {
  const router = useRouter();
  const [data, setData] = useState({});
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    const fetchData = async () => {
      let res = await handleGetData();
      setData(res?.meals);
      setLoading(false);
    };

    fetchData();
 }, []);

 const handleGetData = async () => {
   const data = await fetch(`${endpoint}/getMeals`)
   if (!data.ok) {
     console.log('Failed to fetch data')
   }else{
     return data.json()
   }
 }

const handleRedirection = (item) => {
  let id = item?.id;

  // DYNAMIC ROUTE WITH SINGLE PARAM
  router.push(`/meals/${id}`)

  // DYNAMIC ROUTE WITH MULTIPLE PARAMS
  // router.push(`/meals/${slug}/${item?.id}/${item?.city}`);
}


return (
  <main style={{ background: "white", padding: "20px" }}>
    <header style={{ textAlign: "center", marginBottom: "40px" }}>
       <h1>Recipe Collection</h1>
    </header>
    <div style={{display: "flex", width: "80%", margin: "auto", justifyContent:"end", marginBottom: "20px"}} >
      <button
         onClick={() => router.push(`/add-meal`)}
         className="add-btn"
         >
         Add New Meal
      </button>
    </div>
    {!loading && <div
      style={{
        display: "grid",
        gridTemplateColumns: "repeat(auto-fill, minmax(300px, 1fr))",
        gap: "20px",
        width: "80%",
        margin: "auto",
     }}
   >
     {data?.length > 0 && data?.map((recipe, i) => (
       <div
          key={recipe.id}
          style={{
            display: "flex",
            flexDirection: "column",
            boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
            width: "100%",
            borderRadius: "8px",
            overflow: "hidden",
            cursor:"pointer"
          }}
          onClick={() => handleRedirection(recipe)}
        >
          <Image
            width={100}
            height={200}
            src={recipe.image}
            alt={recipe.name}
            layout="responsive"
            style={{ width: "100%", height: "200px", objectFit: "cover" }}
          />
          <div style={{ padding: "10px" }}>
            <h2 style={{ margin: "0 0 10px 0" }}>{recipe.name}</h2>
            <p style={{ margin: "5px 0" }}>
               <strong>Cooking Time:</strong> {recipe.cooking_time}
            </p>
            <p style={{ margin: "5px 0" }}>
               <strong>Prep Time:</strong> {recipe.prep_time}
            </p>
           </div>
          </div>
        ))}
      </div>}
     </main>
   );
}

6. Styling Your Application

  • Choose a CSS Solution: Use CSS modules, styled components, or any CSS framework like Tailwind CSS.
  • Add Styles: Create a styles directory and add your CSS files or configure your chosen CSS framework.

Related read: Getting Started with Tailwind CSS: A Step-by-Step Guide

coma

Conclusion

Creating a full-stack application with Next.js is straightforward, thanks to its robust features and developer-friendly approach.

And when talking about Next.js 14 folder structure promotes better organization and scalability, especially with the introduction of the /app directory for file-based routing. By following this structure, developers can maintain clean and efficient codebases, making it easier to manage projects as they grow.

One can build a performant, scalable, and modern web application that meets the needs of your users.

Keep Reading

Keep Reading

Enhance Your Epic EHR Expertise in Just 60 Minutes!

Register Here
  • Service
  • Career
  • Let's create something together!

  • We’re looking for the best. Are you in?