API Documentation

API Standards

OpenAPI 2.0: Adheres to Swagger 2.0 specifications, supporting the definition of APIs that adhere to RESTful principles. This specification provides a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection.

OpenAPI 3.0: Employs OpenAPI 3.0, ensuring compatibility with modern API standards and offering improvements over the 2.0 specification, including support for describing more complex APIs through features like callbacks, links, and enhanced security definitions.

Authentication: Utilizes API-Key authentication with the key required in the request header x-api-key. This method ensures secure access to the API by validating the client's identity through a unique API key assigned during registration.

Common Patterns Across Endpoints

  • CRUD Operations: Provides a comprehensive toolkit for data entity lifecycle management via CRUD operations (Create, Read, Update, Delete). This enables a full spectrum of database operations directly through the API, facilitating easy data manipulation and management.
  • Endpoint Naming: Uses a straightforward naming convention /schema_name/entity for endpoint paths, enhancing clarity and consistency across the API. This helps in logically organizing the API's resources and makes the endpoints predictable and intuitive to use.
  • Responses and Status Codes: Utilizes HTTP status codes for indicating operation outcomes, enabling clear communication of success, failure, and error states. Common codes include 200 for successful operation, 400 for bad requests, 401 for unauthorized, and 500 for internal server erros.
  • Query Parameters for Filtering and Pagination: Supports efficient data retrieval through query parameters, allowing for filtering based on specific attributes and pagination to manage large data sets. This provides clients with the flexibility to request exactly what they need, optimizing data access and transfer.
  • Path Variables for Specific Entities: Allows direct operations on specific entities via path variables, enabling actions like retrieving or deleting an entity based on its unique identifier within the URL path.
  • Bulk Operations via Transactions: Enables executing multiple operations in a single API request for efficiency and consistency. This is particularly useful for batch processed transactions and reduces the need for multiple round trips between the client and server.
  • Use of Extended Properties: The API uses extended properties like x-database-schema and x-database-table, which are not part of the standard OpenAPI specification. These provide a clear mapping to underlying database structures, making the API more integrated with its backend storage layer.

Schema Definitions and Relationships

Detailed schema definitions for each data entity are provided, reflecting a relational database design and facilitating a comprehensive understanding of entity relationships. These definitions include the data type of each field, mandatory fields, unique constraints, and relationships with other entities, enabling developers to effectively model and interact with the data.

Unified API Design Principles

  • Entity Lifecycle Management: Each data entity comes with intuitive CRUD operation mappings.
  • Customizable Data Retrieval: Implements flexible query parameters and sorting mechanisms.
  • Uniform Operation Interface: Ensures clear and predictable feedback through standardized HTTP status codes.
  • Transactional Support for Bulk Changes: Offers atomic and consistent handling of bulk data operations.

GET Endpoint Structure

GET endpoints are designed to retrieve existing data about entities within the API's `schema_name` schema. These endpoints allow clients to query and obtain information on entities based on specific criteria or retrieve details of a particular entity by its identifier.

Get by ID: For querying entities based on criteria or retrieving all entities of a specific type, the GET requests are made to /schema_name/entity. To retrieve a specific entity by its identifier, the request is made to /schema_name/entity/{id}, where `{id}` is the unique identifier of the entity.

Get: Query parameters can be used to filter the list of entities returned by the GET request. Common parameters could be `name`, `email`, `phone`, and date range filters like `created` and `modified`. The use of these parameters allows for refined searches tailored to the client's needs.

Example:

GET /schema_name/entity?name=JohnDoe&email=john@example.com HTTP/1.1

Headers:

While GET requests primarily focus on retrieving data and may not require a body, it is essential to include any necessary headers for authentication. The x-api-key header should be included with the client's API key for secure access.

Responses and Status Codes:

  • 200 OK: The request was successful, and the response body contains the requested data about the entities.
  • 400 Bad Request: The request cannot be processed, often due to invalid query parameters.
  • 401 Unauthorized: Authentication has failed, likely due to an incorrect or missing API key.
  • 500 Internal Server Error: An internal server error occured, often because server side problems.

Example:

GET /schema_name/entity?email=john@example.com HTTP/1.1
Host: 127.0.0.1
x-api-key: YourApiKeyHere

This example demonstrates a GET request to query entities based on the `email` attribute within the `schema_name` schema.

OpenAPI Example:

This example illustrates how the generated GET requests are represented in the OpenAPI documentation.

