c++/oat++ Web Service with Swagger-UI and auto-documented endpoints

This is a 5 minutes tutorial how-to use oatpp framework to build your own C++ performant web-service and integrate it with Swagger-UI.


It is supposed in this tutorial that you use Mac or Linux and have git and build-essentials installed. No additional build tools or libraries are required.


You can read more about oat++(oatpp) here https://oatpp.io/


Build and Run Starter Project

$ git clone --recurse-submodules --depth=1 https://github.com/oatpp/oatpp-starter my-service
$ cd my-service/

Now we are able to build and run our service. Oat++ is a zero-dependency framework, so NO additional installations are required.


In order to build our service we run build script from the project’s repository.

$ ./build_app.sh

This script is added for simplicity. It basically just calls g++. It is not recommended to stick to this script in production project.


Let’s run compiled executable $ ./run_app, go to localhost:8000 and make sure we can see server’s response

{"statusCode": 200, "message": "Hello World!"}

Stop the server and let’s look at what we have in the “src” folder…


Project Structure

- src/
|
|- controller/
| |
| |- MyController.hpp // Endpoints are declared here
|
|- dto/
| |
| |- MyDto.hpp // Dto used in "Hello World!" response
|
|- App.cpp // main is here
|- AppComponent.hpp // Application components configuration
|- Logger.hpp // Application logger
  • MyController class — contains declared endpoints and their info together with additional Swagger annotations.
  • MyDto class — describes the Data-Transfer-Object used in the “Hello World” response mentioned above. In oatpp DTOs are used in ObjectMapping and Serialization/Deserialization.
  • App.cpp file — this is an application’s entry point. Here Application Components are loaded, Controller’s endpoints are added to Router, and server starts.
  • AppComponent class — basically it is a collection of components which will be loaded on application start. Here we configure things like which ConnectionProvider to use, port to listen to, which ObjectMapper to use.
  • Logger class — is the place to deal with logs coming from OATPP_LOGV OATPP_LOGD OATPP_LOGE In our case we just write them to std::cout.

Integrate Swagger-UI

In order to integrate Swagger-UI in the project we have to:

  • Add git submodule oatpp-swagger to project
  • Add paths to oatpp-swagger to build_app.sh script
  • Add corresponding code to AppComponent.hpp and App.cpp

Add oatpp-swagger to project

$ cd <project-root>/lib/
$ git submodule add https://github.com/oatpp/oatpp-swagger

build_app.sh

$ g++ -std=gnu++11 \
-pthread \
`find "./lib/oatpp/" -type f -name *.cpp` \
`find "./lib/oatpp-swagger/" -type f -name *.cpp` \
...
...

Again this script is added for simplicity and it is not recommended to stick to it in production project.


AppComponent.hpp

Here we add oatpp::swagger::DocumentInfo and oatpp::swagger::Resources components which gives general information about our API document and specifies a path to swagger-ui resources:

#include "oatpp-swagger/Model.hpp"
#include "oatpp-swagger/Resources.hpp"
...
...
class AppComponent {
...
...
/**
* General API docs info
*/
OATPP_CREATE_COMPONENT(
std::shared_ptr<oatpp::swagger::DocumentInfo>,
swaggerDocumentInfo
)([] {

oatpp::swagger::DocumentInfo::Builder builder;
  builder
.setTitle("My Demo Service with Swagger-UI")
.setDescription("C++/oat++ Web Service with Swagger-UI")
.setVersion("1.0")
.setContactName("Mr. Developer")
.setContactUrl("https://oatpp.io/")
.setLicenseName("Apache License, Version 2.0")
.setLicenseUrl("http://www.apache.org/licenses/LICENSE-2.0")
.addServer("http://localhost:8000", "server on localhost");
   return builder.build();
}());
/**
* Swagger-Ui Resources
*/
OATPP_CREATE_COMPONENT(
std::shared_ptr<oatpp::swagger::Resources>,
swaggerResources
)([] {
  return oatpp::swagger::Resources::loadResources(
"<project-root>/lib/oatpp-swagger/res"
);
}());
};

App.cpp

Here we add oatpp::swagger::Controller to Router with the list of endpoints we want to document

#include "oatpp-swagger/Controller.hpp"
...
...
void run() {
  ...
  auto docEndpoints =
oatpp::swagger::Controller::Endpoints::createShared();
  docEndpoints->pushBackAll(myController->getEndpoints());
  auto swaggerController =
oatpp::swagger::Controller::createShared(docEndpoints);
  swaggerController->addEndpointsToRouter(router);
  ...
}

Now if everything is ok, and <project-root>/lib/oatpp-swagger/res" path is set correctly in the AppComponent, we should be able to build and run our project and see Swagger-UI at http://localhost:8000/swagger/ui in browser

Swagger-UI

Additional Info for Endpoint

Our endpoint is already present in the document with proper method and path. Oat++ automatically documents most of the endpoints’ info, such as endpoint name, method, path, parameters names and parameters types. However, there are things which should be specified explicitly.

Annotate endpoint with additional information

In the file MyController.hpp we add ENDPOINT_INFO above the root ENDPOINT with summary and response information:

ENDPOINT_INFO(root) {
info->summary = "Root endpoint with 'Hello World!!!' message";
info->addResponse<MyDto::ObjectWrapper>(
Status::CODE_200,
"application/json"
);
}
ENDPOINT("GET", "/", root) {
auto dto = MyDto::createShared();
dto->statusCode = 200;
dto->message = "Hello World!";
return createDtoResponse(Status::CODE_200, dto);
}

Build, Run and go to http://localhost:8000/swagger/ui in browser. Refresh.

Endpoint summary added. MyDto schema added to Models.

Notice, summary is added to the endpoint and MyDto schema automatically documented in the Models.

Expand endpoint info and check that response is documented correctly.

Correct response information

Basically that’s it. Now we have swagger-ui integrated in our project and we can easily add and document endpoints!


Add endpoint

Let’s add one more “echo” endpoint and see how it is documented in swagger.

In the file MyController.hpp:

ENDPOINT_INFO(echo) {
info->summary = "Echo endpoint with custom message";
info->addResponse<MyDto::ObjectWrapper>(
Status::CODE_200,
"application/json"
);
}
ENDPOINT("POST", "/echo/status/{status}", echo,
PATH(Int32, status), BODY_STRING(String, message)) {
auto dto = MyDto::createShared();
dto->statusCode = status;
dto->message = message;
return createDtoResponse(Status::CODE_200, dto);
}

Build and run… Refresh…

POST /echo/status/{status} endpoint added

Expand echo endpoint info…

status and request body parameters

execute request with parameters

server response

That’s it. You may experiment by adding more endpoints, playing with parameters, and DTO-fields to see how it is being documented in swagger-ui.