Friday, July 11, 2025

Deploying a React Frontend to Railway: A Complete Guide (To-Do App)

 If you've already deployed your Java Spring Boot backend to Railway, congratulations — the hardest part is done! ๐ŸŽ‰

Now it’s time to deploy your React frontend. Compared to the backend, this will be a breeze. In this guide, I’ll walk you through the entire process of pushing your frontend to GitHub and deploying it to Railway — step-by-step.


๐Ÿงฑ Project Structure Recap

Your project likely looks something like this:

Your API is already live at something like:
https://todo-production-40cc.up.railway.app/api/todos
Let’s now put the frontend online.

✅ Step 1: Update API Route in React

Open your /frontend/src/App.js file and update the API URL:

const API_URL = "https://todo-production-40cc.up.railway.app/api/todos";

Replace the old "http://localhost:8080" value to point to your deployed backend.

✅ Step 2: Prepare React App for Deployment

Inside your frontend folder:

Remove the .git folder

Push the changes to github using the following commands:

git rm --cached frontend

git add frontend 

git commit -m "Fix: add frontend as regular folder"

git push origin main 

✅ Step 3: Deploy Frontend on Railway

  1. Go to Railway

  2. Click “New Project”

  3. Choose “Deploy from GitHub Repo”

  4. Select your todo repository

  5. When prompted for project settings:

    • Root directory: frontend

    • Install command: npm install

    • Build command: npm run build

    • Start command: npx serve -s build

    • (Optional): Predeploy command: npm install -g serve

  6. Click Deploy ๐Ÿš€


✅ Step 4: Fix the Port (IMPORTANT)

By default, serve runs on port 8080, but Railway expects port 3000 unless you say otherwise.

Fix:

  1. Go to your project > Settings 

  2. Generate the domain.

  3. Use: PORT = 8080 when asked.

  4. Save. Railway will redeploy.

 You should see your React To-Do app, fully functional and connected to your backend!

You may check out my live todo web app at:

https://optimistic-creativity-production-e88d.up.railway.app/

 

 

 

Deploying My Spring Boot ToDo List Backend to Railway — A Complete Guide

 As part of my journey in building full-stack Java applications, I recently deployed the backend of my ToDo List application — built with Spring Boot — to Railway, a modern cloud platform that makes app hosting surprisingly easy.

In this post, I’ll walk through the exact steps I took to deploy my Spring Boot backend to Railway, connect it to a PostgreSQL database, and expose a working REST API live on the web. Refer to my previous post "Uploading the Full-Stack To-Do List App to GitHub Using a Personal Access Token" as a prerequisite of the deployment process.


๐Ÿ›  Tech Stack

  • Backend: Spring Boot 3.5.3

  • Database: PostgreSQL

  • Cloud Platform: Railway

  • Build Tool: Maven

  • Java Version: 17

  • API Endpoint: GET /api/todos


✅ Step 1: Prepare the Spring Boot App for Deployment

Modify application.properties, used environment variables for portability:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# PostgreSQL settings using environment variables
spring.datasource.url=${DATABASE_URL}
spring.datasource.username=${PGUSER}
spring.datasource.password=${PGPASSWORD}

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

# Dynamic port for Railway
server.port=${PORT:8080}

⚠️ Note: PORT is dynamically set by Railway at runtime.

Push the changes to github using the following commands:

 git add .

 git commit -m "Updated props file for Railway"

 git push origin main

๐Ÿ—„️ Step 2 Create a Railway Project

  1. Go to Railway.app

  2. Create a New Project > Select Deploy from GitHub

  3. Connect it to the GitHub repo containing the Spring Boot backend

  4. Railway auto-detects it's a Maven project and starts building

  5. Set your build settings

  6. Set custom domain(this is important as it will be used by the frrontend as part of the the url for the api calls)

  7. Set up environment variables
Go to “Variables” tab in Railway and add any necessary environment variables like:

SPRING_DATASOURCE_URL=

SPRING_DATASOURCE_USERNAME=

