Ask me anything
Ithy Logo
Sign in to save

Comprehensive Guide to Building a Live-Updating Event List with React and C# Web API

Create dynamic, real-time event listings using React frontend and C# backend with Dapper

real time web application

Key Takeaways

  • Seamless Integration: Efficiently connect a React frontend with a C# Web API backend using Dapper for streamlined database operations.
  • Real-Time Updates: Implement live updates using SignalR to ensure event listings reflect changes instantly without manual refreshing.
  • Robust Architecture: Establish a scalable and maintainable structure that separates concerns, enhancing both development and future scalability.

Introduction

Building a responsive and dynamic event listing application involves integrating a modern frontend framework with a robust backend. This guide provides a step-by-step approach to creating a React component that lists events and updates live when new events are added. The backend is powered by a C# Web API utilizing Dapper for efficient database interactions. Additionally, real-time updates are facilitated using SignalR, ensuring that users receive immediate feedback when events are added.


Backend Setup: C# Web API with Dapper and SignalR

1. Setting Up the C# Web API Project

Begin by creating a new ASP.NET Core Web API project. This project will serve as the backend, handling data operations and providing endpoints for the React frontend.

a. Create a New Web API Project

dotnet new webapi -n EventListingApi

Navigate to the project directory:

cd EventListingApi

b. Install Necessary NuGet Packages

Install Dapper for database operations and SignalR for real-time functionalities:

dotnet add package Dapper
dotnet add package Microsoft.AspNetCore.SignalR
dotnet add package System.Data.SqlClient

2. Defining the Event Model

Create a model representing the event structure. This model maps directly to the database schema.

// Models/Event.cs
public class Event
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public DateTime CreatedAt { get; set; }
}

3. Configuring the Database

Set up the database table to store event data. This example uses SQL Server, but the approach can be adapted to other relational databases.

a. Creating the Events Table

CREATE TABLE Events (
    Id INT IDENTITY(1,1) PRIMARY KEY,
    Title NVARCHAR(200) NOT NULL,
    Description NVARCHAR(MAX),
    CreatedAt DATETIME NOT NULL DEFAULT GETDATE()
);

b. Adding the Connection String

Update the appsettings.json file with the database connection string:


