Mastering REST API Design: Best Practices for Clean and Effective APIs

Jaspreet Singh Sodhi
4 min readJul 16, 2024

--

Building Scalable REST API’s

API design is crucial for building scalable, maintainable, and user-friendly applications. Well-designed APIs enable seamless communication between different systems, enhance developer experience, and ensure that applications can evolve without breaking existing functionality. This guide provides concise tips on designing effective REST APIs based on best practices and common pitfalls.

1. Learn the Basics of HTTP

Understanding HTTP is fundamental for designing REST APIs. It helps in making informed design choices.

# Don't
GET /books/create
POST /books/delete

# Do
GET /books/
POST /books/

Explanation: HTTP methods (GET, POST, PUT, DELETE) should describe the action, while the URI should represent the resource.

2. Do Not Return Plain Text

Always return responses in JSON format with the appropriate Content-Type header.

# Don't
return "This is plain text"

# Do
response.headers['Content-Type'] = 'application/json'
return jsonify({"message": "This is JSON"})

Explanation: JSON is the standard format for REST APIs. The Content-Type header ensures the client knows how to interpret the response.

3. Avoid Verbs in URIs

Use HTTP methods to describe actions.

# Don't
POST /books/createNewBook/

# Do
POST /books/

Explanation: URIs should represent resources, and actions should be inferred from the HTTP method used.

4. Use Plural Nouns for Resources

Be consistent with plural nouns to avoid ambiguity.

# Don't
GET /book/2/

# Do
GET /books/2/

Explanation: Plural nouns ensure clarity, especially when dealing with collections of resources.

5. Return Error Details in Response Body

Include error details in the response body for easier debugging.

# Don't
return "Error", 400

# Do
response = jsonify({"error": "Invalid payload.", "detail": {"name": "This field is required."}})
response.status_code = 400
return response

Explanation: Detailed error messages help clients understand what went wrong and how to fix it.

6. Use Appropriate HTTP Status Codes

Do not misuse status codes, especially for errors.

# Don't
response = jsonify({"status": "failure", "error": "Expected at least three items in the list."})
response.status_code = 200
return response

# Do
response = jsonify({"error": "Expected at least three items in the list."})
response.status_code = 400
return response

Explanation: Proper use of status codes ensures clients can correctly handle responses without additional checks.

7. Use HTTP Status Codes Consistently

Maintain consistency in the use of HTTP status codes across your API.

# Don't
response = jsonify({"message": "Resource created."})
response.status_code = 200

# Do
response = jsonify({"message": "Resource created."})
response.status_code = 201

Explanation: Consistency in status codes helps clients predict and handle responses more effectively.

8. Do Not Nest Resources

Avoid deep nesting of resources to keep URIs simple and clear.

# Don't
GET /authors/Cagan/books/

# Do
GET /books?author=Cagan

Explanation: Flat URIs with query parameters are easier to manage and understand.

9. Handle Trailing Slashes Gracefully

Decide on a convention for trailing slashes and handle deviations gracefully.

# Don't
POST /buckets # Fails without trailing slash

# Do
from flask import Flask, redirect, request
app = Flask(__name__)

@app.route('/buckets/', methods=['POST'])
def create_bucket():
return "Bucket created", 201

@app.before_request
def remove_trailing_slash():
if request.path != '/' and request.path.endswith('/'):
return redirect(request.path[:-1])

Explanation: Consistent handling of trailing slashes prevents errors and ensures smooth client interactions.

10. Use Query Strings for Filtering and Pagination

Implement filtering and pagination using query string parameters.

# Don't
GET /books/published/

# Do
GET /books?published=true&page=2&page_size=10

Explanation: Query strings are the standard way to implement filtering and pagination in REST APIs.

11. Know the Difference Between 401 Unauthorized and 403 Forbidden

Use the correct status code for authentication and authorization errors.

# Don't
response = jsonify({"error": "Unauthorized"})
response.status_code = 403 # Wrong status code

# Do
response = jsonify({"error": "Unauthorized"})
response.status_code = 401 # Correct status code

response = jsonify({"error": "Forbidden"})
response.status_code = 403 # Correct status code for forbidden

Explanation: Properly distinguishing between authentication and authorization errors helps clients handle security issues correctly.

12. Make Good Use of HTTP 202 Accepted

Use 202 Accepted for requests that will be processed asynchronously.

# Don't
response = jsonify({"message": "Request accepted"})
response.status_code = 201 # Not appropriate if not created yet

# Do
response = jsonify({"message": "Request accepted, processing."})
response.status_code = 202
return response

13. Use a Web Framework Specialized in REST APIs

Choose frameworks designed for building REST APIs to simplify implementation.

# Don't
from flask import Flask
app = Flask(__name__)

@app.route('/books', methods=['GET'])
def get_books():
return jsonify([]) # General-purpose framework without specific REST support

# Do
from flask import Flask
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)

class BookList(Resource):
def get(self):
return []

api.add_resource(BookList, '/books')

Explanation: Using specialized frameworks ensures adherence to REST principles and best practices.

Designing REST APIs is both an art and a science. By adhering to these best practices, you can create APIs that are intuitive, reliable, and easy to use. Remember, the key to a great API is simplicity and consistency. Strive to make your APIs a pleasure to use for developers and clients alike, ensuring that they can be easily integrated and maintained over time.

Whether you are building your first API or refining an existing one, these guidelines will help you create robust and scalable solutions that stand the test of time. Happy coding!

That’s it!. Feel free to follow me and share your thoughts on what else I can improve.

Do checkout the my complete ongoing series on System Design —

jaspreetsodhi02.medium.com/list/system-design-series-f5054fed26fd

See you in the next part! 😊

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Jaspreet Singh Sodhi
Jaspreet Singh Sodhi

Written by Jaspreet Singh Sodhi

Full Stack Software Engineer | Curating Top-Notch Content @jaspreet.dev on Instagram ✨

No responses yet

Write a response