In modern software development, APIs (Application Programming Interfaces) are the backbone of communication between services and applications. Choosing the right API style can greatly influence performance, scalability, and maintainability. In this blog, we will explore four popular API technologies—REST, GraphQL, and gRPC—and how to implement them in C#.
Full C# Solution: REST, GraphQL, and gRPC
We will create a single solution with multiple projects, each demonstrating a different API style.
Step 1: Create a Solution
Open Visual Studio or CLI, then create a solution:
mkdir ApiDemoSolution
cd ApiDemoSolution
dotnet new sln -n ApiDemoSolutionThis creates a solution called ApiDemoSolution.
Step 2: Create Shared Models Project
All APIs will use a shared project for models and services.
dotnet new classlib -n ApiDemo.Shared
Step 3: Install EF Core for PostgreSQL
For all projects that use IProductRepository (Shared or REST API), install the necessary EF Core packages:
cd ApiDemo.Shared
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Microsoft.EntityFrameworkCore.DesignStep 2: Update Shared Project
Models/Product.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Product
{
[Key]
public Guid Id { get; set; } = Guid.NewGuid();
[Required(ErrorMessage = "Product name is required.")]
[MaxLength(100, ErrorMessage = "Product name cannot exceed 100 characters.")]
public string Name { get; set; } = string.Empty;
[MaxLength(500, ErrorMessage = "Description cannot exceed 500 characters.")]
public string Description { get; set; } = string.Empty;
[Range(0, double.MaxValue, ErrorMessage = "Price must be a non-negative value.")]
[Column(TypeName = "decimal(18,2)")] // Ensures proper precision in database
public decimal Price { get; set; }
[Range(0, int.MaxValue, ErrorMessage = "Stock cannot be negative.")]
public int Stock { get; set; }
// Domain Logic
public void ReduceStock(int quantity)
{
if (quantity > Stock)
throw new InvalidOperationException("Insufficient stock.");
Stock -= quantity;
}
}Data/AppDbContext.cs
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
namespace ApiDemo.Shared.Data;
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }
public DbSet<Product> Products { get; set; }
}Repositories/IProductRepository.cs
namespace ApiDemo.Shared.Repositories;
public interface IProductRepository
{
Task<Product?> GetByIdAsync(Guid id);
Task<IEnumerable<Product>> GetAllAsync();
Task AddAsync(Product product);
Task UpdateAsync(Product product);
Task DeleteAsync(Guid id);
}Repositories/ProductRepository.cs
using ApiDemo.Shared.Data;
using Microsoft.EntityFrameworkCore;
namespace ApiDemo.Shared.Repositories;
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _db;
public ProductRepository(AppDbContext db)
{
_db = db;
}
public async Task AddAsync(Product product)
{
product.Id = Guid.NewGuid();
await _db.Products.AddAsync(product);
await _db.SaveChangesAsync();
}
public async Task DeleteAsync(Guid id)
{
var product = await _db.Products.FindAsync(id);
if (product != null)
{
_db.Products.Remove(product);
await _db.SaveChangesAsync();
}
}
public async Task<IEnumerable<Product>> GetAllAsync() => await _db.Products.ToListAsync();
public async Task<Product?> GetByIdAsync(Guid id) => await _db.Products.FindAsync(id);
public async Task UpdateAsync(Product product)
{
_db.Products.Update(product);
await _db.SaveChangesAsync();
}
}
Create REST API Project
dotnet new webapi -n ApiDemo.Rest
dotnet sln add ApiDemo.Rest/ApiDemo.Rest.csproj
Install Swagger
dotnet add ECommerce.API package Swashbuckle.AspNetCore
Update Program.cs
using ApiDemo.Shared.Data;
using ApiDemo.Shared.Repositories;
using Microsoft.EntityFrameworkCore;
using System;
var builder = WebApplication.CreateBuilder(args);
// Configure PostgreSQL
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly("ApiDemo.Rest")));
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.MapControllers();
app.Run();Upgrade All Packages for .net 10
dotnet add "ApiDemo.Shared/ApiDemo.Shared.csproj" package Microsoft.EntityFrameworkCore.Design --version 10.0.1
dotnet add "ApiDemo.Shared/ApiDemo.Shared.csproj" package Npgsql.EntityFrameworkCore.PostgreSQL --version 10.0.1
dotnet add "ApiDemo.Rest/ApiDemo.Rest.csproj" package Npgsql.EntityFrameworkCore.PostgreSQL --version 10.0.1
dotnet add "ApiDemo.Rest/ApiDemo.Rest.csproj" package Microsoft.EntityFrameworkCore.Design --version 10.0.1Create Migrrations
dotnet ef migrations add InitialCreate -p ApiDemo.Rest -s ApiDemo.Rest
Update Database

Optional: Seed Database (Optional)
You can seed some data in Program.cs:
using var scope = app.Services.CreateScope();
var repo = scope.ServiceProvider.GetRequiredService<IProductRepository>();
if (!(await repo.GetAllAsync()).Any())
{
await repo.AddAsync(new Product { Name = "Test Product 1", Price = 10.5 });
await repo.AddAsync(new Product { Name = "Test Product 2", Price = 25.0 });
}Create Controller for Rest
using ApiDemo.Shared.Repositories;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace ApiDemo.Rest.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductController : ControllerBase
{
private readonly IProductRepository _repository;
public ProductController(IProductRepository repository)
{
_repository = repository;
}
// GET: api/product
[HttpGet]
public async Task<IActionResult> GetAll()
{
var products = await _repository.GetAllAsync();
return Ok(products);
}
// GET: api/product/{id}
[HttpGet("{id:guid}")]
public async Task<IActionResult> GetById(Guid id)
{
var product = await _repository.GetByIdAsync(id);
if (product == null)
return NotFound();
return Ok(product);
}
// POST: api/product
[HttpPost]
public async Task<IActionResult> Create([FromBody] Product product)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
await _repository.AddAsync(product);
return CreatedAtAction(nameof(GetById),
new { id = product.Id }, product);
}
// PUT: api/product/{id}
[HttpPut("{id:guid}")]
public async Task<IActionResult> Update(Guid id, [FromBody] Product product)
{
if (id != product.Id)
return BadRequest("ID mismatch.");
var existing = await _repository.GetByIdAsync(id);
if (existing == null)
return NotFound();
await _repository.UpdateAsync(product);
return NoContent();
}
// DELETE: api/product/{id}
[HttpDelete("{id:guid}")]
public async Task<IActionResult> Delete(Guid id)
{
var existing = await _repository.GetByIdAsync(id);
if (existing == null)
return NotFound();
await _repository.DeleteAsync(id);
return NoContent();
}
}
}Lets Test Rest API












Join the conversation! Your thoughts help the community grow.