SPRING_DATASOURCE_PASSWORD=

Set the respective values of each variables after deploying the PosGre Database 

 

Build Settings:

1. Root directory:
Set to backend (since your Maven project is inside /backend)
2. Build command:
./mvnw clean package -DskipTests
3. Start command:
java -jar target/*.jar

๐Ÿ˜ Step 3: Add PostgreSQL Database Plugin

From the Railway dashboard:

  • Click “New” → “Add Plugin” → PostgreSQL

  • Wait for the database to be provisioned

  • Open the Data tab and press the Connection button, a popup window appears and switch to Public Connection

  • Copy the connection string (e.g. postgresql://...)


๐Ÿ” Step 4: Modify Environment Variables

Add the following values to the variables created in step 2:

VariableValue
SPRING_DATASOURCE_URL
jdbc:postgresql://<host>:<port>/<db>
SPRING_DATASOURCE_USERNAME
postgres
SPRING_DATASOURCE_PASSWORD
your_db_password

Replace <host> and <port> with actual values from the public connection string.

Tip: let chatgpt identify the port, username and password by pasting the public connection string.

 And now all is set for deployment. If the deployment is successful, just like in my case, you may now test the api call by like in my case, I used the following url:

https://todo-production-40cc.up.railway.app/api/todos


Here is the result(my todo table is still empty during testing):

If it is not empty, it will return the contents of the table in json format.

 Troubleshooting Guide: You will need a lot of help from chatgpt just like in my case, just copy the logs generated and paste it chatgpt prompt and let it analyze for you.

Wednesday, July 9, 2025

Java Programming Basics

 ✅ 1. Your First Java Program

1
2
3
4
5
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

  • public class HelloWorld: Java code must be inside a class.

  • main(String[] args): Entry point of any Java program.

  • System.out.println(): Prints output to the console.

✅ 2. Variables and Data Types

1
2
3
4
5
int age = 25;
double price = 19.99;
char grade = 'A';
boolean isJavaFun = true;
String name = "Alice";

Type Description Example
int   Integer    5
double   Decimal number   3.14
char   Single character    'A'
boolean   True or false   true / false
String   Text (object) " Hello"

✅ 3. Operators

1
2
3
int x = 10 + 5;    // 15
int y = 10 * 2;    // 20
int z = x / 3;     // 5

  • +, -, *, /, %: Arithmetic

  • ==, !=, >, <, >=, <=: Comparison

  • &&, ||, !: Logical


✅ 4. Control Statements

if Statement

1
2
3
4
5
if (age > 18) {
    System.out.println("Adult");
} else {
    System.out.println("Minor");
}

switch Statement


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int day = 3;
switch (day) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    default:
        System.out.println("Other day");
}

✅ 5. Loops

for Loop

1
2
3
for (int i = 0; i < 5; i++) {
    System.out.println(i);
}


while Loop

1
2
3
4
5
int i = 0;
while (i < 5) {
    System.out.println(i);
    i++;
}

✅ 6. Arrays

1
2
int[] numbers = {1, 2, 3, 4, 5};
System.out.println(numbers[0]); // 1

✅ 7. Methods (Functions)

1
2
3
4
5
6
public static int add(int a, int b) {
    return a + b;
}

// Usage:
int result = add(3, 4);  // 7

✅ 8. Classes and Objects

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Car {
    String color;

    public void drive() {
        System.out.println("Car is driving");
    }
}

// In another class or in main()
Car myCar = new Car();
myCar.color = "Red";
myCar.drive();

✅ 9. Comments

1
2
3
4
// This is a single-line comment

/* This is a
   multi-line comment */

✅ 10. Compile and Run Java

Assuming your file is HelloWorld.java:

javac HelloWorld.java   # Compiles the code

java HelloWorld         # Runs the program

