Interview

15 REST APIs Interview Questions and Answers

Prepare for your next technical interview with this guide on REST APIs, featuring common questions and detailed answers to enhance your understanding.

REST APIs (Representational State Transfer Application Programming Interfaces) have become a cornerstone of modern web development. They enable different software systems to communicate over the internet in a standardized way, making it easier to integrate and scale applications. RESTful services are stateless, scalable, and can be used with a variety of data formats, making them a versatile choice for developers.

This article offers a curated selection of interview questions designed to test your understanding and proficiency with REST APIs. By reviewing these questions and their detailed answers, you will be better prepared to demonstrate your knowledge and problem-solving abilities in your upcoming technical interviews.

REST APIs Interview Questions and Answers

1. Describe the difference between PUT and POST methods.

The PUT and POST methods in REST APIs are used to send data to the server, but they serve different purposes.

PUT Method:

  • Used to update an existing resource or create one if it doesn’t exist.
  • Idempotent, meaning multiple identical requests have the same effect as a single request.
  • The client specifies the resource’s URI, and the server replaces the resource at that URI with the provided data.

POST Method:

  • Used to create a new resource.
  • Not idempotent, meaning multiple identical requests can result in different outcomes.
  • The server determines the URI of the new resource and returns it to the client.

2. What are idempotent operations in REST? Provide examples.

Idempotent operations in REST can be performed multiple times without changing the result beyond the initial application. This ensures repeated requests do not cause unintended side effects, useful in scenarios like network retries.

In REST, the following HTTP methods are idempotent:

  • GET: Retrieves a resource without modifications. Multiple identical GET requests return the same result.
  • PUT: Updates a resource or creates it if it doesn’t exist. Multiple identical PUT requests result in the resource being in the same state as after the first request.
  • DELETE: Removes a resource. Multiple identical DELETE requests result in the resource being deleted (or remaining deleted if it was already removed).
  • HEAD: Similar to GET but only retrieves the headers. Multiple identical HEAD requests return the same headers.
  • OPTIONS: Retrieves the supported HTTP methods for a resource. Multiple identical OPTIONS requests return the same result.

Example:

# Example of an idempotent PUT request
import requests

url = 'https://api.example.com/resource/1'
data = {'name': 'example'}

response = requests.put(url, json=data)
print(response.status_code)  # 200 OK

# Repeating the same PUT request
response = requests.put(url, json=data)
print(response.status_code)  # 200 OK

3. How would you handle versioning in a REST API?

Versioning in a REST API can be handled in several ways:

  • URI Versioning: The version number is included in the URL path, e.g., /api/v1/resource.
  • Query Parameters: The version number is specified as a query parameter, e.g., /api/resource?version=1.
  • Header Versioning: The version number is included in the HTTP headers, e.g., X-API-Version: 1.
  • Content Negotiation: The version is specified in the Accept header, e.g., Accept: application/vnd.myapi.v1+json.

Each method has its pros and cons. URI versioning is straightforward but can clutter URLs. Query parameters are flexible but less intuitive. Header versioning keeps URLs clean but requires clients to manage headers. Content negotiation is powerful but complex to implement.

4. What are the common status codes returned by a REST API and what do they signify?

Common status codes returned by a REST API and their significance are as follows:

  • 200 OK: The request has succeeded.
  • 201 Created: The request has been fulfilled and resulted in new resources being created.
  • 204 No Content: The server successfully processed the request but is not returning any content.
  • 400 Bad Request: The server could not understand the request due to invalid syntax.
  • 401 Unauthorized: The client must authenticate to get the requested response.
  • 403 Forbidden: The client does not have access rights to the content.
  • 404 Not Found: The server cannot find the requested resource.
  • 500 Internal Server Error: The server encountered a situation it doesn’t know how to handle.
  • 502 Bad Gateway: The server received an invalid response from the upstream server.
  • 503 Service Unavailable: The server is not ready to handle the request, often due to maintenance or overload.

