DEV Community

Cover image for Designing URIs for REST APIs: The Cool, the Clean, and the Consistent
Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on

Designing URIs for REST APIs: The Cool, the Clean, and the Consistent

Hi there! I'm Maneshwar. Right now, I’m building LiveAPI, a first-of-its-kind tool for helping you automatically index API endpoints across all your repositories. LiveAPI helps you discover, understand, and use APIs in large tech infrastructures with ease.


You can’t build a great API without designing great URIs.

They’re not just strings – they’re the permanent addresses of your resources, the street names in the city of your service.

In this post, we’ll walk through time-tested patterns, practical dos and don’ts, and real-world techniques to keep your URIs clean, stable, and RESTful.

Design URIs That Make Sense

Problem:

How should I structure URIs so that they are predictable, readable, and RESTful?

Solution:

Follow standard conventions:

  • Use domains/subdomains to logically partition services.
  • Use slashes / for hierarchies (collections and sub-resources).
  • Use commas , and semicolons ; for non-hierarchical segments.
  • Use hyphens - or underscores _ for readability – pick one, stay consistent.
  • Use ampersands & in query strings to separate params.
  • Avoid file extensions like .php, .jsp, etc.

Domains and Subdomains

Use them to split responsibilities, locales, or access types:

http://en.example.org/book/1234   // English version
http://da.example.org/book/1234   // Danish version

http://www.example.org/book/1234  // for browsers
http://api.example.org/book/1234  // for API clients
Enter fullscreen mode Exit fullscreen mode

This lets you route traffic smartly – by region, by client type, even by infrastructure needs.

Slashes for Hierarchies

Use / like folders in a filesystem:

http://www.example.org/customers/1234/orders/5678
Enter fullscreen mode Exit fullscreen mode

This shows that order 5678 belongs to customer 1234.

Beware of trailing slashes though:

http://example.org/orders/  ✅  // Collection
http://example.org/orders   ✅  // Also valid
Enter fullscreen mode Exit fullscreen mode

Just be consistent. Some frameworks normalize these automatically, others don’t.

Hyphens vs Underscores

Pick one:

/blog/my-first-post   // hyphens = readable
/my_photos/our_trip   // underscores = valid too
Enter fullscreen mode Exit fullscreen mode

Most devs lean toward hyphens because they’re easier on the eyes and URLs like my-first-api just scan better.

Skip File Extensions

/report-summary.xml   // 👎 Don’t leak tech
/report-summary.jsp   // 👎 Even worse
/report-summary       // ✅ Let Accept header decide format
Enter fullscreen mode Exit fullscreen mode

File extensions tie your backend implementation to the URL – not great for flexibility or long-term sanity.

Query Parameters

Use them for filtering, pagination, options – not core identity.

/search?phrase=Antarctica&limit=10
Enter fullscreen mode Exit fullscreen mode

Use & to separate params, = for key-value. Easy.

Matrix Parameters (if you dare)

Rare but valid: semicolons ; and commas , can add structure without implying hierarchy.

/coordinates;lat=22.3;lng=88.6
Enter fullscreen mode Exit fullscreen mode

Don’t expect every framework to love this, though. Custom parsing is often required.

Use URIs as Opaque Identifiers

Problem:

How do I make URIs stable and unique without overloading them?

Solution:

  • Treat URIs as opaque keys – don’t inject too much meaning.
  • Avoid stateful hacks like repeatedly POSTing to the same URI to simulate a workflow.
  • Don’t overload semantics into headers or query strings that change behavior drastically.

Cool URIs Don’t Change

Problem:

How do I prevent URI breakage when things change under the hood?

Solution:

  • Design URIs based on stable concepts, not implementations.
  • Use server-side rewrite rules to hide internal changes.
  • If you must change URIs, use 301 redirects and keep old URIs alive.

Bad:

/api/v1/reports.jsp   // tech-coupled, versioned, fragile
Enter fullscreen mode Exit fullscreen mode

Good:

/api/reports/summary  // implementation-free
Enter fullscreen mode Exit fullscreen mode

If you later rewrite this part of your backend in Rust or Go, or if /api/v1 becomes /api/v2, you can:

  1. Leave the old URIs active with 301 redirects.
  2. Make the API version part of the Accept header (application/vnd.api+json;version=2).
  3. Use a gateway (e.g., API Gateway, Kong) to handle old-new mappings invisibly.

Cool URIs = happy clients = fewer breakages.

Bonus: Handle Special Characters Carefully

  • Space becomes %20 in RFC 3986, but + in application/x-www-form-urlencoded.
  • Be consistent with encoding.
  • Capital letters: Avoid them! URIs are case-sensitive after the host.
/search?phrase=Super+Collider         // Sent by HTML forms
/search?phrase=Super%20Collider       // Sent by JS fetch
Enter fullscreen mode Exit fullscreen mode

Always decode carefully on the backend. Normalize where possible.

Final Thoughts

Think of your API like a public library.

The URI is the shelf address.

If you keep rearranging shelves, nobody can find anything.

Follow conventions, stay consistent, and make your URIs boringly predictable.

Because boring = maintainable = scalable.


LiveAPI helps you get all your backend APIs documented in a few minutes

With LiveAPI, you can quickly generate interactive API documentation that allows users to search and execute APIs directly from the browser.

Image description

If you’re tired of manually creating docs for your APIs, this tool might just make your life easier.

Top comments (1)

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

been cool seeing steady progress - it adds up. you think the secret to keeping apis clean long-term is all about strict habits or more about picking one style and sticking to it?