REST API for the RecipeNest platform — handles authentication, chef profiles, and recipe management.
Built with ASP.NET Core 10, Entity Framework Core 9, and PostgreSQL (hosted on NeonDB).
- Tech Stack
- Project Structure
- Getting Started
- Environment Variables
- API Reference
- Database Schema
- Authentication
- Deployment
| Concern | Library / Version |
|---|---|
| Framework | ASP.NET Core 10 |
| ORM | Entity Framework Core 9.0.4 |
| Database driver | Npgsql.EntityFrameworkCore.PostgreSQL 9.0.4 |
| Auth | Microsoft.AspNetCore.Authentication.JwtBearer 9.0.4 |
| Password hashing | BCrypt.Net-Next 4.0.3 |
| Container | Docker (multi-stage, mcr.microsoft.com/dotnet/sdk:10.0) |
RecipeNest.API/
├── Controllers/
│ ├── AuthController.cs # POST /auth/register, POST /auth/login
│ ├── ChefsController.cs # GET /chefs, GET /chefs/{id}
│ ├── RecipesController.cs # Full CRUD /recipes
│ └── ProfileController.cs # GET/PUT /profile
├── Models/
│ ├── User.cs # Auth entity (email + password hash)
│ ├── ChefProfile.cs # Public chef info (1-to-1 with User)
│ └── Recipe.cs # Recipe (many-to-1 with ChefProfile)
├── DTOs/
│ ├── AuthDTOs.cs # RegisterDto, LoginDto, AuthResponseDto
│ ├── ChefDTOs.cs # ChefListDto, ChefDetailDto, UpdateProfileDto
│ └── RecipeDTOs.cs # RecipeDto, CreateRecipeDto, UpdateRecipeDto
├── Data/
│ └── AppDbContext.cs # EF Core context + relationships
├── Migrations/ # EF Core schema history (auto-applied on startup)
├── Dockerfile # Multi-stage Docker build for Render
├── Program.cs # DI, JWT, CORS, EF, PORT binding
└── appsettings.json # Base config (override via env vars in prod)
cd RecipeNest.API
dotnet restore
dotnet runThe API starts at http://localhost:5000.
EF Core migrations are applied automatically on startup — tables are created on first run.
Configuration is read from appsettings.json and overridden by environment variables.
In production (Render), set these as secret environment variables:
| Variable | Description | Example |
|---|---|---|
ConnectionStrings__DefaultConnection |
Full NeonDB / PostgreSQL connection string | Host=...;Database=neondb;Username=...;Password=...;SSL Mode=Require;Trust Server Certificate=true; |
Jwt__Key |
Random secret string, minimum 32 characters | MyApp_SuperSecret_Key_ChangeThis! |
AllowedOrigins |
Comma-separated list of allowed frontend origins | https://recipenest.vercel.app |
PORT |
Set automatically by Render — do not set manually | 8080 |
appsettings.jsonalready contains safe defaults for local development.
All endpoints are prefixed with /api.
| Method | Endpoint | Auth | Body | Description |
|---|---|---|---|---|
POST |
/auth/register |
No | { name, email, password, bio?, avatarUrl? } |
Create account + chef profile |
POST |
/auth/login |
No | { email, password } |
Returns JWT token |
Login response:
{
"token": "<jwt>",
"chefId": 1,
"name": "Gordon Ramsay",
"email": "gordon@example.com",
"avatarUrl": "/avatars/chef1.png"
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/chefs |
No | List all chefs with recipe count |
GET |
/chefs/{id} |
No | Chef profile + all their recipes |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/recipes |
No | All recipes (newest first) |
GET |
/recipes/{id} |
No | Single recipe detail |
GET |
/recipes/chef/{chefId} |
No | Recipes by a specific chef |
POST |
/recipes |
Yes | Create a recipe |
PUT |
/recipes/{id} |
Yes | Update own recipe |
DELETE |
/recipes/{id} |
Yes | Delete own recipe |
Recipe body (create / update):
{
"title": "Beef Wellington",
"description": "Classic British dish...",
"ingredients": "Beef tenderloin, puff pastry...",
"instructions": "1. Sear the beef...",
"imageUrl": "https://example.com/image.jpg"
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/profile |
Yes | Get own chef profile |
PUT |
/profile |
Yes | Update name, bio, avatarUrl |
User (1) ──────── (1) ChefProfile (1) ──────── (Many) Recipe
| Table | Key columns |
|---|---|
Users |
Id, Email, PasswordHash |
ChefProfiles |
Id, UserId (FK), Name, Bio, AvatarUrl |
Recipes |
Id, ChefId (FK), Title, Description, Ingredients, Instructions, ImageUrl, CreatedAt |
Cascade delete is configured: removing a User deletes their ChefProfile and all associated Recipe rows.
- Client
POST /api/auth/loginwith email + password - Server verifies the BCrypt hash and issues a signed JWT (7-day expiry)
- JWT claims:
userId,email,chefId - Client stores the token in
localStorageand sends it asAuthorization: Bearer <token>on protected requests - ASP.NET Core's
[Authorize]attribute validates the token on every protected endpoint
The API is deployed to Render using Docker.
- Push the repo to GitHub
- Create a Web Service on Render, connect the repo, and select the
recipenest-apiservice fromrender.yaml - Add the secret environment variables listed above in the Render dashboard
- Deploy — Render builds the Docker image and starts the container
The Dockerfile uses a multi-stage build:
- Build stage —
mcr.microsoft.com/dotnet/sdk:10.0compiles and publishes the app - Runtime stage —
mcr.microsoft.com/dotnet/aspnet:10.0runs the published output (smaller final image)