MSDN Documentation

Mastering Modern Web Development with .NET and JavaScript

Building a Single-Page Application (SPA) with .NET and JavaScript

This tutorial will guide you through the process of creating a modern Single-Page Application (SPA) leveraging the power of .NET for your backend and robust JavaScript frameworks for your frontend. We'll explore common architectural patterns, best practices, and provide practical code examples.

Step 1: Project Setup and Architecture

We'll start by setting up a new .NET project. For this tutorial, we'll use the ASP.NET Core Web API template. This provides a solid foundation for our backend services.


dotnet new webapi -n MySpaBackend
cd MySpaBackend
            

For the frontend, we'll use a popular JavaScript framework like React, Vue.js, or Angular. We'll assume a separate frontend project setup using its respective CLI (e.g., Create React App, Vue CLI, Angular CLI).

SPA Architecture Diagram

Conceptual SPA Architecture

Step 2: Designing the Backend API

Your .NET backend will serve as the API layer, handling data, business logic, and authentication. We'll define RESTful endpoints for common operations.

Example: Product Controller


using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using MySpaBackend.Models; // Assuming you have a Models folder

namespace MySpaBackend.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ProductsController : ControllerBase
    {
        [HttpGet]
        public ActionResult> GetProducts()
        {
            // In a real app, this would fetch from a database
            var products = new List
            {
                new Product { Id = 1, Name = "Laptop", Price = 1200.50m },
                new Product { Id = 2, Name = "Keyboard", Price = 75.00m },
                new Product { Id = 3, Name = "Mouse", Price = 25.99m }
            };
            return Ok(products);
        }

        [HttpGet("{id}")]
        public ActionResult GetProduct(int id)
        {
            // Find product logic
            var product = new Product { Id = id, Name = "Example Product", Price = 99.99m };
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }

        // Add POST, PUT, DELETE methods as needed
    }
}
            

And the corresponding model:


namespace MySpaBackend.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}
            

Step 3: Frontend Data Fetching and State Management

On the frontend, we'll use JavaScript (or TypeScript) to consume the .NET API. Libraries like Axios or the built-in Fetch API are excellent choices for making HTTP requests.

Example: Fetching Products with React (using Fetch API)


import React, { useState, useEffect } from 'react';

function ProductList() {
    const [products, setProducts] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        async function fetchProducts() {
            try {
                const response = await fetch('/api/products'); // Assuming API is proxied or accessible
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                const data = await response.json();
                setProducts(data);
            } catch (error) {
                setError(error);
                console.error("Error fetching products:", error);
            } finally {
                setLoading(false);
            }
        }
        fetchProducts();
    }, []);

    if (loading) {
        return <p>Loading products...</p>;
    }

    if (error) {
        return <p>Error loading products: {error.message}</p>;
    }

    return (
        <ul>
            {products.map(product => (
                <li key={product.id}>
                    {product.name} - ${product.price.toFixed(2)}
                </li>
            ))}
        </ul>
    );
}

export default ProductList;
            

For more complex applications, consider state management libraries like Redux, Zustand (for React), Vuex (for Vue), or NgRx (for Angular).

Step 4: Routing and Navigation

SPAs rely heavily on client-side routing to provide a seamless user experience without full page reloads. Each JavaScript framework has its preferred routing library.

Example: Basic Routing with React Router


import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
import ProductList from './components/ProductList';
import ProductDetail from './components/ProductDetail'; // Assuming you have this component

function App() {
    return (
        <Router>
            <nav>
                <ul>
                    <li><Link to="/">Home</Link></li>
                    <li><Link to="/products">Products</Link></li>
                </ul>
            </nav>
            <Switch>
                <Route path="/products/:id" component={ProductDetail} />
                <Route path="/products" component={ProductList} />
                <Route path="/" exact render={() => <h2>Welcome to the SPA!</h2>} />
            </Switch>
        </Router>
    );
}

export default App;
            

Step 5: Deployment Considerations

For deployment, you have several options:

Remember to configure CORS (Cross-Origin Resource Sharing) in your .NET backend if your frontend is hosted on a different domain or port during development or in production.

Further Learning

Building SPAs with .NET and JavaScript offers a powerful and flexible way to create modern, dynamic web experiences. By following these steps and best practices, you can build robust and scalable applications.