Skip to content

Repository files navigation

ApiHotelProject

ApiHotelProject is a hotel management system built on ASP.NET Core: a layered Web API backend, an ASP.NET Core MVC front end with a public site and an admin dashboard, and SQL Server for storage.

Live demo: https://hotel-demo-453942543225.europe-west1.run.app Demo account: demo / Demo.2026!

The demo runs on free tiers. The database is Azure SQL serverless and pauses when idle, so the very first request after a quiet period can take 30-60 seconds. After that it is fast. Files uploaded through the admin panel are not persisted, because the container filesystem is ephemeral.

Solution Structure

  • ApiConsume: the backend.

    • Entity Layer: entity models representing the core domain objects (AppUser, Booking, Room, Staff, Testimonial, Service, ...) and the relationships between them.
    • Data Access Layer: database access through Entity Framework Core. Holds Context, the migrations, and the repository implementations.
    • DTO Layer: data transfer objects used between layers, so entities are not exposed directly at the API boundary.
    • Business Layer: service classes that orchestrate business operations and keep the rules out of the controllers.
    • Web API: RESTful endpoints for every resource. Documented with Swagger.
  • Frontend:

    • Web UI: the ASP.NET Core MVC application. The "Default" part is the public hotel site (rooms, services, team, testimonials, booking and contact forms); the "Admin" part is the dashboard for managing users, bookings, rooms, staff and messages.

Running locally

Requires the .NET 10 SDK and a SQL Server instance.

  1. Point the app at a database. Copy .env.example to .env and fill in your connection string, or export it in your shell:

    export HOTELDB_CONNECTION="Server=tcp:YOUR-SERVER.database.windows.net,1433;Initial Catalog=ApiDb;User ID=YOUR-USER;Password=YOUR-PASSWORD;Encrypt=True;Connection Timeout=60;"

    The schema and a set of demo records (rooms, staff, services, testimonials, bookings and the demo admin account) are created automatically the first time the Web UI starts.

  2. Start both projects. In VS Code press F5 and pick Run everything (API + Web UI), or use two terminals:

    # Terminal 1 - Web API
    cd ApiConsume/HotelProject.WebApi
    ASPNETCORE_ENVIRONMENT=Development ASPNETCORE_URLS=http://localhost:11888 dotnet run
    
    # Terminal 2 - Web UI
    cd Frontend/HotelProject.WebUI
    ASPNETCORE_ENVIRONMENT=Development ASPNETCORE_URLS=http://localhost:5050 dotnet run

    The site runs on 5050 rather than the usual 5000 because macOS gives port 5000 to the AirPlay Receiver.

  3. Open the app:

    URL What you get
    http://localhost:5050/ Public hotel site
    http://localhost:5050/Login/Index Admin login (demo / Demo.2026!)
    http://localhost:5050/Dashboard/Index Admin dashboard
    http://localhost:11888/swagger API documentation

No local SQL Server?

SQL Server has no native Apple Silicon build, but the official image runs under Rosetta:

docker run -d --platform linux/amd64 --name hotel-sql \
  -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=LocalDev.2026!" -e "MSSQL_PID=Developer" \
  -p 1433:1433 mcr.microsoft.com/mssql/server:2022-latest

Then use Server=localhost,1433;Initial Catalog=ApiDb;User ID=sa;Password=LocalDev.2026!;Encrypt=True;TrustServerCertificate=True;

Running the container

Both applications ship in a single image. The Web API binds to loopback inside the container and is never exposed publicly; the MVC front end talks to it over 127.0.0.1 and is the only thing listening on the public port.

docker build -t hotel-demo .
docker run -p 8080:8080 -e "HOTELDB_CONNECTION=..." hotel-demo

Deploying to Google Cloud Run

gcloud run deploy hotel-demo \
  --source . \
  --region europe-west1 \
  --allow-unauthenticated \
  --memory 1Gi --cpu 1 \
  --min-instances 0 --max-instances 1 \
  --timeout 120 \
  --set-secrets "HOTELDB_CONNECTION=hoteldb-connection:latest"

ApiSettings__BaseUrl and ASPNETCORE_ENVIRONMENT already have production defaults baked into the image. The connection string is read from Secret Manager and never stored in the repository.

Configuration

Setting Where Purpose
HOTELDB_CONNECTION environment variable SQL Server connection string. Required.
ApiSettings:BaseUrl appsettings.json / ApiSettings__BaseUrl Base address of the Web API. Defaults to http://localhost:11888/.
EmailConfig:Email, EmailConfig:Password user secrets / environment SMTP credentials for the admin mail page. Optional; the page explains itself when they are missing.

Technologies Used

  • .NET 10 (LTS) - runtime and SDK for every project
  • ASP.NET Core Web API - the backend
  • ASP.NET Core MVC - the front end, with view components for reusable sections
  • Entity Framework Core 10 - data access, migrations and automatic schema creation
  • MS SQL Server / Azure SQL Database - storage
  • ASP.NET Core Identity - authentication, roles and role assignment
  • AutoMapper - entity/DTO mapping
  • FluentValidation - input validation
  • Swagger (Swashbuckle) - API documentation
  • MailKit - SMTP mail
  • Docker + Google Cloud Run - containerised deployment
  • Bootstrap 5 - styling

Development Notes

  • The solution follows an N-tier architecture; the DTO layer keeps entities away from the API surface.
  • The front end never talks to the database for domain data. Every read and write goes through the Web API using a named HttpClient whose base address comes from configuration.
  • Public pages degrade gracefully: if the API is unavailable the sections render empty instead of returning an error page.
  • Data Protection keys are stored in the database, so login sessions survive container restarts.
  • RapidApi and JwtProject, two scratch projects used for experimenting with third-party APIs and JWT, were removed from this repository; they were unrelated to the hotel application.