Introduction
API stands for Application Programming Interface. An API is a set of subroutine definitions, communication protocols and tools that allows different software applications to communicate with each other. It defines the methods and data formats that applications can use to request and exchange information.Some examples: There are many types of APIs possible. Some of them are:
- the pandas library can actually be seen as an API. There is a set of rules that allow to use complex functions from a simplified documentation.
- the OpenWeatherMap API is a data API. From an HTTP request, we receive weather data in the requested location, at the requested date.
- the API Google Cloud Translate allows to use the translation function of Google to easily translate the texts we use.
Types of APIs:
- Web APIs (HTTP/RESTful APIs): APIs that use the HTTP protocol and follow REST (Representational State Transfer) principles for communication.
- Library APIs: APIs provided by programming libraries to enable developers to use pre-built functions and classes.
- Operating System APIs: APIs provided by operating systems to allow applications to interact with the underlying system resources.
- Database APIs: APIs that provide access to databases, allowing applications to retrieve or manipulate data.
Concepts
Securing an API is crucial to protect sensitive data, maintain the integrity of your system, and prevent unauthorized access. Securing an API rely on three important levels:- Authentication: Authentication is a step that consists in verifying the identity of the client querying the API. By providing some credentials or a key to the API, the server will know that the client is who he claims to be.
- Authorization: At the authorization level, the API will check that, based on the identity acknowledged at the previous step, the client is indeed allowed to perform the query.
- Traceability: To better the security of your API, you also need to track the queries. We usually store information about
What
,Who
andWhen
the queries were made in a simple log file or, in some cases, in a database.- What:
- which endpoint was called.
- which method was used.
- Who:
- the user that made the call
- the device used to make that call (mobile phone, web browser, programming language, ...)
- the IP used by the user
- When: the time of the request (date and hour)
- What:
Key components
Key components icnclude:- Endpoints: URLs or URIs that define where the API can be accessed.
- Methods: Specify the actions that can be performed, such as GET (retrieve data), POST (submit data), PUT (update data), DELETE (remove data), etc.
- Request and Response Formats: Define how data is structured when making requests to the API and how it is formatted in the response. Data formats define the structure of the data exchanged between the API and the application.
- Authentication: Authentication methods ensure that only authorized users can access the API's functionality.
For example: the following URI http://example.org/resource can be read as follows:
- http:// is the protocol used
- example.org is the domain name of the server, which is a simplification of the server's IP address.
- /resource is the endpoint you wish to request
Note that a URI can also have parameters. For example, the URI http://example.org/resource?key1=value1&key2=value2 allows to pass the keys key1
and key2
which have respectively the values value1
and value2
to the server. This is called a query string or query parameters.
HTTP responses
When an HTTP request is sent, the server sends a response to the client. This response is also composed of different elements:- Headers with the response metadata
- A body with the content of the response
- A status code
10X
: Information20X
: Means that the request was successful30X
: Redirection40X
: Means a client-side error / Client error50X
: Means server side error / Server error
Links with the web
Websites work on the same principle of server-client architecture that we request via HTTP using a Web browser. Thus, by clicking on a link, the browser sends a request of typeGET
to the server of the website. If the address is correct, the server returns a response containing an HTML file that will be interpreted by the browser. Similalry, when a form is filled in on a website, it is generally a request of type POST. The request then contains the data filled in the form.
HTTPS protocol
The HTTPS protocol is a more secure version of the HTTP protocol. It is in fact the HTTP protocol to which an SSL (Secure Socket Layer) encryption layer is added. It protects the authentication of a server, the confidentiality and integrity of the data exchanged, and sometimes the authentication of the client: a public key is given to the client so that the data sent back to the server is encrypted; this data is then decoded thanks to a private key available on the server. It tends to become the standard, pushed by search engines that better reference sites using an HTTPS protocol.HTTP clients
As we have seen in the previous examples, the browser is a client that allows to make HTTP requests to servers that are able to return data according to the request. However, there are other simpler tools to use when you want to interact with an API.CURL
We will query an API from the terminal using the command line interface cURL (client URL Request Library). To make an HTTP request from the terminal with cURL, the syntax for making a request is as follows :curl -X GET http://example.com
The -X argument introduces the method, in this case, GET. Then we can write the URI.
- For example:
curl -X GET https://jsonplaceholder.typicode.com/posts/1
With this request, we receive a "stringified" JSON object containing information about a post.
Here, we have requested the post with ID 1. We can get all posts with a GET request to the
/posts
endpoint. This is a case we commonly encounter: the endpoint with an ID returns an individual observation while without any ID it returns all observations.The HTTP response to this request contains only the body. To see the headers, we can add the
-i
argument to the :
In the header, you can see information about the content of the response, such as the status code, which in this case is 200.curl -X GET -i https://jsonplaceholder.typicode.com/posts/1
- We have seen how to make a request with the GET method. Now, if you want to use the PUT method to add data to the API, you will probably want to specify a body and headers. To do this, you need to precede your headers with the -H argument and the body with the -d argument.
Here, we indicate in the header the type of data sent (JSON), and in the body the data in question. cURL is undoubtedly the simplest tool to use.curl -X PUT -i\ -H "Content-Type: application/json"\ -d '{"id": 1, "content": "hello world"}'\ https://jsonplaceholder.typicode.com/posts/1
Postman
Postman is a collaborative platform for API development. It allows to build and execute HTTP requests, to store them in a history so that they can be replayed, and to organize them in collections. Among other things, Postman allows us to:- Quickly and easily send REST, SOAP and GraphQL requests
- Automate manual tests and integrate them into a CI/CD pipeline to ensure that no code changes interrupt the API in production.
- Communicate the expected behavior of an API by simulating endpoints and their responses without having to configure a backend server
- Generate and publish nice machine-readable documentation to make the API easier to use.
- Stay up-to-date on the health of your API by checking performance and response times at scheduled intervals.
- Collaborate in real time with built-in version control.
Python HTTP libraries
The simplest library to make requests with Python is probably the Requests library and it can be installed using:
pip3 install requests
# or conda install requests
and then we can access the content as:
import requests
# creating a GET request
r = requests.get('https://jsonplaceholder.typicode.com/posts/1')
# getting the response elements
response_dict = r.json
response_header = r.headers
status_code = r.status_code
We can of course pass a body, headers with this library.
r = requests.put(
url='https://jsonplaceholder.typicode.com/posts/1',
data={"id": 1, "content": "hello world"},
headers={"Content-Type": "application/json"}
)
Another popular library is the Urllib library installed natively with Python but more difficult to use.
The REST standard
Each API has a specific architecture, as well as rules to respect that determine the data formats and commands accepted to communicate with. In order to promote accessibility and standardization of APIs for developers, there are now classic API architectures that are very often used. For example, REST (Representational State Transfer) is an architecture that is very often used in the creation of WEB services. It allows applications to communicate with each other regardless of the operating system via the HTTP protocol. A REST API uses HTTP requests to communicate and must respect the following principles:- Client-server architecture : the client must make HTTP requests to request resources. There is independence between the client side and the server application so that changes to one endpoint do not affect the others.
- Uniform interface : simplified architecture that allows each part to evolve independently. To speak of a uniform interface, 4 constraints must be respected
- Resource identification in requests : resources are identified in requests and are separated from the representations returned to the client.
- Resource manipulation by representations : clients receive files that represent resources. These representations must contain enough information to be modified or deleted.
- Self-describing messages : all messages returned to the client contain enough information to describe how the client should handle the information.
- Hypermedia as an engine for application state change (HATEOAS) : after accessing a resource, the REST client must be able to discover all other available actions through hyperlinks.
- Cached : the client must be able to cache the data that the API provides in response, you can check this link for further informations.
- Layered system : communication can take place through intermediate servers (proxy servers or load balancing devices).
- Stateless : no information is stored between two requests and the API treats each request as a first request.
GET
to retrieve informationPOST
to add new data to the serverPUT
to modify data already on the serverDELETE
to delete data
FastAPI
FastApi is a modern, fast (high-performance), web framework for building APIs with Python 3.7+ based on standard Python type hints. It is designed to be easy to use, fast to develop with, and provide high performance, making it an excellent choice for building RESTful APIs. Here's a brief overview and key features of FastAPI:- Fast: As the name suggests, FastAPI is designed to be high-performance. It is built on top of Starlette and Pydantic, which contribute to its speed.
- Type Annotations: One of the standout features is the use of Python type hints to define the types of request and response data. This not only serves as documentation but also enables automatic data validation and serialization.
- Automatic Documentation: FastAPI generates OpenAPI and JSON Schema documentation automatically based on your Python type hints. You can explore and interact with your API using Swagger UI or ReDoc.
- Asynchronous Support: FastAPI fully supports asynchronous programming using Python's async and await keywords. This allows for efficient handling of I/O-bound operations.
- Dependency Injection System: FastAPI has a built-in dependency injection system that makes it easy to manage dependencies and share common resources across different parts of your application.
- OAuth2 and JWT Authentication: It provides built-in support for OAuth2 and JWT authentication, making it easier to secure your APIs.
- Data Validation and Serialization: FastAPI uses Pydantic models for data validation and serialization. This allows you to define data models with type hints, and FastAPI will automatically handle validation and serialization/deserialization.
- WebSocket Support: In addition to traditional HTTP APIs, FastAPI supports WebSocket communication, allowing for real-time bidirectional communication.
- Cors (Cross-Origin Resource Sharing) Middleware: FastAPI includes middleware for handling Cross-Origin Resource Sharing, making it easy to control which domains can access your API.
- Dependency Injection System: FastAPI has a sophisticated dependency injection system that simplifies handling dependencies in your API, making your code clean and modular.
Gettign started:
In this section we will look at the basics of FastAPI. The first step is to install the fastapi and uvicorn libraries. uvicorn is a library that allows you to launch the server created by FastAPI.pip install fastapi uvicorn
Here's a simple example of a FastAPI application (create main.py
file):
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, query_param: str = None):
return {"item_id": item_id, "query_param": query_param}
We can run the app with Uvicorn:
uvicorn main:app --reload
Visit https://127.0.0.1:8000 in your browser, and you should see the automatically generated Swagger documentation. In the conolse, the folloing line should be observed:
INFO: Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to quit)This line gives us the address at which the API works.
In another console, issue the following command to query the endpoint /
:
curl -X GET http://127.0.0.1:8000/
In this section we will see how to use dynamic routing and then how to pass arguments directly into a request. Finally, we will see how FastAPI generates documentation for arguments.
- Dynamic Routing: Dynamic routing allows you to generate endpoints automatically. Modify the main.py file by pasting the following lines :
from fastapi import FastAPI users_db = [ { 'user_id': 1, 'name': 'Alice', 'subscription': 'free tier' }, { 'user_id': 2, 'name': 'Bob', 'subscription': 'premium tier' }, { 'user_id': 3, 'name': 'Clementine', 'subscription': 'free tier' } ] app = FastAPI() @app.get('/') def get_index(): return { 'greetings': 'welcome' } @app.get('/users') def get_users(): return users_db @app.get('/users/{userid:int}') def get_user(userid): try: user = list(filter(lambda x: x.get('user_id') == userid, users_db))[0] return user except IndexError: return {} @app.get('/users/{userid:int}/name') def get_user_name(userid): try: user = list(filter(lambda x: x.get('user_id') == userid, users_db))[0] return {'name': user['name']} except IndexError: return {} @app.get('/users/{userid:int}/subscription') def get_user_suscription(userid): try: user = list(filter(lambda x: x.get('user_id') == userid, users_db))[0] return {'subscription': user['subscription']} except IndexError: return {}
Here
users_db
is the database and with this code, we want to:GET /
returns a welcome messageGET /users
returns the entire databaseGET /users/userid
returns all the data for a user based on its id. userid should be an integer. If the userid provided does not match an existing user, an empty dictionary will be returned.GET /users/userid/name
returns the name of a user based on its id. userid should be an integer. If the userid provided does not match an existing user, an empty dictionary will be returned.GET /users/userid/subscription
returns the subscription type of a user based on their id. userid should be an integer
We notice that the interface to use these requests allows to pass arguments for dynamic routing :
We can see below, how dynamic routing works here:
- Request string: We have seen how to pass data to the API using dynamic routing. In this part, we will see how to do it using the query string. FastAPI makes it easy to specify which arguments can be passed via the query string. In the following example, we will define a function that can take an argument
argument1
. This argument must be passed in the query string.from fastapi import FastAPI api = FastAPI() @api.get('/') def get_index(argument1): return { 'data': argument1 }
When using the curl command in the command prompt, we can interact with web servers by sending HTTP requests. Let's break down two scenarios:
In the first case, with the command, we're sending a GET request (
-X GET
) to http://127.0.0.1:8000/ with an additional query parameterargument1
set tohello world
. The server processes this request and returns the value of theargument1
parameter, which in this case is "hello world" (in first case). Whereas in second case, we're sending a GET request to the same server but without any query parameters. However, the server expects theargument1
parameter to be present. Since it's missing, the server responds with a422 Unprocessable Entity
error. This error indicates that the request couldn't be processed because the required field (argument1
) is missing.We can see this beahviour through the
docs/
documentation. We need to provide the argument otherwise, when executed it will produce an error. We see in the interface that theargument1
field is required. Going down the interface, we can also see the codes that this route can return: 200 if the request succeeds and 422 if the request does not have the required field.- Another example:
This basically gives error, if provide not an integer as argument. However, when provided a strong, it gives our output as@app.get('/typed') def get_typed(argument1: int): return { 'data': argument1 + 1 }
{"data" : 123}
whenargument1 = 123
- Example on Optional class: Finally, we can choose to have an optional argument. For this we can use the Optional class of the typing library. However, a default value must be provided.
where the function takes two parameters: 'a' (an integer) and 'b' (an optional integer), with a default value of 'None'. If 'b' has a value (i.e., it's not 'None'), 'a' and 'b' are added together, and the result is assigned to the variable result. If 'b' is None, 'a' is incremented by '1', and the result is assigned to result.from typing import Optional @api.get('/addition') def get_addition(a: int, b: Optional[int]=None): if b: result = a + b else: result = a + 1 return { 'addition_result': result }
- Another example:
- Request body: To pass data to the API, the FastAPI library relies on the use of the
BaseModel
class frompydantic
to specify the form of the query body. We will first create an Item class inherited from the BaseModel class.from fastapi import FastAPI from typing import Optional from pydantic import BaseModel class Item(BaseModel): itemid: int description: str owner: Optional[str] = None app = FastAPI() @app.post('/item') def post_item(item: Item): return { 'itemid': item.itemid }
Here the Item class has the attributes itemid which must be an integer, description which must be a string and owner which is an optional string. We are going to create a route for which we will have to associate a query body containing these attributes. We can open the OpenAPI and inspect the route description
We can see that the annotations given here are no longer counted as parameters (arguments to be specified in the query string). On the other hand, we must now specify the request body ("Request body required"). Thanks to the inheritance of the BaseModel class, we could change the interpretation of the annotation by FastAPI. The use of this BaseModel class as a parent class thus allows a route to accept a body. We force the body of the request to respect a certain schema with certain values that can be optional. Moreover, the use of this class allows to ignore fields which are not predefined. Finally, the BaseModel class allows to easily return all the attributes of the body of a query that have been created in JSON format without having to specify this definition. Note that we can use thePOST /item
:typing
andpydantic
libraries to give more complex types to our data. The following example shows a use of these libraries:
Pydantic also allows to use "exotic" types like http URLs, IP addresses, ... If you want to explore these data types, you can go to this https://docs.pydantic.dev/latest/concepts/types/.from pydantic import BaseModel from typing import Optional, List class Owner(BaseModel): name: str address: str class Item(BaseModel): itemid: int description: str owner: Optional[Owner] = None ratings: List[float] available: bool
- Headers: In this part, we will see how to pass data to the server via the request headers. This command can be very useful to pass authentication tokens or to check the content type, the origin of the request, ... For this, we can use the
Header
class offastapi
. For example, the following function checks the value ofUser-Agent
. This header is used to determine the source of a request :from fastapi import Header @api.get('/headers') def get_headers(user_agent=Header(None)): return { 'User-Agent': user_agent }
Example Fastapi
In the above case-1 (Dynamic Routing), we have seen theusers_db
. Now we want to add folloing routes:
PUT /users
creates a new user in the database and returns the data for the created user. The data about the new user must be provided in the body of the query.
POST /users/userid
modifies the data for the user identified by userid and returns the data for the modified user. The data about the user to be modified must be supplied in the body of the request
DELETE /users/userid
deletes the user identified by userid and returns a confirmation of the deletion.
from fastapi import FastAPI
from typing import Optional
from pydantic import BaseModel
users_db = [
{
'user_id': 1,
'name': 'Alice',
'subscription': 'free tier'
},
{
'user_id': 2,
'name': 'Bob',
'subscription': 'premium tier'
},
{
'user_id': 3,
'name': 'Clementine',
'subscription': 'free tier'
}
]
app = FastAPI()
class User(BaseModel):
userid: Optional[int]
name: str
subscription: str
@app.put('/users')
def put_users(user: User):
new_id = max(users_db, key=lambda u: u.get('user_id'))['user_id']
new_user = {
'user_id': new_id + 1,
'name': user.name,
'subscription': user.subscription
}
users_db.append(new_user)
return new_user
@app.post('/users/{userid:int}')
def post_users(user: User, userid):
try:
old_user = list(
filter(lambda x: x.get('user_id') == userid, users_db)
)[0]
users_db.remove(old_user)
old_user['name'] = user.name
old_user['subscription'] = user.subscription
users_db.append(old_user)
return old_user
except IndexError:
return {}
@app.delete('/users/{userid:int}')
def delete_users(userid):
try:
old_user = list(
filter(lambda x: x.get('user_id') == userid, users_db)
)[0]
users_db.remove(old_user)
return {
'userid': userid,
'deleted': True
}
except IndexError:
return {}
Conclusion
FastAPI is a powerful and easy-to-use framework for building APIs with Python. Its combination of type hinting, automatic documentation, and high performance makes it a strong choice for a wide range of API development projects. Whether you're building a small RESTful API or a complex real-time application, FastAPI provides the tools and features to make development efficient and enjoyable.
FastApi documentation
- Customize the documentation of functions: To add comments to the use of an endpoint, we can use the docstring of the function. We can also give a name to our API via the FastAPI class.
from fastapi import FastAPI app = FastAPI( title="My API", description="My own API powered by FastAPI. Created by Arun Kumar Pandey.", version="1.0.1") @app.get("/") def read_root(): """Returns greetings """ return {"Hello": "World"}
- If we provide description of the function as a docstring, the interface automatically shows the description straight from the docstring of the function. We can also add the name argument to the decorator to name the route in OpenAPI: by default, route names are created from the titles of the functions used:
Get Index
forread_root
. For example, we can set decorator name@api.get('/', name="Hello World") def get_index(): """Returns greetings """ return {'greetings': 'welcome'}
- Documentation of the function body: We will now see how FastAPI handles the definition of models from the BaseModel class:
In the Schemas tab, the description of the Computer class is now available. Here we have also added description on the headers. You can see in the function description that the header is documented.from pydantic import BaseModel from typing import Optional class Computer(BaseModel): computerid: int cpu: Optional[str] gpu: Optional[str] price: float @app.put('/computer', name='Create a new computer') def get_computer(computer: Computer): """Creates a new computer within the database """ return computer
- Organizing the documentation: By default, all functions are documented in a default tab. However, you can choose to organize these functions in different parts. To do this, one must specify the tags argument in the decorator.
Now note down the various parts added here. The functions are now divided into different parts. The same function can be put in several parts. We could add a description for the different parts using the openapi_tags argument of the FastAPI class constructor. We can also change the address of the OpenAPI and Redoc documentations using theapp = FastAPI( title="My FastApi", description="My own API powered by FastAPI. Created by Arun Kumar Pandey.", version="1.0.1", openapi_tags=[ { 'name': 'home', 'description': 'default functions' }, { 'name': 'items', 'description': 'functions that are used to deal with items' } ] ) @app.get('/', tags=['home']) def get_index(): """returns greetings """ return { 'greetings': 'hello world' } @app.get('/items', tags=['home', 'items']) def get_items(): """returns an item """ return { 'item': "some item" }
docs_url
orredoc_url
arguments. If these arguments are set to None, these endpoints are disabled. Finally, one can choose to change the address of the OpenAPI manifest with theopenapi_url
argument.app = FastAPI( # Change the address of the OpenAPI documentation docs_url="/custom-docs", # Set to the desired URL path, e.g., "/custom-docs" # Change the address of the ReDoc documentation redoc_url=None, # Set to None to disable the ReDoc endpoint or specify a custom URL # Change the address of the OpenAPI manifest openapi_url="/custom-openapi" # Set to the desired URL path, e.g., "/custom-openapi" )
- Using errors: Errors are an important tool when developing applications: they allow to easily give indications about a bad handling of the application. In particular, we can give information by using the status code of the response but also by giving information in the body of the response. Up to now, we have been able to obtain errors of types 500, 404 and 422. In this part we will see how to create more custom errors.
app = FastAPI()
data = [1, 2, 3, 4, 5]
@app.get('/data')
def get_data(index):
return {
'data': data[int(index)]
}
If we open the OpenAPI documentation, we can see that two errors are proposed. In this case, we can see that if the value of index is greater than 4 or less than 0, we may get an IndexError. Also, if index is not an integer, we should get a ValueError.
We have also seen that FastAPI generates its own errors for routes not found (404: you can make a GET /nowhere request to see for yourself) or for data formats that do not correspond to the defined expectations (422: in particular via the use of annotations or classes inherited from BaseModel). We will use try-except blocks to catch Python errors and return HTTPException with the associated HTTP codes.
from fastapi import HTTPException
app =FastAPI()
@app.get('/data')
def get_data(index):
try:
return {
'data': data[int(index)]
}
except IndexError:
raise HTTPException(
status_code=404,
detail='Unknown Index')
except ValueError:
raise HTTPException(
status_code=400,
detail='Bad Type'
)
So we can easily change the error codes and the data returned on error. For the detail argument, we can give a dictionary or any other structure that can be interpreted as a JSON.
Finally, if we want to change the form of the data returned on error, we can create our own exceptions and pass them to the @api.exception_handler
decorator.
from fastapi import FastAPI
from fastapi import Request
from fastapi.responses import JSONResponse
import datetime
app = FastAPI()
class MyException(Exception):
def __init__(self,
name : str,
date: str):
self.name = name
self.date = date
@app.exception_handler(MyException)
def MyExceptionHandler(
request: Request,
exception: MyException
):
return JSONResponse(
status_code=418,
content={
'url': str(request.url),
'name': exception.name,
'message': 'This error is my own',
'date': exception.date
}
)
@app.get('/my_custom_exception')
def get_my_custom_exception():
raise MyException(
name='my error',
date=str(datetime.datetime.now())
)
Let's take some time to describe this code. In the first block, we define a new Exception. We give it the attributes name
and date
. In the second block, we tell FastAPI how to react when the exception is raised. We give a response of type JSON to return, giving it a status_code
and a status. We can then access the attributes of the request or the exception to return them in a JSON. Finally the last block allows us to define a route that generates this error.
JSON: JavaScript Object Notation
JSON, or JavaScript Object Notation, is a lightweight data-interchange format. It is easy for humans to read and write and easy for machines to parse and generate. It is widely used for data exchange between a server and a web application (i.e. is used extensively in web applications and APIs), as well as for configuration files and data storage.Basics of JSON
- Data Structure:
- JSON represents data as key-value pairs, similar to a dictionary in Python or an object in JavaScript.
- Data is organized in name/value pairs, where the name (a string) is followed by a colon, and the value can be a string, number, boolean, array, or another JSON object.
{ "name": "John Doe", "age": 30, "isStudent": false, "courses": ["Math", "History", "Science"], "address": { "street": "123 Main St", "city": "Anytown", "zip": "12345" } }
- Data Types:
JSON data is organized into key-value pairs. Each key-value pair is enclosed in curly braces
{}
. The key is a string of characters and the value can be any of the following types:- Strings: Enclosed in double quotes.
- Numbers: Integer or floating-point.
- Booleans:
true
orfalse
. - Arrays: Ordered list of values.
- null: A special value that indicates the absence of a value.
- Objects: Unordered collection of key/value pairs.
- JSON vs. JavaScript Object: While JSON syntax resembles JavaScript object syntax, they are not exactly the same. In JSON, keys must be strings, and strings must be enclosed in double quotes.
Use cases:
- Data Exchange: Commonly used for sending and receiving data between a server and a web application. APIs often return data in JSON format.
- Configuration Files: Used for configuration settings in various applications. Easy to read and write manually.
- Storage: NoSQL databases often use JSON-like formats to store data.
- Serialization: Objects in programming languages can be serialized into JSON for data interchange.
JSON Schema:
JSON Schema is a vocabulary that allows you to annotate and validate JSON documents. It provides a way to describe the structure of JSON data for documentation, validation, and interaction.For creating JSON schema from scratch, see the Create a nested data structure.
Example: the expected structure and constraints for a JSON object representing information about an individual, particularly someone's personal details, such as a student. Let's break down the schema:- Person Details:
- The root object represents details about an individual, likely a person.
- Name:
- The "name" property is expected to be a string, representing the person's name.
- Age:
- The "age" property is expected to be an integer, representing the person's age.
- Student Indicator:
- The "isStudent" property is a boolean indicating whether the person is a student or not.
- Courses:
- The "courses" property is an array of strings, presumably representing the courses the student is enrolled in.
- Address:
- The "address" property is an object with details about the person's address.
- Address Details:
- It includes "street," "city," and "zip" properties, all of which are expected to be strings.
- These address details are marked as required.
- Overall Requirements:
- The entire object must have the "name," "age," and "isStudent" properties.
- The "address" property is optional, but if present, it must include the required details ("street," "city," and "zip").
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"isStudent": {
"type": "boolean"
},
"courses": {
"type": "array",
"items": {
"type": "string"
}
},
"address": {
"type": "object",
"properties": {
"street": {
"type": "string"
},
"city": {
"type": "string"
},
"zip": {
"type": "string"
}
},
"required": ["street", "city", "zip"]
}
},
"required": ["name", "age", "isStudent"]
}
This schema defines the expected structure and types of the JSON object. This can be understood as:
object
├─ name: string
├─ age: integer
├─ isStudent: boolean
├─ courses: array
│ └─ items: string
└─ address: object
├─ street: string
├─ city: string
└─ zip: string
This schema could be used, for example, in a system where user details need to be validated before they are stored or processed. It ensures that the data conforms to a specific structure and type, enhancing data integrity and providing a basis for consistent data exchange.
Conclusion:
JSON is a versatile and widely adopted format for data interchange. Its simplicity, human-readability, and ease of integration make it a popular choice for APIs, configuration files, and data storage. Understanding JSON is essential for anyone working with web development, APIs, or data exchange between different systems.References
Some other interesting things to know:
- Visit my website on For Data, Big Data, Data-modeling, Datawarehouse, SQL, cloud-compute.
- Visit my website on Data engineering