Base Microservice Specifications
{
"code":"someCode",
"message":"some message",
"target": "some target",
"details": ["some", "details"],
"innererror": {
"some complex json object here"
}
}
This document will describe basic functions all Microservices will have. The service's endpoints and controller files will be generated using OpenAPI's generator. As such, all microservices will follow OpenAPI's specification for code generation and documentation.
Software Topics
Versioning
To distinguish when containers were built, each container will have a buildinfo.json text file generated.
{
"timestamp":"2021-10-16T14:45:51.208015",
"base-version":"<insert base version here>",
"application-version":"<insert application version here>"
}
In the keys "base-version" and "application-version", developers will be required to follow the semantic versioning method, a sequence versioning method that follows a vX.Y.Z format (eg: v1.2.3), where X is a major change, Y is a minor change and Z is a patch change. Additional information can be added on the end of the version number to distinguish between test builds, nightly builds, ect. Example: v1.1.2-20211209Nightly.
Syslog Logging
In effort to centralize logging, all containers must log to a local syslog server. In the condition of Cloud 4.0, a syslog container will be active for programs to log to. A general syslog message will follow RFC 5424.
Event Messaging
Microservices will have their own event messaging topics that other microservices can subscribe to. For instance, an authentication microservice might have a topic for notifying other services when tokens have expired.
Database/caching
Each microservice will have its own cache. There will be a group of other services that provide database functionality.
Well Known Endpoints
/health
Can return HTTP status 200 for healthy.
or 500 for not healthy.
{
"error_id":1,
"error_description":"Some reason as a string"
}
This will not be available to end users
/docs
HTTP documentation generated with OpenAPI. This will not be available to end users
/build
Information about the application.
API Guidelines
All microservices will be required to follow the guidelines as closely as possible. This is to ensure stability of the ecosystem and to make developing client applications easier. The API guidelines will be a combination of the following existing guidelines:
Errors and Faults
An error occurs when the client passes invalid data to the server. Typically, errors are 4XX errors. When an error occurs, the service should be able to operate normally and not shut down.
A fault occurs when the service can not pass the correct response to the clients. Typically, faults are 5XX HTTP errors. Services should attempt to recover from a fault in order to avoid shutting down, however if they can not recover, the health of the service should be labeled as Unhealthy so it can be troubleshooted/restarted.
URL Structures
URLs should be easy to read by people. This includes the maximum length of a URL. While RFC7230 does not a specific URL length, the length of a URL should not exceed 2,083 characters (This is the maximum length for Internet Explorer).
Required Supported Methods
Services should support the required methods with no exception: GET, POST, DELETE, PUT, HEAD, OPTIONS, PATCH. If Open API is used, all of these methods will be available for use
POST Method
POST should be used for creating resources. A POST request should always return HTTP 201 (Created) with the new location of the object.
For example, consider an API for managing users, the client can POST data a URL to create users:
curl \
-X POST \
-d "{'user':'bob12345'}" \
-H "Content-Type: application/json"
Should return
201 Created
{
"location":"http://example.com/users/bob12345"
}
Additionally, the server may return the entire metadata for the object. The metadata object should have a "self" key to dictate the path to the object.
201 Created
{
"username":"bob",
"self":"http://example.com/users/bob",
"creation_date":"2022-09-20T15:00:00.00Z"
}
Required Request Headers
The following request headers should be sent with every request:
Date: The date the request was made. The date should follow ISO8601 and should always include a timezone. You can just use Z if using UTC timezone. The server should never assume the clients clock is accurate. If the time is vastly different than the servers time (+ or - 1 minute) the server should throw 403 with a reason of the clock being out of date.
Accept: Allows the server to see what type of content the client accepts
Accept-Encoding: Allows the server to see what encoding the client accepts
Accept-Charset: Allows the server to see what charset the client accepts (This will probably always be UTF-8)
Content-Type: Only used for POST or PUT calls, allows the server to handle incoming data
Prefer: Allows a client to change how much data the server returns. Can either be "return=minimal" or "return=representation"
Required Response Headers
Date: The date the request was sent to the user.
Content-Type: Content type of the response
Content-Encoding: Encoding the server encoded the response to
Custom Headers
Custom headers can not be required for a service to work. All services should work within the required headers above. Custom headers should in a generic format or a scoped format. Custom header data should never be JSON objects
Using Query Parameters
Personally identifiable information should never be used in a URLs query parameters. Additionally query parameters should avoid using json objects.
Handling Errors
When an error occurs, the server should return some sort of message. At minimal the error object should contain a "code" key and a "message" key.
{
"code":"someCode",
"message":"some message"
}
More complex errors can contain the "target", "details" and "innererror" keys like so:
{
"code":"someCode",
"message":"some message",
"target": "some target",
"details": ["some", "details"],
"innererror": {
"some complex json object here"
}
}
An example of a more complex error object is:
{
"error": {
"code": "BadArgument",
"message": "Previous passwords may not be reused",
"target": "password",
"innererror": {
"code": "PasswordError",
"innererror": {
"code": "PasswordDoesNotMeetPolicy",
"minLength": "6",
"maxLength": "64",
"characterTypes": ["lowerCase","upperCase","number","symbol"],
"minDistinctCharacterTypes": "2",
"innererror": {
"code": "PasswordReuseNotAllowed"
}
}
}
}
}
Special Note on Clients
When services are designed, they should be designed to be effortlessly used by simple HTTP tools such as curl or postman.
Collections
An inevitable topic that will come up during designing will be collections. A collection is just a simple group of similar objects. For example, a group of 'user' objects are 'users', a group of 'file' objects are 'files'. This should be used when designing endpoints.
For example, if you want to get a list of users, you might want to do the following:
curl \
-X GET \
-H "Content-Type: application/json" \
https://example.com/users
If you want to get a singular user, you would do:
curl \
-X GET \
-H "Content-Type: application/json" \
https://example.com/users/<user-id>
For querying and filtering the collection, URL headers should be used. Take care to not expose personal identifiable information!
curl \
-X GET \
-H "Content-Type: application/json" \
https://example.com/users?orderBy=name asc
curl \
-X GET \
-H "Content-Type: application/json" \
https://example.com/users?filter=name eq 'kyle'&orderBy=createdDate dec
Collections will always return a response with a "value" key like so:
{
"value": [
. . .
]
}
If no data is found, the HTTP 204 status code should be used
Nested Collections
Nested collections should also be designed around. For example, a user might have multiple friends:
curl \
-X GET \
-H "Content-Type: application/json" \
https://example.com/users/<user-id>/friends
Big Collections and Pagination
Pagination should be used on all results by default. Pagination parameters should be URL query parameters. paginated responses should have a "@nextlink" key that indicates the next link for the user or client to follow. The URL query parameters "skip" and "limit" should be used:
{
"value": [
. . .
]
"@nextlink":"https://example.com/collection?$orderBy=name asc&skip=10&limit=10"
}