{
    "/schema_name/entity": {
        "get": {
            "x-swagger-router-controller": "GenericController",
            "x-database-schema": "schema_name",
            "x-database-table": "entity",
            "description": "GET",
            "operationId": "schema_name.entity.get",
            "parameters": [
                {
                    "name": "ID",
                    "in": "query",
                    "required": false,
                    "description": "\"ID\" of entity",
                    "style": "pipeDelimited",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "integer",
                            "format": "int64",
                            "minimum": 1
                        },
                        "uniqueItems": true
                    }
                },
                {
                    "name": "STATUS",
                    "in": "query",
                    "required": false,
                    "description": "\"STATUS\" of entity",
                    "style": "pipeDelimited",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "boolean"
                        },
                        "uniqueItems": true
                    }
                },
                {
                    "name": "EMAIL",
                    "in": "query",
                    "required": false,
                    "description": "\"EMAIL\" of entity",
                    "style": "pipeDelimited",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "format": "email",
                            "minLength": 5,
                            "maxLength": 255,
                            "pattern": "/^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/"
                        },
                        "uniqueItems": true
                    }
                },
                {
                    "name": "NAME",
                    "in": "query",
                    "required": false,
                    "description": "\"NAME\" of entity",
                    "style": "pipeDelimited",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 255
                        },
                        "uniqueItems": true
                    }
                },
                {
                    "name": "CREATED",
                    "in": "query",
                    "required": false,
                    "description": "\"CREATED\" of entity",
                    "style": "pipeDelimited",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "format": "date-time",
                            "minLength": 1,
                            "maxLength": 255
                        },
                        "minItems": 1,
                        "maxItems": 2,
                        "uniqueItems": true
                    }
                },
                {
                    "name": "MODIFIED",
                    "in": "query",
                    "required": false,
                    "description": "\"MODIFIED\" of entity",
                    "style": "pipeDelimited",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "format": "date-time",
                            "minLength": 1,
                            "maxLength": 255
                        },
                        "minItems": 1,
                        "maxItems": 2,
                        "uniqueItems": true
                    }
                },
                {
                    "name": "$select",
                    "in": "query",
                    "description": "Specify one or more properties to retrieve (horizontal filtering).",
                    "required": false,
                    "style": "pipeDelimited",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "enum": [
                                "id",
                                "status",
                                "email",
                                "name",
                                "created",
                                "modified"
                            ]
                        },
                        "uniqueItems": true
                    }
                },
                {
                    "name": "$orderBy",
                    "in": "query",
                    "description": "\"orderBy\": to order the result asc(+) or desc(-) by columns. The '+' prefix is optional.",
                    "required": false,
                    "style": "pipeDelimited",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "enum": [
                                "-id",
                                "+id",
                                "id",
                                "-status",
                                "+status",
                                "status",
                                "-email",
                                "+email",
                                "email",
                                "-name",
                                "+name",
                                "name",
                                "-created",
                                "+created",
                                "created",
                                "-modified",
                                "+modified",
                                "modified"
                            ]
                        },
                        "uniqueItems": true
                    }
                },
                {
                    "name": "$offsetId",
                    "in": "query",
                    "description": "Set offset by ID",
                    "required": false,
                    "schema": {
                        "type": "integer",
                        "format": "int64",
                        "minimum": 0
                    }
                },
                {
                    "name": "$limit",
                    "in": "query",
                    "description": "Limit the amount of fetched items and specify the amount of skipped items",
                    "required": false,
                    "style": "form",
                    "explode": false,
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "integer",
                            "format": "int64"
                        },
                        "maxItems": 2
                    }
                }
            ],
            "responses": {
                "200": {
                    "description": "OK",
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "array",
                                "items": {
                                    "$ref": "#/components/schemas/schema_name.entity.get"
                                }
                            }
                        }
                    }
                },
                "400": {
                    "description": "Bad Request"
                },
                "401": {
                    "description": "Unauthorized"
                },
                "500": {
                    "description": "Internal Server Error"
                }
            },
            "summary": "Search and sort entity by its paramters. Pagination can be used also.",
            "tags": [
                "schema_name.entity"
            ]
        }
    }
}           

GET Query Parameters

Various filter options are available in the API, designed to offer the flexibility to retrieve data efficiently and effectively. These filtering options include basic filtering, searching by multiple values, range search for dates, LIKE operations on string data types, and combined searches.

Horizontal Filtering

Horizontal filtering allows the filtering of attributes before the entity is retrieved.

  • Usage: Append the $select keyword and the needed attribute names to the query parameters in your GET request.
  • Example: To retrieve entities with only specific attributes, your request could look like this:
    GET /entity?$select=email|name|status HTTP/1.1
  • SQL Equivalent:
    SELECT email, name, status FROM entity;

Vertical Filtering

