Contents

Developing the Frontend

Building a user-friendly and responsive frontend is key to providing a good user experience. In this guide, we’ll walk through setting up a simple frontend using React, integrating it with your Node.js backend, and handling user authentication and session management.

Setting Up a Simple Front-End Using React

React is a popular JavaScript library for building user interfaces, especially single-page applications (SPAs).

1. Create the React App:

Start by creating a new React app using create-react-app, which sets up everything you need with a single command.

				
					npx create-react-app client
cd client

				
			

2. Organize the Project Structure:

Inside the src folder, you can create directories for components, pages, and services to keep your code organized.

Example Structure:

				
					/src
  /components
    TaskList.js
    TaskForm.js
  /pages
    HomePage.js
    LoginPage.js
    RegisterPage.js
  /services
    authService.js
    taskService.js
  App.js
  index.js

				
			

3. Create Basic Components:

Let’s start by creating a simple TaskList component to display tasks:

				
					// src/components/TaskList.js
import React from 'react';

const TaskList = ({ tasks }) => {
  return (
    <ul>
      {tasks.map(task => (
        <li key={task.id}>
          {task.title} - {task.completed ? 'Completed' : 'Pending'}
        </li>
      ))}
    </ul>
  );
};

export default TaskList;

				
			

And a TaskForm component for adding new tasks:

				
					// src/components/TaskForm.js
import React, { useState } from 'react';

const TaskForm = ({ onAddTask }) => {
  const [title, setTitle] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    if (title.trim()) {
      onAddTask({ title, completed: false });
      setTitle('');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        placeholder="New task"
      />
      <button type="submit">Add Task</button>
    </form>
  );
};

export default TaskForm;


				
			

4. Set Up Routing:

Install react-router-dom to handle routing in your app:

				
					npm install react-router-dom

				
			

Then, set up basic routing in App.js:

				
					// src/App.js
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import HomePage from './pages/HomePage';
import LoginPage from './pages/LoginPage';
import RegisterPage from './pages/RegisterPage';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/login" element={<LoginPage />} />
        <Route path="/register" element={<RegisterPage />} />
      </Routes>
    </Router>
  );
}

export default App;


				
			

Integrating the Front-End with the Node.js Backend

Now that you have a basic frontend set up, the next step is to connect it to your Node.js backend.

1. Create Service Functions for API Calls:

Inside the services directory, create functions to handle API requests. For example, here’s how you might set up a service to fetch tasks from your backend:

				
					// src/services/taskService.js
import axios from 'axios';

const API_URL = 'http://localhost:5000/api/tasks';

export const getTasks = async () => {
  const response = await axios.get(API_URL);
  return response.data;
};

export const addTask = async (task) => {
  const response = await axios.post(API_URL, task);
  return response.data;
};


				
			

2. Use the Service Functions in Your Components:

In HomePage.js, you can use these service functions to fetch and display tasks:

				
					// src/pages/HomePage.js
import React, { useEffect, useState } from 'react';
import { getTasks, addTask } from '../services/taskService';
import TaskList from '../components/TaskList';
import TaskForm from '../components/TaskForm';

const HomePage = () => {
  const [tasks, setTasks] = useState([]);

  useEffect(() => {
    const fetchTasks = async () => {
      const tasks = await getTasks();
      setTasks(tasks);
    };

    fetchTasks();
  }, []);

  const handleAddTask = async (newTask) => {
    const savedTask = await addTask(newTask);
    setTasks([...tasks, savedTask]);
  };

  return (
    <div>
      <h1>Task Manager</h1>
      <TaskForm onAddTask={handleAddTask} />
      <TaskList tasks={tasks} />
    </div>
  );
};

export default HomePage;


				
			

Handling User Authentication and Session Management

Authentication is a critical part of any web application that deals with user data. Let’s add basic user authentication to your frontend.

1. Create Authentication Service:

Create an authService.js file in the services directory to handle login and registration:

				
					// src/services/authService.js
import axios from 'axios';

const API_URL = 'http://localhost:5000/api/users';

export const register = async (userData) => {
  const response = await axios.post(`${API_URL}/register`, userData);
  return response.data;
};

export const login = async (userData) => {
  const response = await axios.post(`${API_URL}/login`, userData);
  if (response.data.token) {
    localStorage.setItem('user', JSON.stringify(response.data));
  }
  return response.data;
};

export const logout = () => {
  localStorage.removeItem('user');
};

export const getCurrentUser = () => {
  return JSON.parse(localStorage.getItem('user'));
};

				
			

2. Create Authentication Pages:

Set up basic login and registration pages using these service functions.

LoginPage.js:

				
					// src/pages/LoginPage.js
import React, { useState } from 'react';
import { login } from '../services/authService';
import { useNavigate } from 'react-router-dom';

const LoginPage = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      await login({ email, password });
      navigate('/');
    } catch (error) {
      console.error('Login failed:', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
        required
      />
      <button type="submit">Login</button>
    </form>
  );
};

export default LoginPage;

				
			

RegisterPage.js:

				
					// src/pages/RegisterPage.js
import React, { useState } from 'react';
import { register } from '../services/authService';
import { useNavigate } from 'react-router-dom';

const RegisterPage = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      await register({ email, password });
      navigate('/login');
    } catch (error) {
      console.error('Registration failed:', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
        required
      />
      <button type="submit">Register</button>
    </form>
  );
};

export default RegisterPage;

				
			

3. Protecting Routes:

To protect routes that require authentication, you can create a simple PrivateRoute component:

				
					// src/components/PrivateRoute.js
import React from 'react';
import { Navigate } from 'react-router-dom';
import { getCurrentUser } from '../services/authService';

const PrivateRoute = ({ children }) => {
  const user = getCurrentUser();
  return user ? children : <Navigate to="/login" />;
};

export default PrivateRoute;

				
			

Then use it in your routing setup:

				
					// src/App.js
import PrivateRoute from './components/PrivateRoute';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<PrivateRoute><HomePage /></PrivateRoute>} />
        <Route path="/login" element={<LoginPage />} />
        <Route path="/register" element={<RegisterPage />} />
      </Routes>
    </Router>
  );
}

				
			

Conclusion

Developing the frontend involves setting up a user interface, connecting it to the backend, and managing user authentication and sessions. By following these steps, you can create a responsive, functional frontend that interacts seamlessly with your Node.js backend, providing a smooth user experience.