Creating a Full Stack Web Application with Spring Boot and Next js

create next app

Learn to build a full-stack web app with Spring Boot and Next.js. In this blog post, we will cover the step-by-step process of building a web application using Next.js and Spring Boot. We'll explore various topics, including configuring Next.js as a static site with a Spring Boot backend, integrating Next.js and Spring Boot in a single app server, setting up proxy server settings in Next.js, and leveraging application plugins in a Spring Boot application. 

Here are some of the topics covered.

Configuring Next.js as a Static Site with a Spring Boot Backend.
Integrating Next.js and Spring Boot in a Single App Server.
Next.js Proxy Server Settings for development purposes.
Application Plugins with Spring Boot App.
Next.js Proxy Server Settings without Cross-Domain Proxy Settings for dev.
Build a Spring Boot Application with Application Plugins in Gradle.
Gralde with Spring Boot for production.


Environment and Tools

Java
17
Spring Boot
3.1.1
NextJs
13.4.10           
React
18.2.0
Gradle
8.1.1
Node
16.17.1
npm
8.15.0

​Frontend Setup with Next.js

Create a Next.js App

To start, open your desired directory and run the following command to create the front-end of your application:

npx create-next-app@latest


This command sets up a new Next.js project with the latest version, providing you with a solid foundation for building the front end.

Open the Next.js Project in VS Code

Once the Next.js project is created, open it in your preferred text editor, such as Visual Studio Code. This will allow you to work with the front-end code and make necessary modifications.

Update the package.json File

In the Next.js project, locate the package.json file and update it with any additional dependencies or scripts specific to your application. This file manages the project's dependencies and configuration.

{
  "name": "nextjs-spring-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build && next export",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "eslint": "8.44.0",
    "eslint-config-next": "13.4.10",
    "next": "13.4.10",
    "react": "18.2.0",
    "react-dom": "18.2.0"
  }
}

Modify the next.config.js File

Configure your Next.js application by modifying the next.config.js file. This file allows you to define custom settings, configure routing, and perform other project-specific configurations.

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    unoptimized: true,
  },
  async rewrites() {
    return [
      {
        source: '/e3docs/:path*',
        destination: 'http://localhost:8080/e3docs/:path*',
      },
    ]
  },
}
module.exports = nextConfig

Fetch Data from the Backend

Create a list.js file in the Next.js project responsible for fetching data from the Spring Boot API. Implement the necessary logic to retrieve data from the backend and render it in the frontend.

list.js

import Link from 'next/link';
import { Fragment, useEffect, useState } from 'react';

function List() {
    const [list, setList] = useState([])
    const fetchList = () => {
        fetch("/e3docs/getlist")
            .then(response => {
                return response.json()
            })
            .then(data => {
                setList(data)
            })
    }
    useEffect(() => {
        fetchList()
    }, [])
    function getlist() {
        return list.map((item, i) => {
            return (
                <Fragment key={i}>
                    <tr>
                        <td>
                            {(i + 1)}
                        </td>
                        <td>
                            {item}
                        </td>
                    </tr>
                </Fragment>
            );
        })
    }
    return (
        <main>
            <div style={{ paddingLeft: '34%', paddingTop: '2%', display: 'flex' }}>
                <h2 style={{ paddingRight: '10px' }}>
                    <Link href="/">Home</Link>
                </h2>
                <h2>
                    <Link href="/list">List</Link>
                </h2>
            </div>
            <div style={{ paddingLeft: '34%', paddingTop: '3%' }}>
                <table>
                    <thead>
                        <tr>
                            <th>No.</th>
                            <th>Description</th>
                        </tr>
                    </thead>
                    <tbody>
                        {list.length > 0 && (getlist())}
                    </tbody>
                </table >
            </div>
        </main>
    );
}
export default List;​​

Modify index.js and Add List Mapping

In the Next.js project, modify the index.js file and add a mapping for the list page using the Link tag. This will enable users to navigate to the list page and view the data fetched from the backend.

index.js

import Head from 'next/head'
import Image from 'next/image'
import Link from 'next/link'

export default function Home() {
  return (
    <>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <div style={{ paddingLeft: '34%', paddingTop: '2%', display: 'flex' }}>
          <h2 style={{ paddingRight: '10px' }}>
            <Link href="/">Home</Link>
          </h2>
          <h2>
            <Link href="/list">List</Link>
          </h2>
        </div>
        <div style={{ paddingLeft: '34%', paddingTop: '3%' }}>
          <Image
            src="/next.svg"
            alt="Next.js Logo"
            width={180}
            height={37}
            priority
          />
          <Image
            src="/Spring_Logo.svg"
            alt="Spring Logo"
            width={200}
            height={45}
            priority
          />
        </div>
      </main>
    </>
  )
}