{
  "ConnectionStrings": {
    "DefaultConnection": "Server=YOUR_SERVER_NAME;Database=EventDb;Trusted_Connection=True;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

4. Implementing the Repository with Dapper

The repository handles database interactions, leveraging Dapper for efficient Object-Relational Mapping (ORM).

// Repositories/EventRepository.cs
using Dapper;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;

public class EventRepository
{
    private readonly string _connectionString;

    public EventRepository(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("DefaultConnection");
    }

    public async Task<IEnumerable<Event>> GetEventsAsync()
    {
        using (IDbConnection db = new SqlConnection(_connectionString))
        {
            string sql = "SELECT * FROM Events ORDER BY CreatedAt DESC";
            return await db.QueryAsync<Event>(sql);
        }
    }

    public async Task<Event> AddEventAsync(Event newEvent)
    {
        using (IDbConnection db = new SqlConnection(_connectionString))
        {
            string sql = @"
                INSERT INTO Events (Title, Description, CreatedAt)
                VALUES (@Title, @Description, @CreatedAt);
                SELECT CAST(SCOPE_IDENTITY() as int)";
            var id = await db.QuerySingleAsync<int>(sql, new { newEvent.Title, newEvent.Description, newEvent.CreatedAt });
            newEvent.Id = id;
            return newEvent;
        }
    }
}

5. Creating the API Controller

The controller exposes endpoints for fetching and adding events. It also integrates SignalR to broadcast new events in real-time.

// Controllers/EventsController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

[ApiController]
[Route("api/[controller]")]
public class EventsController : ControllerBase
{
    private readonly EventRepository _repository;
    private readonly IHubContext<EventHub> _hubContext;

    public EventsController(EventRepository repository, IHubContext<EventHub> hubContext)
    {
        _repository = repository;
        _hubContext = hubContext;
    }

    [HttpGet]
    public async Task<IActionResult> GetEvents()
    {
        IEnumerable<Event> events = await _repository.GetEventsAsync();
        return Ok(events);
    }

    [HttpPost]
    public async Task<IActionResult> CreateEvent([FromBody] Event newEvent)
    {
        if (newEvent == null || string.IsNullOrEmpty(newEvent.Title))
        {
            return BadRequest("Invalid event data.");
        }

        newEvent.CreatedAt = DateTime.UtcNow;
        Event createdEvent = await _repository.AddEventAsync(newEvent);

        // Broadcast the new event to all connected clients
        await _hubContext.Clients.All.SendAsync("ReceiveEvent", createdEvent);

        return Ok(createdEvent);
    }
}

6. Setting Up SignalR Hub

SignalR enables real-time communication between the server and clients, allowing instant updates when new events are added.

// Hubs/EventHub.cs
using Microsoft.AspNetCore.SignalR;

public class EventHub : Hub
{
    // Hub methods can be added here if needed
}

7. Configuring the Application

Update the Program.cs file to register services and map SignalR hubs.

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddSingleton<EventRepository>();
builder.Services.AddSignalR();
builder.Services.AddCors(options =>
{
    options.AddPolicy("CorsPolicy", builder =>
    {
        builder
            .WithOrigins("http://localhost:3000") // React app URL
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials();
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
app.UseCors("CorsPolicy");

app.MapControllers();
app.MapHub<EventHub>("/eventHub");

app.Run();

Frontend Setup: React Application

1. Creating the React App

Use Create React App to bootstrap a new React project.

npx create-react-app event-listing-app
cd event-listing-app

2. Installing Required Dependencies

Install Axios for HTTP requests and @microsoft/signalr for real-time communication.

npm install axios @microsoft/signalr

3. Building the EventList Component

The EventList component fetches events from the backend, displays them, and listens for real-time updates.

// src/components/EventList.jsx
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import * as signalR from '@microsoft/signalr';

const EventList = () => {
    const [events, setEvents] = useState([]);
    const [newEvent, setNewEvent] = useState({ title: '', description: '' });
    const [connection, setConnection] = useState(null);

    useEffect(() => {
        // Initialize SignalR connection
        const newConnection = new signalR.HubConnectionBuilder()
            .withUrl('http://localhost:5000/eventHub') // Backend URL
            .withAutomaticReconnect()
            .build();

        setConnection(newConnection);
    }, []);

    useEffect(() => {
        if (connection) {
            connection.start()
                .then(() => {
                    console.log('Connected to SignalR hub');
                    connection.on('ReceiveEvent', event => {
                        setEvents(prevEvents => [event, ...prevEvents]);
                    });
                })
                .catch(err => console.error('Connection failed: ', err));
        }
    }, [connection]);

    useEffect(() => {
        // Fetch initial events
        fetchEvents();
    }, []);

    const fetchEvents = async () => {
        try {
            const response = await axios.get('http://localhost:5000/api/events');
            setEvents(response.data);
        } catch (error) {
            console.error('Error fetching events:', error);
        }
    };

    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            const response = await axios.post('http://localhost:5000/api/events', newEvent);
            // The new event will be added via SignalR, so no need to update state here
            setNewEvent({ title: '', description: '' });
        } catch (error) {
            console.error('Error adding event:', error);
        }
    };

    return (
        <div className="event-list-container">
            <h1>Event List</h1>
            <form onSubmit={handleSubmit} className="event-form">
                <input
                    type="text"
                    placeholder="Event Title"
                    value={newEvent.title}
                    onChange={(e) => setNewEvent({ ...newEvent, title: e.target.value })}
                    required
                />
                <textarea
                    placeholder="Event Description"
                    value={newEvent.description}
                    onChange={(e) => setNewEvent({ ...newEvent, description: e.target.value })}
                    required
                ></textarea>
                <button type="submit">Add Event</button>
            </form>
            <div className="events-list">
                {events.map(event => (
                    <div key={event.id} className="event-item">
                        <h3>{event.title}</h3>
                        <p>{event.description}</p>
                        <small>{new Date(event.createdAt).toLocaleString()}</small>
                    </div>
                ))}
            </div>
        </div>
    );
};

export default EventList;

4. Integrating the Component into the App

Update the main App.jsx to include the EventList component.

// src/App.jsx
import React from 'react';
import EventList from './components/EventList';
import './App.css';

function App() {
    return (
        <div className="App">
            <EventList />
        </div>
    );
}

export default App;

5. Styling the Application

Add basic styling to enhance the user interface.

// src/App.css
.App {
    font-family: Arial, sans-serif;
    padding: 20px;
}

.event-list-container {
    max-width: 600px;
    margin: 0 auto;
}

.event-form {
    display: flex;
    flex-direction: column;
    margin-bottom: 20px;
}

.event-form input,
.event-form textarea {
    margin-bottom: 10px;
    padding: 10px;
    font-size: 16px;
}

.event-form button {
    padding: 10px;
    font-size: 16px;
    background-color: #388278;
    color: white;
    border: none;
    cursor: pointer;
}

.event-form button:hover {
    background-color: #2a5d61;
}

.events-list .event-item {
    border: 1px solid #ccc;
    padding: 15px;
    margin-bottom: 10px;
    border-radius: 5px;
}

.events-list .event-item h3 {
    margin: 0 0 10px 0;
}

.events-list .event-item p {
    margin: 0 0 10px 0;
}

.events-list .event-item small {
    color: #666;
}

Real-Time Updates with SignalR

1. Understanding SignalR

SignalR is a library for ASP.NET that facilitates real-time bi-directional communication between server and client. It abstracts the complexities of managing connections, allowing developers to focus on the application logic.

2. Configuring SignalR in the Backend

Ensure SignalR is properly configured in the backend to handle event broadcasts.

// Program.cs (Additional Configuration)
app.MapControllers();
app.MapHub<EventHub>("/eventHub");

app.Run();

3. Handling Real-Time Events in React

The React frontend connects to the SignalR hub and listens for incoming events, updating the state accordingly.

// Inside EventList.jsx (SignalR Integration)
useEffect(() => {
    if (connection) {
        connection.start()
            .then(() => {
                console.log('Connected to SignalR hub');
                connection.on('ReceiveEvent', event => {
                    setEvents(prevEvents => [event, ...prevEvents]);
                });
            })
            .catch(err => console.error('Connection failed: ', err));
    }
}, [connection]);

4. Security Considerations

Implement authentication and authorization to secure the API endpoints and SignalR hub. Utilize JWT tokens or other authentication mechanisms to ensure that only authorized users can add or view events.

5. Error Handling and Validation

Incorporate comprehensive error handling both on the frontend and backend. Validate incoming data to prevent malformed entries and ensure data integrity.


Enhancing the Application

1. Implementing Pagination

For scalability, consider implementing pagination to handle large lists of events. This improves performance by loading events in chunks rather than all at once.

2. Adding User Authentication

Integrate user authentication to allow users to create, edit, or delete their events. This adds a layer of personalization and security.

3. Deploying the Application

Once development and testing are complete, deploy the backend to a cloud service like Azure or AWS and host the React frontend on platforms such as Netlify or Vercel.

4. Optimizing Performance

Optimize both frontend and backend for performance. Use caching strategies, optimize database queries, and utilize CDN services for static assets to ensure swift load times and a smooth user experience.


Conclusion

Creating a live-updating event list application involves integrating a React frontend with a C# Web API backend, utilizing Dapper for efficient database operations, and implementing SignalR for real-time communication. This comprehensive approach ensures a responsive and dynamic user experience, with the ability to scale and enhance functionalities as needed.


References


Last updated January 21, 2025

AI-assisted research. Sources, methodology and corrections · Source-checked guides and tools · Explore published answers

Ask Ithy AI