Uploading the Full-Stack To-Do List App to GitHub Using a Personal Access Token

 Deploying a web app like our full-stack Spring Boot + React To-Do List often starts by pushing the source code to GitHub. If you've encountered issues with the traditional username/password authentication (especially after GitHub disabled password-based logins), this guide will walk you through using a Personal Access Token (PAT) instead.


๐Ÿ” Step 1: Generate a Personal Access Token

  1. Go to https://github.com/settings/tokens.

  2. Click "Generate new token (classic)".

  3. Add a descriptive name, like "todo app push access".

  4. Set an expiration date (e.g., 30 or 90 days).

  5. Under Scopes, check at least:

    • repo (full control of private repositories)

  6. Click Generate Token.

  7. Copy the token now. GitHub will not show it again.


๐Ÿ“ Step 2: Initialize Git in Your Project

If you haven’t already, open your terminal (Git Bash on Windows) and navigate to your project folder:

cd /c/todo_app

Initialize the Git repo:

git init

git add .

git commit -m "Initial commit for full-stack To-Do app"

๐ŸŒ Step 3: Add Your Remote Repository

Go to https://github.com, create a new repository called todo.

Then link your local project to it:

git remote set-url origin https://<token>@github.com/<username>/todo.git

Replace yourusername with your GitHub username.


๐Ÿš€ Step 4: Push Code Using the Personal Access Token

When you run:

git push -u origin main

✔️ Once successful, your project is live on GitHub.


Building a Java Swing Desktop Version of the Spring Boot + React To-Do List App

 If you haven’t followed the earlier guides, the app consists of a backend built with Spring Boot, a frontend built with React.js, and a PostgreSQL database. You can check them out here:

Why a Desktop Version?

Sometimes it's convenient to have a native GUI app that connects to the same backend. This Java Swing version uses the same PostgreSQL database as the web app, providing a consistent experience regardless of platform.


Project Structure

  • Language: Java (Swing GUI)

  • Database: PostgreSQL (same as backend)

  • Access: Uses JDBC to connect directly

  • Features:

    • View all tasks

    • Mark tasks as complete

    • Delete tasks

    • Add new tasks


Dependencies

  1. PostgreSQL JDBC Driver

    Download the .jar from here, for example:

    • postgresql-42.2.29.jre7.jar

    Save it in the same folder as your Swing project.

  2. Java 17+

    Ensure you're using a compatible Java version (same as Spring Boot).


Directory Layout

Assuming your project is in C:\todo_app\desktop, the folder should contain:


Database Table Format

The table should already be created by your Spring Boot app via JPA: 

1
2
3
4
5
CREATE TABLE todo (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255),
    completed BOOLEAN
);

If it's empty, add sample tasks from the web app or using psql.


Source Code: TodoApp.java

Here's the complete Swing code that mirrors the behavior and look of the React frontend:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.awt.font.TextAttribute;

public class TodoApp extends JFrame {
    private Connection conn;
    private JPanel taskPanel;
    private JTextField inputField;

    public TodoApp() {
        setTitle("To-Do List");
        setSize(400, 600);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setLayout(new BorderLayout());

        inputField = new JTextField();
        JButton addButton = new JButton("Add");

        JPanel inputPanel = new JPanel(new BorderLayout());
        inputPanel.add(inputField, BorderLayout.CENTER);
        inputPanel.add(addButton, BorderLayout.EAST);

        taskPanel = new JPanel();
        taskPanel.setLayout(new BoxLayout(taskPanel, BoxLayout.Y_AXIS));
        JScrollPane scrollPane = new JScrollPane(taskPanel);

        add(inputPanel, BorderLayout.NORTH);
        add(scrollPane, BorderLayout.CENTER);

        try {
            conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/todo_db", <user>, <paword>);
        } catch (SQLException e) {
            JOptionPane.showMessageDialog(this, "Database connection failed: " + e.getMessage());
            System.exit(1);
        }

        loadTasks();

        addButton.addActionListener(e -> addTask());
    }