Create a New Page to Handle Web App Refresh and Direct URL Entry

To handle scenarios like web app refresh or direct URL entry, create a new page in the Next.js project. This page will help to handle the 404 error page in Next JS when the user enters a direct URL into the browser or the page refreshes.

pagerefresh.js

import { useRouter } from 'next/router';
import { useEffect } from 'react';
function PageRefresh() {
    const router = useRouter();
    useEffect(() => {
        if (router.isReady) {
            router.push(router.asPath).catch((e) => {
                router.push('/_error');
            });
        }
    }, [router.isReady])
    return (
        <div>Loading...</div>
    );
}
export default PageRefresh;

Build and Export the Next.js Project

To generate the static content for deployment, run the following command in your Next.js project:

npm run build

This will create an "out" folder with the compiled and optimized Next.js application.

Backend Setup with Spring Boot

Create a New Project with Spring Initializr

Use Spring Initializr to create a new Spring Boot project. Specify the necessary dependencies and project settings, and generate the project structure.

Update the build.gradle File

After generating the project, locate the build.gradle file and update it with any additional dependencies or plugins required for your application. This file serves as the configuration for your build system.

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.1'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'eclipse'
    id 'application'
}
group = 'com.e3docs'
version = '0.0.1-SNAPSHOT'
java {
    sourceCompatibility = '17'
}
repositories {
    mavenCentral()
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-rest'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
    useJUnitPlatform()
}
application {
    mainClass = 'com.e3docs.SpringBootAndNextJsApplication'
    applicationDefaultJvmArgs = ['-Dfile.encoding=UTF-8']
    bootDistZip.enabled = false
    bootDistTar.enabled = false
    distTar.enabled = false
    bootJar.enabled = false
    executableDir = ""
}

Build the Project for Eclipse

Prepare the project for Eclipse by running the following command in your project directory:

gradlew clean cleanEclipse build eclipse

This command cleans the project, builds it, and generates Eclipse-specific files for smooth integration with the IDE.

Import the Project into Eclipse

Once the command from the previous step is completed successfully, import the project into Eclipse as an existing project. This allows you to leverage the powerful features of Eclipse for development.

Create a Controller Class for API

To handle API requests, create a controller class in your Spring Boot project. This class will define the endpoints and their associated request-handling methods, allowing you to interact with your backend API.

package com.e3docs.controller;

import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("e3docs")
public class E3DocsController {
@GetMapping("/getlist")
public List<String> getList() {
List<String> list = new ArrayList<String>();
list.add("India");
list.add("China");
list.add("United States(US)");
list.add("Russia");
list.add("Australia");
list.add("African");
list.add("Europe");
return list;
}
}


Create a Filter Component for Next.js Integration

For smooth integration between the Spring Boot backend and the Next.js frontend, create a filter component. This component will intercept requests and perform necessary actions like direct URL entry or page refresh.

package com.e3docs.config;

import java.io.IOException;
import org.springframework.stereotype.Component;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class PageRefreshFilter implements Filter {
    private static final String PAGE = "/pagerefresh/pagerefresh.html";
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
    }
    private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String path = request.getServletPath();
        if ("/".equals(path) || path.contains(".") || path.contains("e3docs")) {
            chain.doFilter(request, response);
        } else {
            request.getRequestDispatcher(PAGE).forward(request, response);
        }
    }
}

Integrate the Frontend with the Backend

Copy the static content from the "out" folder of the Next.js project into the Spring Boot project's /src/main/resources/static folder. This ensures that the frontend assets are accessible from the Spring Boot server.

Build the Backend Project

Build the Spring Boot project by running the following command:

gradlew clean assemble

After a successful build, you will find a zip file in the build/distributions folder.

Run the Application

Extract the zip file and run the appropriate script (e.g., bat file) to start the Spring Boot server.


Once the server is up and running, access the application by navigating to http://localhost:8080 in your web browser.

Congratulations! You have successfully learned how to create a full-stack web application using Spring Boot and Next.js. By following the steps outlined in this guide, you now have the knowledge and tools to build robust and modern web applications. With Spring Boot powering the backend and Next.js handling the front end, you can leverage the strengths of both frameworks to deliver an exceptional user experience.

GitHub source code

Happy coding!