API Introduction

Posted by Daksh on Tuesday, January 10, 2023

API (Application Programming Interface)

Content:

Introduction

An API is an access or a gateway to backend data, making it more accessible. They allow to exchange information and access functionality. Designing an API is an art itself and includes thoughtfullness, creativity, and a lot of planning to present them to end users. You can think of APIs as essentially an intermediary between an end-user and a back-end service.

What points to keep in mind while designing an API?

  1. Most important step among all is to decide who your end user is. This is the most important step because it will help you to decide what kind of data you need to present to the end user. It will also help you to decide what kind of data you need to collect from the end user. It is crucial in maintaining the security & integrity of the data.
  2. What is the request structure and how should the response look like?
  3. It is generally a collaborative effort between the front-end, product, and back-end teams.

Dev tools to build and test APIs

  • Postman - Test and debug APIs
  • Insomnia - Powerful API client used to store, organize, and execute REST API requests
  • Curl - HTTP calls from the command line
  • HTTPbin - free service that allows you to experiment with different types of HTTP methods.
  • Mockaroo - Generate fake data for your API
  • Mockapi - Create mock APIs for free

Organization of an API project

  • Break down the project into multiple apps for different functionalities. The apps should be decoupled from each other. Plan for smaller and independent aims to be achieved by each app.
  • Avoid global environment. Use a virtual environment to isolate the project from the global environment.
  • Upgrade might break apps (Django apps). The result of modified api might differ from the previous one. To avoid this, use versioning and keep old APIs intact. Have timely launches and updates.
  • For the newer version, try to have a new app instead of modifying the old one.
  • List your dependencies. Keep track of the packages and version numbers.
  • Seperate resource folder for each app. This will help to keep the code clean and organized, avoiding cunflicts.
  • Split settings into multiple settings files. You can use Django split settings package to do this.
  • Instead of having all the business logic in the views, try to place them in the models instead. This will lead to less code, more powerful, decoupled, and reusable code.

RESTfulness

What makes an API RESTful? REST is an architectural style for designing APIs. It stands for Representational State Transfer. It is much easier to develop and implement than other architectural styles.

An API is only RESTful if it complies with certain constraints:

  • It must have client-server architecture

  • A REST API is always Stateless: this means that the server does not contain any state of the API client who is making the call so it cannot identify who is making the request or what their previously requested data was without proper user information. In fact, the State is only saved on the client machine, not on the server and this influences what you should include in your API endpoints or URL Paths.

  • It should be Cacheable: This means that responses can be saved by a web browser or a server or any system. This caching process can help to reduce the server load by using the API result from the cache instead of making an actual request to the server every time.

  • It should be layered: system can be split or decoupled into layers with the flexibility to add or remove layers as needed. Example:

    Client -> Firewall -> Load Balancer -> Web Server -> Database

  • It should have a uniform interface: it means that the system should offer a uniform communication system to access the resources. For example, there should be unique URLs for each resource. There should also be a unified way to modify or proceed further with a resource from the API result or representation in a standard XML or Jason format.

  • Optionally, include code on demand: it means that the API may deliver some business logic or code that the client can run to further improve the response result.

Response Types

Generally, there are two types of responses JSON and XML. An api developer must allow the client to request the preferred content type. The client can request the content type by using the Accept header. The server can respond with the content type by using the Content-Type header.

JSON (JavaScript Object Notation)

The request header of json data is Accept: application/json. It is simple and light-weight. Creating and parsing it is easier than XML. It is a dependency-free data format and has less bandwidth than XML due to its smaller data size. It doesn’t support comments. JSON data is like keys and values.

{
"author": "Daksh Gaur",
"title": "APIs", 
"items": [1,2,3,4,5]
}

In Django, you can use the JSONResponse class to return a JSON response.

from django.http import JsonResponse
from django.forms.models import model_to_dict

...
book = Book.objects.get(pk=10)
book_dict = model_to_dict(book)
return JsonResponse(book_dict)

Use the QueryDict class from the django.http module to convert a dictionary to a query string.

# done because the payload is a JSON object or a URL encoded string
# This code will parse a raw JSON string to access its individual data elements.
from django.http import QueryDict

...
request_body = QueryDict(request.body)
individual_element = request_body.get('that_element')

XML (Extensible Markup Language)

XML is more readable and supports data attributes that are not possible in JSON. And you can represent complex data in XML and still keep it readable. The request header of xml data is Accept: application/xml or Accept: text/xml.

XML or Extensible Markup Language is a powerful, tag-based data format. It is similar to HTML. XML data can be fairly complex. It can also include comments.

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <author>Daksh Gaur</author>
   <title>APIs</title>
   <items>
   <element>1</element>
   <element>2</element>
   <element>3</element>
   <element>4</element>
   <element>5</element>
   </items>
</root>

Resource types

Consider an ecommerce app. This app is used by both the customers and the store owners. Owners use the app to manage their store and get the information about the orders. Customers use the app to browse the products, add them to the cart, and place an order. Keep in mind that the server always represents only what you ask for. It does not remember anything of what happened before. The server cannot recognize the client automatically. API calls must include more information about the user.

Naming conventions