5. How would you implement authentication in a REST API?

Authentication in a REST API can be implemented using several methods:

  • Basic Authentication: Involves sending the username and password with each request. It is simple but not very secure unless used over HTTPS.
  • Token-Based Authentication: Involves generating a token upon successful login, which is then used for subsequent requests. It is more secure and scalable.
  • OAuth: A more complex and secure method, often used for third-party authentication.

A common method is token-based authentication. Here is an example using Python and the Flask framework:

from flask import Flask, request, jsonify
import jwt
import datetime

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'

@app.route('/login', methods=['POST'])
def login():
    auth = request.authorization
    if auth and auth.password == 'password':
        token = jwt.encode({'user': auth.username, 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=30)}, app.config['SECRET_KEY'])
        return jsonify({'token': token})
    return jsonify({'message': 'Could not verify'}), 401

@app.route('/protected', methods=['GET'])
def protected():
    token = request.headers.get('x-access-tokens')
    if not token:
        return jsonify({'message': 'Token is missing'}), 401
    try:
        data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
    except:
        return jsonify({'message': 'Token is invalid'}), 401
    return jsonify({'message': 'Protected route', 'user': data['user']})

if __name__ == '__main__':
    app.run()

6. Explain how CORS works and why it is important in REST APIs.

CORS (Cross-Origin Resource Sharing) allows restricted resources on a web page to be requested from another domain. It is implemented through a set of HTTP headers that dictate how and when cross-domain requests are allowed.

When a web application makes a request to a different domain, the browser sends an HTTP request with an Origin header. The server responds with specific CORS headers indicating whether the request is allowed. Key headers involved in CORS are:

  • Access-Control-Allow-Origin: Specifies which origins are permitted to access the resource.
  • Access-Control-Allow-Methods: Lists the HTTP methods that are allowed when accessing the resource.
  • Access-Control-Allow-Headers: Lists the headers that can be used when making the actual request.
  • Access-Control-Allow-Credentials: Indicates whether the response to the request can be exposed when the credentials flag is true.

CORS is important in REST APIs because it helps enforce security policies by ensuring that only authorized domains can access the API.

7. How would you handle rate limiting in a REST API?

Rate limiting in a REST API controls the number of requests a client can make to the server within a specified time frame. This prevents abuse, ensures fair usage among clients, and maintains the performance and availability of the API. There are several ways to implement rate limiting, such as using fixed windows, sliding windows, or token buckets.

A common approach is to use middleware to intercept requests and check if the client has exceeded their allowed number of requests. If the limit is exceeded, the server can respond with a status code indicating that the client should slow down their request rate.

Example:

from flask import Flask, request, jsonify
from time import time

app = Flask(__name__)

# Dictionary to store request timestamps
request_times = {}

# Rate limit configuration
RATE_LIMIT = 5  # requests
TIME_WINDOW = 60  # seconds

@app.route('/api/resource', methods=['GET'])
def resource():
    client_ip = request.remote_addr
    current_time = time()

    if client_ip not in request_times:
        request_times[client_ip] = []

    # Filter out timestamps outside the time window
    request_times[client_ip] = [t for t in request_times[client_ip] if current_time - t < TIME_WINDOW]

    if len(request_times[client_ip]) >= RATE_LIMIT:
        return jsonify({"error": "Rate limit exceeded"}), 429

    request_times[client_ip].append(current_time)
    return jsonify({"message": "Request successful"})

if __name__ == '__main__':
    app.run()

8. Describe how you would implement pagination in a REST API.

Pagination in a REST API divides a large dataset into smaller, more manageable chunks, which can be retrieved in separate requests. This improves performance and user experience when dealing with large amounts of data.

Common methods are:

  • Offset-based pagination: Uses an offset and a limit to specify the starting point and the number of records to retrieve.
  • Cursor-based pagination: Uses a cursor to keep track of the current position in the dataset and retrieve the next set of records.

Here is an example of offset-based pagination using Python and Flask:

from flask import Flask, request, jsonify

