Edward's Tech Site

this site made with Next.js 13, see the code

FORAY: Feb 10 - C#, Vue.js
Set up site in one directory, C# backend with Vue.js frontend

DOING: add another route...

  • research
  • links
  • build frontend
    • create Vue site
      • go to your projects directory
      • npm create vite vue-csharp-fullstack-showcase-001
      • Vue
      • Customize with create-vue
      • choose the following:
    • test
      • code vue-csharp-fullstack-showcase-001
      • npm i
      • npm run dev
      • stop the site: CTRL-C
  • build backend
    • cd vue-csharp-fullstack-showcase-001
    • check .NET version
      • dotnet --version
      • 8.0.302 (2025-02-10)
    • create API
      • dotnet new webapi -n backend
    • test it
  • set up script to start both frontend and backend with one command
    • install needed tools
      • npm i -D concurrently
      • npm i -D nodemon
    • backend/run-api.sh
      • #!/bin/bash
         
        # Set the desired URL
        URL="http://localhost:5200"
         
        # Run the .NET application with the specified URL
        dotnet run --urls "$URL"
    • package.json
      • "scripts": {
        "dev": "conc \"npm run api\" \"npm run frontend\"",
        "api": "nodemon",
        "frontend": "vite --port 5100 --open",
        "build": "run-p type-check \"build-only {@}\" --",
        "preview": "vite preview",
        "build-only": "vite build",
        "type-check": "vue-tsc --build",
        "lint": "eslint . --fix",
        "format": "prettier --write src/"
        },
        "nodemonConfig": {
        "watch": [
        "backend"
        ],
        "ext": "cs",
        "exec": "cd backend && bash ./run-api.sh"
        },
  • .. get frontend to read data from backend API
    • Program.cs
      • var builder = WebApplication.CreateBuilder(args);
         
        builder.Services.AddEndpointsApiExplorer();
        builder.Services.AddSwaggerGen();
        builder.Services.AddCors(options =>
        {
        options.AddPolicy("AllowAll",
        policy => policy.AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader());
        });
         
         
        var app = builder.Build();
         
        if (app.Environment.IsDevelopment())
        {
        app.UseSwagger();
        app.UseSwaggerUI();
        }
        app.UseCors("AllowAll");
        app.UseHttpsRedirection();
         
        var summaries = new[]
        {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
         
        app.MapGet("/weatherforecast", () =>
        {
        var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
        DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
        Random.Shared.Next(-20, 55),
        summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
        return forecast;
        })
        .WithName("GetWeatherForecast")
        .WithOpenApi();
         
        app.Run();
         
        record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
        {
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
        }
    • App.vue
      • <script setup lang="ts">
        import { ref, onMounted } from 'vue'
         
        interface WeatherData {
        date: string
        temperatureC: number
        temperatureF: number
        summary: string
        }
         
        const weatherData = ref<WeatherData[]>([])
         
        const fetchWeather = async () => {
        try {
        const response = await fetch('http://localhost:5200/weatherforecast')
        if (!response.ok) throw new Error('Failed to fetch weather data')
         
        weatherData.value = await response.json()
        } catch (error) {
        console.error('Error fetching weather data:', error)
        }
        }
         
        onMounted(fetchWeather)
        </script>
         
        <template>
        <div>
        <h2>Weather Forecast</h2>
        <ul v-if="weatherData.length">
        <li v-for="weather in weatherData" :key="weather.date">
        <strong>{{ weather.date }}</strong> -
        {{ weather.temperatureC }}°C / {{ weather.temperatureF }}°F -
        <em>{{ weather.summary }}</em>
        </li>
        </ul>
        <p v-else>Loading weather data...</p>
        </div>
        </template>
         
        <style scoped>
        h2 {
        margin-bottom: 10px;
        }
        ul {
        list-style: none;
        padding: 0;
        }
        li {
        padding: 5px 0;
        }
        </style>
    • it displays the data
  • .. add another route