REST Principles
REST is a set of rules that makes APIs predictable. If you know one well-designed REST API, you can guess how the next one works — that is the whole point.
On this page · 6 min · Beginner
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 6 min
- Difficulty
- Beginner
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Explain resources, URIs and representations
- Design URLs with nouns, not verbs
- Use HTTP methods for actions
- Explain why REST APIs are stateless
Explanation#
A resource is a thing your API exposes: a customer, an order, a product. Each has a URI: /api/customers/5.
URLs are nouns, usually plural. The action comes from the HTTP method: GET reads, POST creates, PUT replaces, PATCH updates part, DELETE removes. /api/getCustomer?id=5 is RPC style, not REST.
Relationships are expressed by nesting: /api/customers/5/orders is “the orders of customer 5”. Keep nesting to one level.
Stateless: every request carries everything the server needs (including the auth token). The server keeps no session between requests, so any server in a farm can answer — which is how APIs scale.
A representation is the format of the data, usually JSON. Clients ask with the Accept header; Web API 2 can also return XML unless you remove that formatter.
Use status codes to report outcomes (201 Created, 404 Not Found) instead of returning 200 with an error message inside.
Syntax#
✓ GET /api/customers list customers
✓ GET /api/customers/5 one customer
✓ POST /api/customers create a customer
✓ GET /api/customers/5/orders orders of customer 5
✗ GET /api/getAllCustomers
✗ POST /api/deleteCustomer?id=5
Code example: A RESTful customers controller#
using System.Web.Http;
public class CustomerDto
{
public int Id { get; set; }
public string Name { get; set; }
}
[RoutePrefix("api/customers")]
public class CustomersController : ApiController
{
// GET api/customers
[HttpGet, Route("")]
public IHttpActionResult GetAll()
{
return Ok(new[] { new CustomerDto { Id = 1, Name = "Asha" }, new CustomerDto { Id = 2, Name = "Ravi" } });
}
// GET api/customers/1
[HttpGet, Route("{id:int}")]
public IHttpActionResult GetById(int id)
{
return Ok(new CustomerDto { Id = id, Name = "Asha" });
}
// GET api/customers/1/orders — a related resource
[HttpGet, Route("{id:int}/orders")]
public IHttpActionResult GetOrders(int id)
{
return Ok(new[] { new { OrderId = 101, CustomerId = id, Total = 1499.50m } });
}
// POST api/customers — create; reply 201 with the new resource's URL
[HttpPost, Route("")]
public IHttpActionResult Create(CustomerDto customer)
{
customer.Id = 3;
return Created("api/customers/" + customer.Id, customer);
}
}
GET /api/customers → 200 [{"Id":1,"Name":"Asha"},{"Id":2,"Name":"Ravi"}]
GET /api/customers/1/orders → 200 [{"OrderId":101,"CustomerId":1,"Total":1499.50}]
POST /api/customers {"Name":"Meera"}
→ 201 Created, Location: api/customers/3, body {"Id":3,"Name":"Meera"}This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- [RoutePrefix("api/customers")] names the resource collection once for the whole controller.
- GET on the collection lists customers; GET with an id returns one.
- {id:int}/orders exposes a related resource under its parent.
- POST on the collection creates a customer and answers 201 Created with a Location header pointing to it.
- No URL contains a verb — the HTTP method says what to do.
Real-world use case#
A bank’s partner API follows REST strictly, so partners integrate in days: once they have used GET /api/accounts/{id}, they correctly guess GET /api/accounts/{id}/transactions without reading the docs.
Common mistakes#
/api/products/delete/5 duplicates what DELETE /api/products/5 already says.
Returning 200 { "success": false } forces every client to parse the body to detect failure. Use 4xx/5xx codes.
Storing the user’s cart in Session breaks when a load balancer sends the next request to another server. Keep APIs stateless.
Best practices#
- Plural nouns for collections (/api/products)
- One level of nesting at most
- Let HTTP methods carry the action
- Return meaningful status codes and Location headers
Remember this#
URLs are nouns; HTTP methods are verbs.
Stateless: each request carries everything it needs.
Resource = thing. Representation = its JSON.
Practice exercise#
Your task: Design REST endpoints (method + URL) for a blog: list posts, read one post, create a post, update a post, delete a post, list comments of a post, add a comment to a post.
Need a hint?
Show solution
GET /api/posts
GET /api/posts/{id}
POST /api/posts
PUT /api/posts/{id}
DELETE /api/posts/{id}
GET /api/posts/{id}/comments
POST /api/posts/{id}/comments
Mini quiz#
Which URL follows REST conventions to delete product 7?
Want more? Practise all ASP.NET Web API MCQs · ASP.NET Web API interview questions
My notes#
Saved in this browser.