    private void loadTasks() {
        taskPanel.removeAll();
        try {
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT * FROM todo");

            while (rs.next()) {
                long id = rs.getLong("id");
                String title = rs.getString("title");
                boolean completed = rs.getBoolean("completed");

               
		JPanel panel = new JPanel(new BorderLayout());
		panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 40)); // Set row height to 40px
		panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // Optional spacing
                JCheckBox checkBox = new JCheckBox();
                checkBox.setSelected(completed);

                JLabel label = new JLabel(title);
                if (completed) {
                    Map<TextAttribute, Object> attributes = new HashMap<>(label.getFont().getAttributes());
                    attributes.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
                    label.setFont(label.getFont().deriveFont(attributes));
                }

                checkBox.addActionListener(e -> toggleComplete(id, checkBox.isSelected()));

                JButton deleteBtn = new JButton("X");
                deleteBtn.addActionListener(e -> deleteTask(id));

                panel.add(checkBox, BorderLayout.WEST);
                panel.add(label, BorderLayout.CENTER);
                panel.add(deleteBtn, BorderLayout.EAST);

                taskPanel.add(panel);
            }

            taskPanel.revalidate();
            taskPanel.repaint();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private void addTask() {
        String title = inputField.getText().trim();
        if (title.isEmpty()) return;

        try {
            PreparedStatement ps = conn.prepareStatement("INSERT INTO todo (title, completed) VALUES (?, false)");
            ps.setString(1, title);
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        inputField.setText("");
        loadTasks();
    }

    private void deleteTask(long id) {
        try {
            PreparedStatement ps = conn.prepareStatement("DELETE FROM todo WHERE id = ?");
            ps.setLong(1, id);
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        loadTasks();
    }

    private void toggleComplete(long id, boolean completed) {
        try {
            PreparedStatement ps = conn.prepareStatement("UPDATE todo SET completed = ? WHERE id = ?");
            ps.setBoolean(1, completed);
            ps.setLong(2, id);
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        loadTasks();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new TodoApp().setVisible(true);
        });
    }
}

Building and Running

Compile:

javac -cp ".;postgresql-42.2.29.jre7.jar" TodoApp.java

Run:

java -cp ".;postgresql-42.2.29.jre7.jar" TodoApp

 

Final Output

The desktop app should:

  • Display tasks just like the React UI

  • Use checkboxes for status

  • Show an "X" button to delete

  • Allow typing and adding new tasks at the bottom

Example:



Tuesday, July 8, 2025

Switching from MySQL to PostgreSQL in a Java Spring Boot To-Do List App

When building a CRUD-based To-Do List web application using Java Spring Boot and MySQL, everything works fine locally—until you realize you want to switch databases, maybe for features, compatibility, or performance reasons.

In this post, I’ll walk you through how I migrated the backend of my To-Do List app from MySQL to PostgreSQL, without touching the frontend. This transition keeps the app fully functional while making it easier to scale and adapt to modern SQL standards.


๐Ÿ“ Stack Overview

Before the migration, the backend stack looked like this:

  • Backend: Spring Boot (Java 17, Maven)

  • Database: MySQL (via WampServer64)

  • Frontend: React.js

