UPD: I wrote this for swagger v1.2. Stuff has changed since then. Now swagger supports single-file YAML specifications, so making them is easier now. What a time to be alive!
A few days ago was the first time I created a REST API documentation. This is the report on how I did it.
There are several generators that generate pretty interface for your documentation so you don't have to do it manually. I've been picking between two variants:
API Blueprint - it allows you to write API documentation in markdown and there is a service apiary that generates documentation from it for free. Also, several useful tools work with this format: they generate server mocks, test your APIs and do other cool stuff.
Swagger - wants you to write documentation in json-format. It has a cool dеpendency-free documentation interface that creates pages like this. Also, it has a lot of client code generators for different languages.
First, I wanted to use Blueprint because of markdown. Yes, swagger's json files look ugly. But then I decided it's important to store documentation inside the project. It allows you to have different documentations for different API versions (like dev and prod). And it increases the chance that your docs won't be forgotten.
It's cool to generate documentation by reading your application docblocks, but there's a lot of metadata that I don't know where to store. I wanted to create documentation manually at least once to know what to automate. But if you want to generate it from docblock, a library Swagger-PHP makes it for you.
First, we'll create interactive documentation. You need to download Swagger UI, move directory "dist" to your public web folder, and rename it to "docs". Congratulations, it's already working. It was not so hard. But for now, it shows documentation for a pet store. You need to edit the file "index.html" and change url to your json API docs url. There's a convention to store them in the "api-docs" directory.
Now about creating json docs. Here's the specification for them. And you don't want to create json manually. There's a human-readable format to store data structures like this. It's called YAML. It's much more convenient to write data in YAML than in JSON. I used symfony/Yaml package and created an action that reads YAML files and converts them to JSON on the fly.
One more YAML feature can help you a lot - aliases. When you're using a hash or an array for the first time, you can name it, and when you need to use it again, you can white its name and not copy-paste its content. Documentation has a lot of recurring blocks, so aliases are very handful in this case.
But there's a problem with aliases. Specification wants you to declare each API group in a separate file, and you can't use aliases declared in other files. And there are lot of data parts that are used in all files (like error response models).
I created a file _includes.yml
that contains all common aliases and ends with an empty line. All its contents are grouped in a hash called _includes
. I add this file text to the beginning of every YAML file before parsing YAML and remove group _includes
before converting to json. This allows us to use global aliases. YAML validators fire an error when see using of an alias that is not declared in the current file, so we have to write %alias
instead of *alias
in YAML files and convert it using preg_replace
before parsing YAML. I have no words starting with "%" char in my documentation, so it's ok.
Here are the Yii routing rules for my documentation:
'api-docs' => 'apiDocs/index',
'api-docs/<view>' => 'apiDocs/index',
And here's the controller I used:
use Symfony\Component\Yaml\Yaml;
/**
* This controller converts yaml docs files to json format for swagger-ui.
*/
class ApiDocsController extends ApiController
{
public function actionIndex($view = 'index')
{
$file = Yii::getPathOfAlias('application.docs.' . $view) . '.yml';
if (!file_exists($file))
throw new CHttpException(404, 'File not found');
$yaml =
file_get_contents(Yii::getPathOfAlias('application.docs._includes') . '.yml') .
file_get_contents($file);
// Replace placeholders
$yaml = strtr($yaml, [
'%apiv%' => '0.1.0',
'%host%' => Yii::app()->request->hostInfo,
]);
// Convert remote alias calls
$yaml = preg_replace('~%([a-z+])~i', '*$1', $yaml);
$data = Yaml::parse($yaml);
unset($data['_includes']);
echo json_encode($data);
}
}
That's how I created an API documentation.