Vertical filtering allows for the retrieval of entities based on exact matches of their attributes.

  • Usage: Append the attribute and its desired value to the query parameters in your GET request.
  • Example: To retrieve entities with a specific status, your request could look like this:
    GET /entity?status=true HTTP/1.1
  • SQL Equivalent:
    SELECT * FROM entity WHERE status = true;

Searching by Multiple Values

The API supports filtering by multiple values for both numeric and string attributes. This feature enables fetching entities that match any of the specified values.

  • Usage: Separate multiple values with a pipe (|) in the query parameters.
  • Example: To retrieve entities where numberAttribute is either 1, 2, or 3:
    GET /entity?numberAttribute=1|2|3 HTTP/1.1
  • SQL Equivalent:
    SELECT * FROM entity WHERE numberAttribute IN (1, 2, 3);

Range Search for Dates

Range searches allow you to retrieve entities based on a date range, useful for filtering entities created or modified within specific timeframes.

  • Usage: Specify the start and end dates separated by a pipe (|). The dates should follow the ZULU time format.
  • Example: To filter entities created between January 1, 2023, and December 31, 2023, in ZULU time format:
    GET /entity?created=2023-01-01T00:00:00Z|2023-12-31T23:59:59Z HTTP/1.1
  • SQL Equivalent:
    SELECT * FROM entity WHERE created BETWEEN '2023-01-01T00:00:00Z' AND '2023-12-31T23:59:59Z';

Like Operation on String Data Types

The LIKE operation offers pattern matching for string attributes, following the SQL LIKE syntax. It's ideal for searching text fields when you don't have exact match criteria.

  • Usage: Use % as a wildcard for any sequence of characters, and _ for any single character.
  • Example: To find entities where name contains "abc" or "xyz":
    GET /entity?name=%abc%|%xyz% HTTP/1.1
  • SQL Equivalent:
    SELECT * FROM entity WHERE name LIKE '%abc%' OR name LIKE '%xyz%';

Combined Searches

The API supports combining multiple filter types in a single query, enabling precise control over the data retrieved.

  • Usage: Combine different filtering options in your query parameters as needed.
  • Example: To perform a combined search for entities created in 2023 with a status of true and a name containing "abc" or "xyz":
    GET /entity?created=2023-01-01T00:00:00Z|2023-12-31T23:59:59Z&status=true&name=%abc%|%xyz% HTTP/1.1
  • SQL Equivalent:
    SELECT * FROM entity 
    WHERE created BETWEEN '2023-01-01T00:00:00Z' AND '2023-12-31T23:59:59Z' 
    AND status = true 
    AND (name LIKE '%abc%' OR name LIKE '%xyz%');

GET Pagination

Efficient data retrieval in applications that handle large volumes of data is crucial. The API implements advanced pagination techniques allowing for effective data management and retrieval by segmenting data into manageable chunks.

Pagination Techniques

This guide assumes that each resource in the API has at a minimum an id and a created column where id is an integer and created a date-time type.

1. Offset Pagination with Sorting

  • Use Case: Best suited for ordered data retrieval while controlling the starting point based on item IDs.
  • Parameters:
    • $limit: The maximum number of items to return.
    • $offsetId: Excludes items where the "id" is less than or equal to the specified $offsetId.
    • $orderBy: Specifies the column to sort by. Use - for descending order.
  • Example:
    GET /schema/table?$limit=10&$offsetId=20&$orderBy=-id HTTP/1.1
  • SQL Equivalent:
    SELECT * FROM schema.table WHERE id > 20 ORDER BY id DESC LIMIT 10;

2. Combined Limit and Offset

  • Use Case: Simplifies request syntax by combining the page size and starting offset into one parameter.
  • Parameters:
    • $limit: A composite parameter where the first number is the page size and the second is the offset count.
  • Example:
    GET /schema/table?$limit=20,10 HTTP/1.1
  • SQL Equivalent:
    SELECT * FROM schema.table LIMIT 10 OFFSET 20;

3. Filtering with Pagination and Sorting

  • Use Case: Retrieves data based on specific filters with support for pagination and sorting.
  • Parameters:
    • $limit: Sets the maximum number of items returned.
    • $orderBy: Determines the order of the results.
    • Filter Parameters: Additional parameters for filtering.
  • Example:
    GET /schema/table?$limit=10&$orderBy=-created&created=2023-03-23|2025-03-23 HTTP/1.1
  • SQL Equivalent:
    SELECT * FROM schema.table WHERE created BETWEEN DATE('2023-03-23T17:42:00.667Z') AND DATE('2025-03-23T17:42:06.107Z') ORDER BY created DESC LIMIT 10;