  • IDE: Notepad (yes, keeping it raw!)

We’ll update the backend to use PostgreSQL instead of MySQL.


๐Ÿ“ฆ Step 1: Remove MySQL Connector from pom.xml

Open your pom.xml file and locate the MySQL dependency:

1
2
3
4
5
<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-j</artifactId>
  <scope>runtime</scope>
</dependency>

Either delete or comment this out.


✅ Step 2: Add PostgreSQL Dependency

Still in pom.xml, add the PostgreSQL JDBC driver instead:

1
2
3
4
5
<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <version>42.7.3</version>
</dependency>

This ensures that Spring Boot can connect to a PostgreSQL database using JPA.


⚙️ Step 3: Update application.properties

Head over to src/main/resources/application.properties and update the datasource settings. Replace the old MySQL config with the following PostgreSQL configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# PostgreSQL connection
spring.datasource.url=jdbc:postgresql://localhost:5432/todo_db
spring.datasource.username=todo_user
spring.datasource.password=yourpassword

# JPA (Hibernate) Settings
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

# Server port (optional)
server.port=8080

If you're using a different port, adjust the 5432 part of the URL accordingly.


๐Ÿ—ƒ️ Step 4: Create the PostgreSQL Database

If PostgreSQL isn't installed on your system yet, grab it from:
๐Ÿ‘‰ https://www.postgresql.org/download/

Then open the psql terminal or any PostgreSQL GUI like pgAdmin, and run:

1
2
3
CREATE DATABASE todo_db;
CREATE USER todo_user WITH PASSWORD 'yourpassword';
GRANT ALL PRIVILEGES ON DATABASE todo_db TO todo_user;

๐Ÿ’ก Make sure the credentials match your application.properties.


๐Ÿงช Step 5: Run Your Spring Boot App

Back in your terminal, run:

mvnw spring-boot:run

If everything is set up correctly, Spring Boot will:

  • Connect to the new PostgreSQL database

  • Auto-generate your todo table using ddl-auto=update

  • Serve your endpoints like before

You should see Hibernate logs showing SQL being run on PostgreSQL.


✅ Backend Now Uses PostgreSQL

Your app now works with PostgreSQL instead of MySQL, with zero changes to the actual codebase (like your Todo model or TodoRepository).


๐ŸŒŸ Final Thoughts

PostgreSQL is a great fit for modern backend development—offering JSONB support, better standards compliance, and advanced SQL features. Whether you're planning to scale up or just want to get closer to production-ready setups, switching to Postgres is a smart step.

If you’re using tools like pgAdmin or DBeaver, you’ll also find PostgreSQL easier to inspect and work with for complex queries.

Sunday, July 6, 2025

To-Do List (CRUD) Web Application using Java Spring Boot, MySQL (via WampServer64), and React.js, all using Notepad as your IDE

 ๐Ÿ“‹ Introduction

In this project, we’ll build a simple To-Do List Web Application using Java Spring Boot for the backend, MySQL (via WampServer64) for data storage, and React.js for the frontend. This app lets users perform basic CRUD operations—create, read, update, and delete tasks. It’s perfect for beginners who want to understand how to connect a Java backend with a modern JavaScript frontend. Uniquely, we'll use Notepad as our code editor, proving that you don’t need heavy IDEs to create a fully functional full-stack application from scratch.

๐Ÿงฑ Part 1: Set Up the Backend (Java Spring Boot)

๐Ÿ”น Step 1: Generate Spring Boot Project

  1. Go to https://start.spring.io

  2. Fill in the following:

    • Project: Maven

    • Language: Java

    • Spring Boot: (Use the latest stable version)

    • Group: com.example

    • Artifact: todo

    • Name: todo

    • Description: Simple ToDo List

    • Package name: com.example.todo

    • Packaging: Jar

    • Java version: (Pick what you have installed)

  3. Dependencies:

    • Spring Web

    • Spring Data JPA

    • MySQL Driver

    • Spring Boot DevTools (optional for auto-reload)

  4. Click Generate, and it will download a .zip file.

  5. Extract the zip to your preferred folder (e.g., C:\todo-backend).




๐Ÿ”น Step 2: Configure MySQL Database on WampServer64