The api endpoint must use:

  • follow a consistent & standard naming convention
  • use of clear, concise, descriptive and meaningful words in your api names
  • should use a noun to represent resources it’s working with
  • do not use verbs in your api names, example: getUsers, createUser, updateUser, deleteUser. These names should be avoided. A rest api endpoint should be always represented by a noun and not a verb.
  • avoid using endpoints such as /users/{userId}/update, /users/{userId}/delete, /users/{userId}/create because they use HTTP requests as verbs to indicate the action the API performs. Endpoints that use standard CRUD operation names such as create, read, update or delete should be avoided for two reasons, the resources these endpoints represent should be nouns, and the appropriate HTTP requests like GET, POST, PUT, or DELETE should be used with those endpoints to perform the necessary manipulations.
  • use all lowercase letters (no title case allowed)
  • avoid special characters. Exception: if your API can accept multiple user ids, then they should be separated using a comma,, example: /users/12,13,14/address.
  • avoid using camelCase, TitleCase, or snake_case (for better readability)
  • hypens (-) instead of underscores (_) in between words
  • do not keep abridged, or shortened, words in your URI; always use the full and meaningful form.
  • API with variable, should always be represented by camelCase and wrapped in curly braces {}. For example, /users/{userId}. Do not include any hyphens (-) or underscores (_) in the variable name.
  • use of forward slashes (/) to indicate a hierarchical relationships between related objects. Example, author of a book: library/books/{bookId}/author, all books by an author: library/authors/{authorId}/books
  • you should never use a file name extension in your API endpoint. For example, library/books/{bookId}.json or library/books/{bookId}.xml should never be used. Use the accepted data format in the query string parameter instead. For example, library/books/{bookId}?format=json or library/books/{bookId}?format=xml.
  • to filter the results, use query string parameters. For example, library/books?author=Daksh. In the URL, everything after a ? is a query string.
  • while sharing your api endpoint with others, you should never have any trailing slashes in your endpoint. For example, library/books/ should be avoided. Instead, it should be library/books.
  • Use query parameters to filter when necessary

API Security

The key role of an API is to provide access to the data. Therefore, it is crucial to secure them as they essentially give third-party access to your database and server.

  • Authentication - Generally, token based authentication is used over HTTP basic authentication. Because it is inconvenient to send the username and password with every request. So how does token based authentication work? The client sends the username and password to the server on sing-in or sign-up url. The server then verifies the credentials and if they are valid, it generates a token and sends it back to the client. The client then stores the token and sends it along with every request (inside http header) to the server. The server verifies the token and if it is valid, it allows the request to proceed. If the token is invalid, the request is rejected. It can be done using JWT.
  • Signed URLs - Limited access to a resource for a limited time. Whenever an api is called, a unique signature is generated and sent along with the request. The server verifies the signature and if it is valid, it allows the request to proceed. If the signature is invalid, the request is rejected. It can be done using HMAC.
  • SSL(Secure Sockets Layer) - performs encryption of data when sent over the internet. On installation, the api can be served over https instead of http.
  • Firewall - Allowing only specific IP addresses to access the API.
  • CORS(Cross-Origin Resource Sharing) - It is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the first resource was served. It is used to prevent unauthorized access to the API. It can be done using CORS middleware.

Status codes related to authentication:

  • 401 Unauthorized - Username and password are invalid
  • 403 Forbidden - User is not allowed to access the resource. No authority to perform the action.

To design an access control system, you should consider the following:

  • Role - Collection of privileges
  • Privilege - Permission to perform an action or authority to do a task

Authentication and authorization are two different things. Authentication is the process of verifying the identity of a user. Authorization is the process of verifying what the user is allowed to do. Authorization is done after authentication.

REST best practices

To keep APIs healthy. performant, maintainable and sustainable, you should follow these best practices:

  • KISS (Keep it simple, stupid): Keep your API simple and easy to use. Avoid unnecessary complexity. One api endpoint should do one thing only, and it should do it well.
  • Versioning: It is a good practice to version your API. This will help you to make changes to your API without breaking the existing clients. For example, library/v1/books or library/v2/books. When to update and when to modify existing endpoints? In general, you should only support two versions of any given resource, because maintaining multiple versions can be complex, error prone, and costly.
  • Filter, sort, and paginate your results: It is a good practice to filter, sort, and paginate your results. This will help to reduce the amount of data that is returned to the client. It will also help to reduce the amount of data that is sent over the network. This will help to improve the performance of your API. Using pagination you can deliver the results in chunks. For example, this api request 16 books on page 10 library/books?perpage=16&page=10.
  • Caching: You should always implement cacheing and send relevant HTTP headers in your response. This will minimize the number of calls your client makes to your API. You should only update the cache when the data changes. For example, library/books?genre=horror. Have a caching policy on the reverse proxy and on the web server.
  • Monitoring & Rate Limiting: You should always monitor your API and implement rate limiting. This will help to prevent your API from being abused. For example, you can limit the number of requests a client can make in a given time period. You can also limit the number of requests a client can make to a specific endpoint in a given time period. You should monitor latency, bandwidth, errors, status codes (especially 4xx & 5xx codes), and other metrics.