app = Flask(__name__)

data = list(range(1, 101))  # Example dataset

@app.route('/items', methods=['GET'])
def get_items():
    try:
        page = int(request.args.get('page', 1))
        per_page = int(request.args.get('per_page', 10))
    except ValueError:
        return jsonify({"error": "Invalid pagination parameters"}), 400

    start = (page - 1) * per_page
    end = start + per_page
    paginated_data = data[start:end]

    return jsonify({
        "page": page,
        "per_page": per_page,
        "total": len(data),
        "data": paginated_data
    })

if __name__ == '__main__':
    app.run(debug=True)

In this example, the get_items endpoint retrieves the page and per_page parameters from the query string, calculates the starting and ending indices, and returns the corresponding subset of the dataset.

9. How would you secure a REST API against common vulnerabilities like SQL injection and XSS?

Securing a REST API against vulnerabilities like SQL injection and Cross-Site Scripting (XSS) involves several best practices:

  • Input Validation and Sanitization: Validate and sanitize user inputs to ensure they conform to expected formats.
  • Parameterized Queries: Use parameterized queries or prepared statements to interact with the database, preventing SQL injection attacks.
  • Output Encoding: Encode outputs to prevent XSS attacks by converting special characters to HTML entities.
  • Authentication and Authorization: Implement robust mechanisms to ensure only authorized users can access the API.
  • HTTPS: Use HTTPS to encrypt data transmitted between the client and the server.
  • Security Headers: Implement headers like Content Security Policy (CSP) and X-Content-Type-Options to protect against various attacks.
  • Rate Limiting and Throttling: Implement to prevent abuse and mitigate the risk of denial-of-service (DoS) attacks.
  • Regular Security Audits: Conduct audits and code reviews to identify and fix vulnerabilities.

10. How would you implement caching in a REST API?

Caching in a REST API stores copies of responses to reduce the time and resources required to generate the same response multiple times. It improves performance and scalability by reducing server load and decreasing response time.

Strategies for implementing caching include:

  • Client-Side Caching: The client stores the response and reuses it for subsequent requests, controlled using HTTP headers like Cache-Control and Expires.
  • Server-Side Caching: The server stores the response and serves it to multiple clients, using in-memory stores like Redis or Memcached.
  • Proxy Caching: A proxy server between the client and the server caches the responses and serves them to clients, using tools like Varnish or Nginx.

Example:

from flask import Flask, request, jsonify
from werkzeug.contrib.cache import SimpleCache

app = Flask(__name__)
cache = SimpleCache()

@app.route('/data')
def get_data():
    cache_key = 'data'
    data = cache.get(cache_key)
    if data is None:
        # Simulate a slow database query
        data = {'value': 'This is the data'}
        cache.set(cache_key, data, timeout=60)  # Cache for 60 seconds
    return jsonify(data)

if __name__ == '__main__':
    app.run(debug=True)

In this example, a simple in-memory cache is used to store the response for a specific endpoint. The cache is checked before processing the request, and if the data is found, it is returned immediately. Otherwise, the data is fetched, stored in the cache, and then returned.

11. Describe the use of middleware in REST APIs.

Middleware in REST APIs acts as a bridge between the incoming request and the final request handler. It allows for the processing of requests before they reach the endpoint and responses before they are sent back to the client. Middleware can be used for various purposes such as:

  • Logging: Capturing request and response data for monitoring and debugging.
  • Authentication and Authorization: Verifying user credentials and permissions before allowing access to certain endpoints.
  • Error Handling: Catching and managing errors to provide meaningful responses to the client.
  • Data Transformation: Modifying request or response data, such as parsing JSON or XML.

Here is a simple example of middleware in a REST API using Python’s Flask framework:

from flask import Flask, request, jsonify

app = Flask(__name__)

# Middleware for logging
@app.before_request
def log_request_info():
    print('Headers: %s', request.headers)
    print('Body: %s', request.get_data())