Pagination Key Considerations

  • Understanding $offsetId: Functions by excluding records with an ID less than the specified value.
  • Using $offsetId for Efficiency: More efficient than numeric offsets for large datasets.
  • Use filtering parameters alongside $limit and $orderBy to finely tune data retrieval.
  • Choose page size to balance granularity of data retrieval and performance.
  • Provide navigational controls for easy page movement in a UI.

POST Endpoint Structure

POST endpoints are designed for the creation of new instances of data entities within the API's `schema_name` schema. These endpoints enable clients to submit new data records for creation, expanding the dataset with new entities as needed.

Endpoint Naming and Path:

The POST requests target the endpoint following the pattern /schema_name/entity, indicating the creation of new entities within the specified schema. This naming convention ensures that the API's structure remains clear and consistent.

Request Body Structure:

The request body must contain all the necessary attributes required to create new entities, as defined by the entity’s schema. This includes specifying values for all required fields and adhering to any defined data types and constraints.

Batch Operations are supported also. Include as many entities in the request body to create them all at once.

[
  {
    "name": "New Entity Name",
    "attribute1": "Value1",
    "attribute2": "Value2"
  },
  {
    "name": "Another Entity Name",
    "attribute1": "Value3",
    "attribute2": "Value4"
  }
]

Note: Replace "attribute1", "attribute2", etc., with the actual attribute names and values relevant to the entity being created.

Headers:

Every POST request must include the Content-Type: application/json header to indicate the format of the request body. Authentication is managed through the inclusion of the x-api-key header, containing the API key provided to the client.

Responses and Status Codes:

  • 200 OK: The entity was successfully created. The response body usually includes the newly created entity, including its unique identifier and any other relevant information.
  • 400 Bad Request: The request could not be processed, typically due to missing required fields or data that does not comply with the entity’s schema.
  • 401 Unauthorized: Indicates that authentication has failed, which may be due to an incorrect or missing API key.
  • 500 Internal Server Error: An internal server error occured, often because server side problems.

Example:

This example shows how to construct a POST request to create a new entity within the `schema_name` schema, including specifying attribute names and values.

POST /schema_name/entity HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
x-api-key: YourApiKeyHere

[{
  "name": "New Entity Name",
  "attribute1": "Value1",
  "attribute2": "Value2"
}]

OpenAPI Example:

This example illustrates how the generated POST requests are represented in the OpenAPI documentation.

{
    "/schema_name/entity": {
        "post": {
            "x-swagger-router-controller": "GenericController",
            "x-database-schema": "schema_name",
            "x-database-table": "entity",
            "description": "POST",
            "operationId": "schema_name.entity.post",
            "requestBody": {
                "content": {
                    "application/json": {
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/components/schemas/schema_name.entity.post"
                            }
                        }
                    }
                },
                "description": "Create entity",
                "required": true
            },
            "responses": {
                "200": {
                    "description": "OK",
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "array",
                                "items": {
                                    "$ref": "#/components/schemas/schema_name.entity.post"
                                }
                            }
                        }
                    }
                },
                "400": {
                    "description": "Bad Request"
                },
                "401": {
                    "description": "Unauthorized"
                },
                "500": {
                    "description": "Internal Server Error"
                }
            },
            "summary": "POST Request to create one or more entity",
            "tags": [
                "schema_name.entity"
            ]
        }
    }
}

PATCH Endpoint Structure

The PATCH endpoints are designed to update existing instances of data entities within the API. Unlike PUT, PATCH applies partial modifications to a resource. It requires the "id" of the entity to be included in the request, along with any fields that need updating. This approach allows for the specific fields of an entity to be updated without resubmitting the entire entity.

Endpoint Naming and Path:

PATCH requests are made to the endpoint following the pattern /schema_name/entity. This endpoint is used for idempotent operations, where providing the same "id" and data in multiple requests results in the same state of the entity without creating duplicates.

Request Body Structure:

The request body for a PATCH endpoint must include the "id" of the entity being updated, along with the specific fields that need modification. There's no need to include all attributes of the entity; only the "id" and those being updated are required. The "id" must be specified in the body not in the path of the request because you can update multiple items at once.

[
  {
	"id": 123,
	"name": "Updated Entity Name",
	"email": "updated_email@example.com"
  },
  {
    "id": 456,
    "name": "Another Updated Entity Name",
    "email": "another_updated_email@example.com"
  }
]

Headers:

All PATCH requests must include the Content-Type: application/json header. Authentication is handled through the x-api-key header, which must contain the client's API key.

