API documentation should be targeted towards the developer that will consume the API. A good documentation is one of the single most important qualities of an API as it can significantly reduce the implementation time for the API consumers. The API provider is responsible for keeping the documentation up to date.
To help with keeping documentation up to date consider using automatic generation tools that during build time can, for example, gather comments in predefined syntax and generate the Open API Specification (OAS), this means that the OAS lives bundled with the code and should be easier for developers to maintain.
Open Api Specification requirements for the API Catalogue
To be able to register a REST service to the API Catalogue the service MUST provide an OPENAPI 3 service description.
The following fields, not marked as (Optional), are required for services to be automatically imported to the API Catalogue:
description — short but proper description of the API.
version — to distinguish API versions following semantic versioning specification.
title — descriptive name of the API.
contact — information on who to contact about an issue with the service.
name — of the person or a department.
url (Optional) — containing information on the person or department to contact.
email — fully qualified email.
x-category — What kind of data does this service work with.
Possible values: open, official, personal, health, financial.
These values are displayed in the service view.
x-pricing — Cost of using this service.
Possible values: free, paid.
These values are displayed in the service view.
x-links — Links regarding the service.
responsibleParty — a fully qualified url to an online page containing information about the responsible party/owner of the service. This is linked to in the service view.
documentation — (Optional) a fully qualified url to the API documentation page. This is linked to in the service view.
bugReport (Optional) — a fully qualified url to an online page or form a consumer can report bugs about the service. This is linked to in the service view.
featureRequest (Optional) — a fully qualified url to an online page or form a consumer can ask for a new feature in api service. This is linked to in the service view.
An Open API Specification document MUST also describe the following:
Uses the Example Object to show example path and query parameters, request and response body for each operation.
Documents all expected HTTP statuses in the Response Object, both success and errors.
Describes all the content types with a Media Type Object for operations, both requests and responses.
Orders the Parameter Objects to list all required parameters before optional.
Setup example for NSwag in .NET core can be found here.
Describe error handling
Provide information on which HTTP status codes a client consuming your service can expect the API to return, and provide information on application defined errors and how the errors are presented to clients. See Errors for further details.
Provide feedback mechanism
Provide users with a way to comment on your documentation. This will help with finding concepts that need further explanation and keeping the documentation up to date.
The field x-links in the OpenAPI schema provides a way to include paths where consumers can provide feedback on the API.
Example
openapi:3.0.3servers: - url:https://development.my-service.island.isdescription:Development server - url:https://staging.my-service.island.isdescription:Staging server - url:https://production.my-service.island.isdescription:Production serverinfo:description:|- Provides access to an example service that retrieves individualsversion:0.0.1title:Example servicetermsOfService:''contact:name:Digital Icelandurl:https://stafraent.island.is/email:stafraentisland@fjr.islicense:name:MITurl:'https://opensource.org/licenses/MIT'x-pricing: - freex-category: - personal - officialx-links:documentation:'https://docs.my-service.island.is'responsibleParty:'https://my-service.island.is/responsible'bugReport:'https://github.com/island-is/island.is/issues/new'featureRequest:'https://github.com/island-is/island.is/issues/new'paths:/individuals:get:description:| Returns all individuals registeredoperationId:getIndividualsparameters: - name:dateOfBirthin:querydescription:Find all individuals born after set daterequired:falseschema:type:stringformat:dateexamples:dateOfBirthExample:summary:Example of date of birth query parametervalue:'1930-12-10'responses:'200':description:| Returns an array of individuals, either it returns all individuals or individuals born after a specific datecontent:application/json:schema:type:objectproperties:individuals:type:arrayitems:$ref:'#/components/schemas/Individual'examples:individual:$ref:'#/components/examples/IndividualsResponse''400':$ref:'#/components/responses/BadRequest''500':$ref:'#/components/responses/InternalServerError'default:description:Unexpected errorcontent:application/json:schema:type:objectproperties:error:$ref:'#/components/schemas/Error'security: - auth: - '@myorg.is/individuals:read'/individuals/{id}:get:description:| Returns individual based on a single IDoperationId:getIndividualparameters: - name:idin:pathdescription:UUID of an individualrequired:trueschema:type:stringformat:UUIDexamples:uuidExample:summary:Example of UUID parametervalue:'0762e29f-edf9-4fb3-b324-4503e92a5033'responses:'200':description:| Returns an individual with a specific idcontent:application/json:schema:type:objectproperties:individuals:type:arrayitems:$ref:'#/components/schemas/Individual'examples:individual:$ref:'#/components/examples/IndividualResponse''400':$ref:'#/components/responses/BadRequest''404':$ref:'#/components/responses/NotFound''500':$ref:'#/components/responses/BadRequest'security: - auth: - '@myorg.is/individuals:read'components:responses:BadRequest:description:Bad requestcontent:application/json:schema:type:objectproperties:error:$ref:'#/components/schemas/Error'Unauthorized:description:Unauthorizedcontent:application/json:schema:type:objectproperties:error:$ref:'#/components/schemas/Error'NotFound:description:The specified resource was not foundcontent:application/json:schema:type:objectproperties:error:$ref:'#/components/schemas/Error'InternalServerError:description:The specified resource was not foundcontent:application/json:schema:type:objectproperties:error:$ref:'#/components/schemas/Error'schemas:Individual:type:objectproperties:id:type:stringdescription:Unique UUIDformat:UUIDnationalId:type:stringminLength:12maxLength:12pattern:'^\d{12}$'description:National security numberfirstName:type:stringminLength:1maxLength:250description:First name and middle namelastName:type:stringminLength:1maxLength:250description:Last namedateOfBirth:type:stringformat:date-timedescription:UTC date of birthexample:id:'BA84DAF1-DE55-40A8-BF35-8A76C7F936F6'nationalId:'160108117573'firstName:'Quyn G.'lastName:'Rice'dateOfBirth:'2019-03-29T18:00:58.000Z'address:'377-8970 Vitae Rd.'Error:type:objectrequired: - code - messageproperties:code:type:integermessage:type:stringerrors:type:arrayitems:$ref:'#/components/schemas/ErrorDetail'example:code:400message:'Bad request'errors: - code:87message:'Parameter is incorrectly formatted'help:'https://www.moa.is/awesome/documetation/devices'trackingId:'5d17a8ada52a2327f02c6a1a'param:'deviceId' - code:85message:Parameter missinghelp:'https://www.moa.is/awesome/documetation/devices'ErrorDetail:type:objectrequired: - messageproperties:code:type:integermessage:type:stringhelp:type:stringtrackingId:type:stringparam:type:stringexamples:IndividualResponse:summary:Example of a response of a single individualvalue:id:'0762e29f-edf9-4fb3-b324-4503e92a5033'nationalId:'1234567890'firstName:'Doctor'lastName:'Strange'dateOfBirth:'1930-12-10T13:37:00.000Z'address:'535 West End Avenue at West 86th Street'IndividualsResponse:summary:Example of a response of array of individualsvalue: - id:'0762e29f-edf9-4fb3-b324-4503e92a5033'nationalId:'1234567890'firstName:'Doctor'lastName:'Strange'dateOfBirth:'1930-12-10T13:37:00.000Z'address:'535 West End Avenue at West 86th Street' - id:'68c78874-ff03-4ea6-b68d-ceba07286450'nationalId:'0987654321'firstName:'Wanda'lastName:'Maximoff'dateOfBirth:'1989-02-15T13:37:00.000Z'address:'Sokovia'securitySchemes:auth:type:oauth2flows:authorizationCode:authorizationUrl:https://identityprovider.is/connect/authorizetokenUrl:https://identityprovider.is/connect/tokenscopes:'@myorg.is/individuals:read':Read access to the individual api.'@myorg.is/individuals:write':Write access to the individual api.
Setup example
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddRouting(options => options.LowercaseUrls = true);
services.AddOpenApiDocument(config =>
{
//Registers all the required information for the API catalogue
config.PostProcess = document =>
{
//Basic information for the service, what it does and who is responsible for it
document.Info.Version = "v1";
document.Info.Title = "National Registry";
document.Info.Description = "Provides access to an example service that retrieves individuals.";
document.Info.Contact = new NSwag.OpenApiContact { Name = "Digital Iceland", Email = "stafraentisland@fjr.is", Url = "https://stafraent.island.is/" };
//The extension fields specifically used for API catalogue to help filter services
document.Info.ExtensionData = new Dictionary<string, object>();
document.Info.ExtensionData.Add(new KeyValuePair<string, object>("x-category", new string[] { "personal", "official" }));
document.Info.ExtensionData.Add(new KeyValuePair<string, object>("x-pricing", new string[] { "free", "paid" }));
document.Info.ExtensionData.Add(new KeyValuePair<string, object>("x-links", new Dictionary<string, string>()
{
{ "documentation", "https://docs.my-service.island.is" },
{ "responsibleParty", "https://www.skra.is/um-okkur" },
{ "bugReport", "https://github.com/island-is/island.is/issues/new" },
{ "featureRequest", "https://github.com/island-is/island.is/issues/new" },
}));
};
});
}