  1. Launch WampServer and open phpMyAdmin (usually at http://localhost/phpmyadmin).

  2. Create a new database:

    • Name: todo_db

  3. Create a MySQL user or use root (default with no password unless you've changed it).


๐Ÿ”น Step 3: Configure application.properties

Open src/main/resources/application.properties in Notepad:

1
2
3
4
5
6
7
spring.datasource.url=jdbc:mysql://localhost:3306/todo_db
spring.datasource.username=root
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
server.port=8080

๐Ÿ”น Step 4: Create the To-Do Entity

Create a file Todo.java in src/main/java/com/example/todo/model/:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.example.todo.model;

import jakarta.persistence.*;

@Entity
public class Todo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private boolean completed;

    // Getters and setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }

    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }

    public boolean isCompleted() { return completed; }
    public void setCompleted(boolean completed) { this.completed = completed; }
}

๐Ÿ”น Step 5: Create Repository

Create TodoRepository.java in src/main/java/com/example/todo/repository/:

1
2
3
4
5
6
7
package com.example.todo.repository;

import com.example.todo.model.Todo;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TodoRepository extends JpaRepository<Todo, Long> {
}

๐Ÿ”น Step 6: Create REST Controller

Create TodoController.java in src/main/java/com/example/todo/controller/:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.example.todo.controller;

import com.example.todo.model.Todo;
import com.example.todo.repository.TodoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@CrossOrigin(origins = "*") // allow frontend
@RequestMapping("/api/todos")
public class TodoController {

    @Autowired
    private TodoRepository repository;

    @GetMapping
    public List<Todo> getAll() {
        return repository.findAll();
    }

    @PostMapping
    public Todo create(@RequestBody Todo todo) {
        return repository.save(todo);
    }

    @PutMapping("/{id}")
    public Todo update(@PathVariable Long id, @RequestBody Todo todo) {
        Todo existing = repository.findById(id).orElseThrow();
        existing.setTitle(todo.getTitle());
        existing.setCompleted(todo.isCompleted());
        return repository.save(existing);
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) {
        repository.deleteById(id);
    }
}

๐Ÿ”น Step 7: Run the Backend

Open CMD, navigate to the project folder:

cd C:\todo-backend

.\mvnw spring-boot:run

Backend should start on http://localhost:8080


๐ŸŒ Part 2: Set Up the Frontend (React.js)

๐Ÿ”น Step 1: Create React App

  1. Open CMD and run:

npx create-react-app todo-frontend
cd todo-frontend
  1. Start the app:

npm start

It should open http://localhost:3000.


๐Ÿ”น Step 2: Create Simple CRUD UI

In src/App.js, replace with:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import React, { useState, useEffect } from 'react';

const API_URL = "http://localhost:8080/api/todos";

function App() {
  const [todos, setTodos] = useState([]);
  const [title, setTitle] = useState("");

  useEffect(() => {
    fetchTodos();
  }, []);

  const fetchTodos = async () => {
    const res = await fetch(API_URL);
    const data = await res.json();
    setTodos(data);
  };

  const addTodo = async () => {
    if (!title.trim()) return;
    await fetch(API_URL, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ title, completed: false }),
    });
    setTitle("");
    fetchTodos();
  };

  const toggleTodo = async (todo) => {
    await fetch(`${API_URL}/${todo.id}`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ ...todo, completed: !todo.completed }),
    });
    fetchTodos();
  };

  const deleteTodo = async (id) => {
    await fetch(`${API_URL}/${id}`, { method: "DELETE" });
    fetchTodos();
  };

  return (
    <div style={{ padding: 20 }}>
      <h2>To-Do List</h2>
      <input value={title} onChange={(e) => setTitle(e.target.value)} />
      <button onClick={addTodo}>Add</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <span
              onClick={() => toggleTodo(todo)}
              style={{
                textDecoration: todo.completed ? "line-through" : "none",
                cursor: "pointer",
              }}
            >
              {todo.title}
            </span>
            &nbsp;
            <button onClick={() => deleteTodo(todo.id)}>X</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

๐Ÿ”น Step 3: Allow CORS (already done)

We’ve already allowed CORS in the controller using:

@CrossOrigin(origins = "*")

 

๐Ÿงช Test Everything

  1. Open http://localhost:3000 in your browser.

You should now be able to:

  • Add a to-do
  • Toggle completion
  • Delete items

 The todo_db current contents:


The todo_db table structure(created automatically):



Deploying a React Frontend to Railway: A Complete Guide (To-Do App)

 If you've already deployed your Java Spring Boot backend to Railway, congratulations — the hardest part is done! ๐ŸŽ‰ Now it’s time to ...