Responses and Status Codes:

  • 200 OK: The entity was successfully updated.
  • 400 Bad Request: The request body does not match the entity's schema, violates constraints, or the "id" is missing.
  • 401 Unauthorized: Authentication failed, likely due to an invalid API key.
  • 500 Internal Server Error: An internal server error occured, often because server side problems.

Example:

This example demonstrates a PATCH request that updates specific fields of an entity, using its "id" for identification.

PATCH /schema_name/entity HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
x-api-key: YourApiKeyHere

[{
  "id": 123,
  "name": "Updated Entity Name",
  "email": "updated_email@example.com"
}]

OpenAPI Example:

This example illustrates how the generated PATCH requests are represented in the OpenAPI documentation.

{
    "/schema_name/entity": {
        "patch": {
            "x-swagger-router-controller": "GenericController",
            "x-database-schema": "schema_name",
            "x-database-table": "entity",
            "description": "PATCH",
            "operationId": "schema_name.entity.patch",
            "requestBody": {
                "content": {
                    "application/json": {
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/components/schemas/schema_name.entity.patch"
                            }
                        }
                    }
                },
                "description": "Modify entity",
                "required": true
            },
            "responses": {
                "200": {
                    "description": "OK",
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "array",
                                "items": {
                                    "$ref": "#/components/schemas/schema_name.entity.patch"
                                }
                            }
                        }
                    }
                },
                "400": {
                    "description": "Bad Request"
                },
                "401": {
                    "description": "Unauthorized"
                },
                "500": {
                    "description": "Internal Server Error"
                }
            },
            "summary": "PATCH Request to update one or more entity. A PATCH request is idempotent, updating parts of a resource without full replacement.",
            "tags": [
                "schema_name.entity"
            ]
        }
    }
}

DELETE Endpoint Structure

DELETE endpoints are designated for removing existing entities. This operation allows clients to delete specific entities by their unique identifiers, ensuring data that is no longer needed can be appropriately managed and removed.

Endpoint Naming and Path:

DELETE requests target the endpoint /schema_name/entity/{id}, where `{id}` is the unique identifier of the entity to be deleted. This convention ensures precise targeting of the entity intended for deletion.

Headers:

All DELETE requests must include necessary headers for authentication. This typically involves the x-api-key header containing the client's API key, ensuring that only authorized users can delete entities.

Responses and Status Codes:

  • 200 OK: Indicates the entity was successfully deleted.
  • 400 Bad Request: The request was malformed, often due to an incorrect entity ID format.
  • 401 Unauthorized: Authentication failed due to an incorrect or missing API key.
  • 500 Internal Server Error: An internal server error occured, often because server side problems.

Example:

DELETE /schema_name/entity/123 HTTP/1.1
Host: 127.0.0.1
x-api-key: YourApiKeyHere

This example illustrates a DELETE request aimed at removing an entity identified by the ID `123` within the `schema_name` schema.

OpenAPI Example:

This example illustrates how the generated DELETE requests are represented in the OpenAPI documentation.

{
    "/schema_name/entity": {
        "delete": {
            "x-swagger-router-controller": "GenericController",
            "x-database-schema": "schema_name",
            "x-database-table": "entity",
            "description": "DELETE by ID",
            "operationId": "schema_name.entity.deleteById",
            "parameters": [
                {
                    "name": "ID",
                    "description": "Delete entity by ID",
                    "required": true,
                    "in": "path",
                    "style": "simple",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "integer",
                            "format": "int64",
                            "minimum": 1
                        },
                        "uniqueItems": true
                    }
                }
            ],
            "responses": {
                "200": {
                    "description": "OK"
                },
                "400": {
                    "description": "Invalid ID supplied"
                },
                "401": {
                    "description": "Unauthorized"
                },
                "500": {
                    "description": "Internal Server Error"
                }
            },
            "summary": "DELETE Request to delete one or more entity by ID",
            "tags": [
                "schema_name.entity"
            ]
        }
    }
}

Conclusion

The API documentation meticulously details the structured approach, diverse functionalities, and operational paradigms that characterize the API. By leveraging RESTful principles, adhering to OpenAPI specifications, and implementing secure authentication mechanisms, the API provides a robust platform for developers. The comprehensive coverage of CRUD operations, filtering techniques, pagination strategies, and transactional support for bulk changes exemplifies the API's flexibility and efficiency in data management. Through the strategic organization of endpoints, schema clarity, and a focus on interoperability, this documentation serves as an essential guide for developers, enabling them to harness the full potential of the API in building scalable, data-driven applications.

Author: Björn Berger

Email: bjoernberger.io@gmail.com