# Middleware for authentication
@app.before_request
def authenticate():
    token = request.headers.get('Authorization')
    if not token or token != 'valid-token':
        return jsonify({'error': 'Unauthorized'}), 401

@app.route('/data', methods=['GET'])
def get_data():
    return jsonify({'data': 'This is some data'})

if __name__ == '__main__':
    app.run(debug=True)

In this example, two middleware functions are defined using the @app.before_request decorator. The first middleware logs the request headers and body, while the second middleware checks for a valid authorization token.

12. Explain the concept of API gateways and their benefits.

An API gateway is a server that acts as an API front-end, receiving API requests, enforcing throttling and security policies, passing requests to the back-end service, and then passing the response back to the requester. It can also perform various cross-cutting tasks such as authentication, logging, rate limiting, and load balancing.

The benefits of using an API gateway include:

  • Centralized Authentication and Authorization: API gateways can handle all authentication and authorization, ensuring that only authenticated and authorized requests reach the backend services.
  • Load Balancing: They can distribute incoming requests across multiple instances of a service, improving performance and reliability.
  • Rate Limiting and Throttling: API gateways can limit the number of requests a client can make in a given time period, protecting backend services from being overwhelmed.
  • Logging and Monitoring: They provide a centralized point for logging and monitoring API calls, making it easier to track usage and diagnose issues.
  • Protocol Translation: API gateways can translate between different protocols, such as HTTP to WebSocket, allowing clients to communicate with services using different protocols.
  • Request and Response Transformation: They can modify requests and responses, such as adding or removing headers, or transforming the payload format.

13. What are some best practices for error handling in REST APIs?

Error handling in REST APIs is important for providing a clear and consistent experience for clients. Here are some best practices:

  • Use appropriate HTTP status codes: Ensure that the API returns the correct HTTP status codes for different types of errors.
  • Provide meaningful error messages: Include detailed and human-readable error messages in the response body.
  • Consistent error format: Use a consistent structure for error responses, typically including fields like error code, message, and details.
  • Log errors: Ensure that server-side errors are logged for monitoring and debugging purposes.
  • Do not expose sensitive information: Avoid including sensitive information in error messages that could be exploited by malicious users.
  • Document error responses: Clearly document the possible error responses in the API documentation.

14. How would you document a REST API effectively?

Documenting a REST API effectively ensures that other developers can understand and use your API without confusion. Here are some best practices:

  • Use API Documentation Tools: Tools like Swagger (OpenAPI), Postman, and Apiary can automate and standardize the documentation process.
  • Clear and Concise Descriptions: Each endpoint should have a clear and concise description of its purpose.
  • Request and Response Examples: Provide examples of both requests and responses for each endpoint.
  • Error Handling: Document the possible error responses for each endpoint, including HTTP status codes and error messages.
  • Authentication and Authorization: Clearly explain the authentication and authorization mechanisms used by the API.
  • Versioning: If your API has multiple versions, document each version separately.
  • Rate Limiting and Quotas: If your API enforces rate limits or quotas, document these limits and explain how users can monitor their usage.
  • Consistency: Ensure that the documentation is consistent in terms of terminology, formatting, and style.

15. What strategies would you use to test a REST API?

Testing a REST API involves several strategies to ensure its reliability, performance, and security. Here are some key strategies:

  • Unit Testing: Testing individual endpoints in isolation to ensure they return the expected responses.
  • Integration Testing: Ensures that different parts of the application work together as expected.
  • End-to-End Testing: Tests the entire workflow of the application, from the client to the server and back.
  • Performance Testing: Tests the API under various load conditions to ensure it can handle high traffic.
  • Security Testing: Tests the API for vulnerabilities such as SQL injection, cross-site scripting (XSS), and other security threats.
  • Mocking and Stubbing: Simulate the behavior of the API endpoints, allowing you to test the client-side code without relying on the actual API.
Previous

15 RESTful Web Services Interview Questions and Answers

Back to Interview
Next

15 Oracle Database Interview Questions and Answers