🔍 What Is an API?
"Think of an API as a waiter in a restaurant — it takes your request, communicates
it to the kitchen, and brings your food back. You don't need to know how the kitchen
works — just how to place the order correctly."
An API (Application Programming Interface) is a communication contract between two
systems — often between a client (like your Flutter app) and a server (like a .NET backend).
APIs define what data can be accessed, how it should be requested, and what structure
the response will have.
In modern web and mobile development, APIs are the backbone of everything:
● Your mobile app’s login screen? It calls an auth API.
● That product list screen? It’s powered by a product API.
● Payment, notifications, and delivery tracking? All APIs.
But there’s more to APIs than just calling GET /products.
Well-designed APIs are secure, scalable, and developer-friendly. They require thoughtful
planning, consistent structure, and clear error handling.
APIs come in different styles depending on how data is exposed, consumed, and
communicated. Here are the main types you’ll encounter as a mobile/web developer or team
lead:
1. 🔁 REST API (Representational State Transfer)
The most common type — used in nearly every mobile app today.
● Communicates over HTTP
● Uses standard HTTP verbs (GET, POST, PUT, DELETE)
● Returns JSON (usually)
● Stateless — each request is independent
✅ Easy to use
✅ Human-readable
✅ Great for CRUD apps
📦 Example:
GET /api/users/123
2. 🧠 GraphQL API
Developed by Facebook. Flexible, powerful, but more complex.
● Uses a single endpoint (/graphql)
● Client defines exact data shape it needs
● Often used when frontend wants fine-grained control
✅ Avoids over-fetching/under-fetching
✅ Great for mobile apps needing optimization
📦 Example query:
query {
user(id: "123") {
name
email
posts {
title
3. ⚙️ gRPC (Google Remote Procedure Call)
High-performance, binary-based protocol — ideal for microservices.
● Uses Protocol Buffers (not JSON)
● Very fast, but not human-readable
● Requires code generation from .proto files
✅ High-speed
✅ Good for internal microservices communication
❌ Not ideal for browser clients
4. 🔐 SOAP API (Simple Object Access Protocol)
Older, XML-based protocol — mostly used in enterprise or legacy systems.
● Strict, heavy-weight
● XML formatted messages
● Built-in standards for security, transactions, and reliability
✅ Still used in banking, government, insurance systems
❌ Overkill for most modern apps
5. 🔄 Webhooks (Event-based callbacks)
Not an API to call — an API that calls you.
● Your server exposes an endpoint
● Third-party service sends data to you when an event occurs
📦 Example:
● Stripe calls your /payment/received endpoint when a user pays
● GitHub calls /deploy when a push is made
✅ Great for real-time integrations
✅ Common in payments, analytics, CI/CD
API Type Protocol Data Format Use Case
REST HTTP JSON Standard CRUD and mobile APIs
GraphQL HTTP JSON Flexible frontend querying
gRPC HTTP/2 Protobuf Fast microservices
SOAP HTTP/XML XML Legacy enterprise systems
Webhooks HTTP JSON/XML Push data on events
🔍 In this article, we’ll explore:
1. 🎯 API Design Goals – what makes an API reliable, scalable, and developer-friendly
2. 🔁 HTTP Methods – understanding how RESTful APIs use verbs like GET, POST, and
DELETE
3. 📦 Response Structure – how to format clean, consistent JSON responses
4. 🚦 HTTP Status Codes – the meaning behind 200, 400, 401, and other common
codes
5. 🧱 Popular API Architectures – from Layered to Clean Architecture and Microservices
6. 🔄 API Request Flow (N-Tier) – how a request moves through middleware, controllers,
services, and repositories
7. 🗃 ORM (Object-Relational Mapping) – using tools like Entity Framework Core to
connect code with databases
🎯 API Design Goals – What Makes a Great API?
Designing an API is like designing a public-facing product — it’s not just about functionality, but
clarity, predictability, and scalability. Whether you're building internal APIs for your mobile
team or exposing APIs to external consumers, these core goals will keep your architecture
clean and your team aligned.
✅ 1. Clarity
“If someone can’t guess how your API works by looking at its URL and response —
it’s not clear enough.”
Your endpoints, parameters, and responses should be:
● Easy to understand
● Named consistently
● Self-explanatory
Bad: GET /fetchAllDataFromDb2024
Good: GET /api/users
✅ 2. Consistency
APIs should follow the same patterns across endpoints, HTTP verbs, and
response formats.
● Use GET to retrieve, POST to create, PUT to update, DELETE to remove.
● Stick to a common response structure (e.g., status, message, data)
● Errors should always follow a consistent error format
✅ This makes your API easy to learn, test, and document.
✅ 3. Security
A great API protects data and access — not just exposes it.
● Use JWT or OAuth2 for authentication
● Apply role-based access control
● Never leak internal DB structure or stack traces
● Sanitize input and validate everything
Even internal APIs should be secured — don’t rely on “it’s just on our network.”
✅ 4. Scalability
Can your API handle 10x more users or requests with minimal changes?
● Use pagination for lists
● Avoid expensive joins or heavy payloads
● Consider caching (e.g., Redis) for frequent data
● Break down responsibilities across modules or even microservices
✅ Think long-term: what works for 10 users might break for 10,000.
✅ 5. Performance
APIs should respond fast and fail fast.
● Use async/await for non-blocking operations
● Minimize payloads — return only what the client needs
● Monitor response times and database call durations
📉 Slow APIs kill user experience — especially in mobile apps.
✅ 6. Versioning & Evolution
APIs change. Good design plans for that without breaking existing apps.
● Use versioning in URLs: /api/v1/products
● Don’t remove fields — mark them deprecated
● Allow old and new clients to work side by side
✅ This protects you from breaking production users every time you deploy a new feature.
✅ 7. Developer Experience (DX)
APIs are developer products — treat them with care.
● Document every endpoint (Swagger/OpenAPI)
● Use meaningful error messages
● Support test environments
● Make it easy to onboard new devs
Think of your API as part of your brand — whether internal or public.
🧠 Final Thought
A well-designed API feels predictable, safe, and scalable. As a team lead, you’ll be
responsible for reviewing endpoints and setting standards that help your team move faster and
build more reliable systems.
🔁 HTTP Methods – The Language of RESTful APIs
“If your API is a conversation between systems, HTTP methods are the verbs that
describe the action being taken.”
These methods define what operation the API should perform. Choosing the correct method
ensures your API is intuitive and follows industry standards.
🧠 Summary Table
Method Action Use Case Example Notes
GET Read Fetch products, user profiles Should not modify data
POST Create Submit forms, register users Not idempotent
PUT Update (Full) Replace an entire record Sends full object
PATCH Update (Partial) Modify a few fields Sends only updated fields
DELETE Delete Remove a user, cancel an order Should be irreversible
📦 API Response Structure – Speak a Clear, Consistent
Language
“The backend should always respond in a predictable format — success or error.”
Whether you're returning user data, a list of products, or a validation error, the response format
should be the same in every case. This makes your API easy to consume and debug —
especially in Flutter or frontend clients.
✅ 1. The Ideal Standard Response Format
Json
{
"status": true,
"message": "User created successfully",
"data": {
"id": 1,
"name": "Mostafa",
"email": "mostafa@example.com"
}
}
🔍 Explanation:
● status: true or false — indicates success or failure
● message: A human-readable message for logs or UI
● data: Actual payload returned from the API
❌ Example: Bad Response
json
{
"result": "OK",
"userData": "SUCCESS",
"1": "Data found"
}
This kind of response:
● Is hard to parse
● Has no standard structure
● Breaks client-side abstraction
❌ 2. The Standard Error Format
json
{
"status": false,
"message": "Validation failed",
"errors": {
"email": "Email is required",
"password": "Password must be at least 8 characters"
}
}
🔍 Why it's powerful:
● Your Flutter app can show specific field errors automatically
● Backend devs can debug without guessing
● You don’t break the contract even when failing
🧪 3. Special Cases
Empty Data (List):
json
{
"status": true,
"message": "No items found",
"data": []
}
Pagination:
json
{
"status": true,
"message": "Success",
"data": {
"items": [...],
"pagination": {
"page": 1,
"pageSize": 10,
"totalPages": 5,
"totalItems": 47
}
}
}
🔁 4. Structure at a Glance
Field Type Description
status Boolean Success (true) or failure (false)
message String Human-readable message for logs/UI
data Object Payload for success responses
errors Object Field-level validation errors (if any)
🚦 HTTP Status Codes – Communicate with Clients the
Right Way
“Think of status codes as the tone of your API's response — they tell the client how
to interpret the response, even before reading the body.”
Every HTTP response must return a status code that accurately describes what happened —
whether it’s success, failure, or something in between.
✅ 1. 200 OK
“Everything worked perfectly.”
Used for:
● GET requests (retrieving data)
● Successful PATCH or PUT requests
📦 Example:
http
GET /api/users/7 → 200 OK
✅ 201 Created
“A new resource was successfully created.”
Used for:
● Successful POST requests (e.g., user signup, order creation)
📦 Example:
http
CopyEdit
POST /api/users → 201 Created
📝 Bonus: Include the new resource in the response:
json
{
"status": true,
"message": "User created successfully",
"data": { "id": 1, "email": "mostafa@example.com" }
}
✅ 204 No Content
“The action succeeded, but there’s nothing to return.”
Used for:
● DELETE requests
● Some successful PUT/PATCH where no response body is needed
📦 Example:
DELETE /api/users/7 → 204 No Content
❌ 400 Bad Request
“The request was malformed or invalid.”
Used when:
● Required fields are missing
● Invalid values are sent (e.g., email not an email)
📦 Example:
json
{
"status": false,
"message": "Validation failed",
"errors": {
"email": "Invalid format"
}
}
🔒 401 Unauthorized
“You are not authenticated.”
Used when:
● No token is provided
● Token is invalid or expired
📦 Response:
json
{
"status": false,
"message": "Unauthorized"
}
✅ Trigger login screen in mobile/web client.
🛑 403 Forbidden
“You are authenticated, but not allowed.”
Used when:
● User is logged in, but lacks permission (e.g., role check fails)
📦 Example:
json
{
"status": false,
"message": "You do not have access to this resource."
}
❓ 404 Not Found
“The resource doesn’t exist.”
Used when:
● Requested ID doesn’t exist in DB
● Wrong URL
📦 Example:
http
GET /api/users/9999 → 404 Not Found
💥 500 Internal Server Error
“Something broke on the server side.”
Use this for:
● Unhandled exceptions
● Crashes in the controller/service/repo layer
✅ Should be caught and formatted by error-handling middleware
🧠 Status Code Cheat Sheet
Code Meaning When to Use
200 OK Successful GET/PUT/PATCH
201 Created Successful POST
204 No Content Successful DELETE
400 Bad Request Validation or format errors
401 Unauthorized No/invalid JWT token
403 Forbidden Logged in but no permission
404 Not Found Resource does not exist
500 Internal Server Error Uncaught backend exception
🧱 Popular API Architecture Patterns – Choosing the
Right Structure
“Architecture isn’t just about where files go — it’s about creating a system that is
maintainable, testable, and scalable.”
Here are the most common API architecture styles you'll see in real-world .NET (or any
backend) projects:
1. 🧊 Layered Architecture (N-tier)
Also known as “3-tier” or “classic” architecture
📦 Layers:
Controller → Service → Repository → Database
✅ When to Use:
● Small to medium CRUD apps
● Simple projects with 1–2 developers
🔧 Pros:
● Easy to learn and apply
● Clear separation of responsibilities
❌ Cons:
● Layers can become tightly coupled over time
● Doesn’t scale well for large teams or systems
2. 🧅 Clean Architecture (Onion Architecture)
Designed by Uncle Bob (Robert C. Martin)
🧱 Structure:
css
[ Domain (Entities) ]
→ [ Application (Use Cases) ]
→ [ Infrastructure (DB, APIs) ]
→ [ UI (Controllers, CLI, etc.) ]
✅ When to Use:
● Large systems
● Apps needing strong testability and separation
🔧 Pros:
● Business logic is completely independent of frameworks
● Easy to test core logic without touching DB or HTTP
● Scalable, modular, clean
❌ Cons:
● More complex and verbose
● Requires discipline and experience
3. 🗂 Modular Monolith
A monolith where features are split into isolated modules
🗃 Structure:
markdown
/Modules
/Users
UsersController.cs
UserService.cs
UserRepository.cs
/Orders
OrdersController.cs
...
✅ When to Use:
● Mid-to-large apps with multiple teams or domains
● Projects that may eventually migrate to microservices
🔧 Pros:
● Clear domain boundaries
● Faster development than microservices
● Prepares your app for easy service separation
❌ Cons:
● Shared resources can still introduce coupling
● Needs team discipline to isolate modules
4. 🧬 Microservices Architecture
Split the system into independent services, each with its own DB
🛠️ Example:
● AuthService → handles login/JWT
● OrderService → manages orders
● ProductService → manages product catalog
✅ When to Use:
● Large, enterprise-scale systems
● Teams working on completely separate domains
● Need for scaling specific parts independently
🔧 Pros:
● Services are fully independent
● Teams can work in parallel
● Scales well horizontally
❌ Cons:
● Complex to set up and maintain
● Requires infrastructure: API Gateway, service discovery, CI/CD, logging
● Higher operational overhead
🔁 Architecture Comparison Table
Architecture Scale Pros Cons
Layered Small → Medium Simple, fast to build Hard to scale, tight coupling
Clean Medium → Large Testable, clean logic Verbose, more setup
Architecture
Modular Mid-large Scalable within one app Shared codebase, modular
Monolith complexity
Microservices Large / Enterprise Full isolation, scales Complex infra, hard to debug
horizontally
🔄 N-Tier API Request Flow – Step-by-Step
“An N-tier architecture separates responsibilities into distinct layers — making it
easier to build, scale, and maintain APIs.”
Let’s simulate a real-world example:
📲 Client sends a request to GET /api/users/7
Here’s what happens behind the scenes:
🧩 1. Client Sends the Request
● Your mobile/Web app sends:
GET /api/users/7
Authorization: Bearer <token>
🛠 2. Program.cs Initializes the App
● This is the entry point of the .NET application.
● It sets up:
○ Dependency injection (services.AddScoped<IUserService,
UserService>())
○ Middleware (UseAuthentication, UseAuthorization)
○ Routing (MapControllers())
🧱 3. Middleware Runs First
● Middleware intercepts the request before it hits your controller.
● Examples:
○ 🔐 JWT token validation
○ ❌ Global error handler
○ 🧾 Logging middleware
If the JWT is invalid → 401 is returned here (not the controller).
🚪 4. Controller Handles the Endpoint
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _service;
public UsersController(IUserService service) => _service =
service;
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var user = await _service.GetUserByIdAsync(id);
if (user == null) return NotFound();
return Ok(user); // wraps it in 200 OK response
}
}
✅ Thin controller — delegates logic to the service
🧠 5. Service Layer Contains Business Logic
public class UserService : IUserService
{
private readonly IUserRepository _repo;
public UserService(IUserRepository repo) => _repo = repo;
public async Task<UserDto> GetUserByIdAsync(int id)
{
var user = await _repo.GetByIdAsync(id);
return user != null ? new UserDto(user) : null;
}
}
Here’s where:
● Business rules live (e.g., “don’t return soft-deleted users”)
● DTO transformation happens
🗃 6. Repository Handles Data Access
public class UserRepository : IUserRepository
{
private readonly AppDbContext _context;
public UserRepository(AppDbContext context) => _context = context;
public async Task<User> GetByIdAsync(int id)
{
return await _context.Users.FindAsync(id); // EF Core ORM
}
}
✅ Talks directly to the EF Core ORM
❌ Should not contain business logic
🧱 7. Database Access via ORM
● Uses Entity Framework Core
● Class User maps to a table Users
● ORM handles query, mapping, and SQL behind the scenes
📦 8. DTO Sent Back to Controller
● UserDto is returned from Service → Controller
● Controller wraps it in Ok(userDto) → returns as 200 OK
json
{
"status": true,
"message": "User found",
"data": {
"id": 7,
"name": "Mostafa",
"email": "mostafa@example.com"
}
}
🧠 Full Flow Recap:
1. Client (web or mobile)
|
2. Program.cs bootstraps the app
|
3. Middleware (JWT, Logging, CORS, Exception Handling)
|
4. Controller receives request
|
5. Service applies business logic
|
6. Repository talks to DB via EF Core
|
7. Data returned to service, mapped to DTO
|
8. Controller wraps and returns response
|
9. Client receives clean JSON
🔄 The Role of Middleware
Middleware is like a “checkpoint” between the request and your app’s core logic.
It intercepts requests/responses and can modify, log, block, or handle them.
Middleware Jobs: The Unsung Heroes of API Architecture
Introduction
● What is Middleware in the context of APIs?
● Why Middleware is critical in modern application development.
Core Jobs of Middleware
1. Request Processing
○ Intercepting requests before they reach backend services.
○ Parsing, validating, and transforming incoming data.
2. Authentication and Authorization
○ Verifying user credentials.
○ Enforcing security policies and roles.
3. Logging and Monitoring
○ Tracking API usage and performance metrics.
○ Sending logs to monitoring systems for observability.
4. Rate Limiting and Throttling
○ Controlling traffic to prevent abuse or overload.
○ Ensuring fair usage policies.
5. Data Transformation and Formatting
○ Converting between data formats (e.g., XML to JSON).
○ Adding or removing fields as needed by consumers.
6. Error Handling and Response Management
○ Catching errors from backend or middleware itself.
○ Returning consistent error responses to clients.
7. Caching
○ Improving performance by serving cached responses.
8. Cross-Origin Resource Sharing (CORS)
○ Enabling or restricting client origins for security.
🔥 Where Does Error Handling Live?
Framework Error Handler Location Is It Reusable? Customizable?
Middleware?
.NET ✅ Middleware (usually) ✅ Yes ✅ Yes ✅ Fully
Node.js ✅ Middleware ✅ Yes ✅ Yes ✅ Fully
(app.use(...))
Laravel ✅ Middleware OR Exception ⚠️ Sometimes ✅ Yes ✅ Fully
Handler
Let’s break it down per platform 👇
🟦 .NET (ASP.NET Core)
✅ Error Handling = Custom Middleware
In .NET, the recommended way to handle all unhandled exceptions globally is to write a
custom middleware:
csharp
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context); // next = controller/service/etc.
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteAsJsonAsync(new {
status = false,
message = "Internal Server Error",
error = ex.Message
});
}
}
}
📌 Register It in Program.cs:
csharp
app.UseMiddleware<ErrorHandlerMiddleware>();
✅ This makes it middleware-based
❌ Error handling should not live in the service or controller unless it’s specific to business
rules
🟩 Node.js (Express.js)
✅ Error Handling = Last Middleware Function
In Express, error-handling middleware has 4 arguments:
javascript
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
status: false,
message: "Internal Server Error",
error: err.message
});
});
✅ Add this after all routes/controllers
✅ This is a middleware function
📦 You can also use tools like:
● express-async-errors to catch async route errors
● Wrap your own middlewares/services in try/catch for fine control
🟥 Laravel (PHP)
Laravel offers 2 ways to handle errors:
1. ✅ Global Handler (Not Middleware, but a core class)
● Located in: app/Exceptions/Handler.php
php
public function render($request, Throwable $exception)
{
return response()->json([
'status' => false,
'message' => $exception->getMessage()
], 500);
}
✅ This is NOT middleware technically
✅ But it is the Laravel-standard error catcher
2. ⚙️ Middleware-Based Error Catching (Optional)
You can also define custom middleware like:
php
public function handle($request, Closure $next)
{
try {
return $next($request);
} catch (Exception $e) {
return response()->json([
'status' => false,
'message' => 'Something went wrong',
]);
}
}
📌 Register in app/Http/Kernel.php
✅ Summary: Who Should Handle Errors?
Layer Should Handle Errors? Use Case
Middleware ✅ Yes (global) Unexpected server exceptions
Service ✅ Sometimes (domain) Business rule errors (e.g., "out of
stock")
Controller ❌ Only minimal try/catch Should pass errors to middleware
.NET ✅ Use ErrorHandlerMiddleware
Node.js ✅ Last app.use() middleware
Laravel ✅ Use Handler.php or optional
middleware
Middleware Jobs and ORM: The Backbone
of Modern API Architecture
In today’s interconnected world, APIs are the highways of digital communication. But behind the
scenes, there’s a crucial layer that often goes unnoticed—middleware—and the tools that
connect your code to databases, like ORMs. Together, they form the backbone of scalable,
secure, and maintainable APIs.
What is Middleware in APIs?
Middleware acts as the intermediary software layer between your API’s frontend (client
requests) and backend (business logic and databases). It intercepts, processes, and augments
API requests and responses to ensure smooth and secure communication.
Core Jobs of Middleware
1. Request Processing
Middleware intercepts incoming API requests, parsing and validating them before
passing to the backend services.
2. Authentication and Authorization
It verifies user credentials and enforces access control, protecting sensitive data and
resources.
3. Logging and Monitoring
Middleware tracks API usage and performance, sending logs to monitoring systems for
real-time observability.
4. Rate Limiting and Throttling
To prevent abuse, middleware controls the number of requests a client can make in a
given timeframe.
5. Data Transformation and Formatting
It converts data between formats (e.g., XML to JSON), adjusts payloads to meet client
needs.
6. Error Handling
Middleware catches errors, formats consistent error messages, and ensures the API
doesn’t expose sensitive internal details.
7. Caching
To boost performance, middleware can serve cached data for repeated requests.
8. CORS Management
Middleware controls which client origins are allowed to access the API, enhancing
security.
The Role of ORM in API and Middleware Architecture
Connecting your API to a database efficiently is just as important as processing requests. This is
where Object-Relational Mapping (ORM) tools shine. ORM bridges your code’s objects with
relational database tables, abstracting SQL and simplifying data operations.
Why Use ORM?
● Productivity: Developers work with objects instead of SQL queries, focusing on
business logic.
● Security: ORMs handle query parameterization, preventing SQL injection attacks.
● Maintainability: Code is cleaner and easier to manage with consistent data models.
● Flexibility: Support for migrations and schema evolution without manual scripts.
Example: Entity Framework Core
Entity Framework Core (EF Core) is a powerful ORM for .NET applications that integrates
seamlessly with APIs and middleware.
Here’s a simple example of how EF Core maps a C# object to a database table and performs a
query:
// Define a model
public class Employee
public int Id { get; set; }
public string Name { get; set; }
public string Role { get; set; }
// DbContext representing the database session
public class AppDbContext : DbContext
public DbSet<Employee> Employees { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder
options)
=> options.UseSqlServer("YourConnectionStringHere");
// Usage example in middleware or API service
public async Task<List<Employee>> GetEmployeesAsync()
{
using var context = new AppDbContext();
return await context.Employees.Where(e => e.Role ==
"Developer").ToListAsync();
Middleware or API services call this data access method without worrying about SQL syntax,
while EF Core handles the translation and execution efficiently.
Why Middleware Jobs and ORM Matter
Middleware keeps your APIs secure, scalable, and easy to maintain by centralizing
cross-cutting concerns. ORMs enable rapid development with cleaner code and safer database
interactions. Together, they enable teams to build robust, performant API ecosystems that meet
business needs while ensuring security and scalability.