Integrate OpenAPI Into a Slim (PHP) Project

Patric
Patric
Sep 25 · 7 min read

In this tutorial, I want to give you a short introduction on how to integrate OpenAPI into a Slim Project or any other that is based on PHP. We will use the approach to first write the code and then add annotations to it.

If you are new to OpenAPI you might want to read my introduction to OpenAPI first here.

OpenAPI or Swagger? First, I want to clarify most times in this article we will be talking about OpenAPI, but a few times like the UI npm package we will be referring to Swagger. Till version 2 the specification was named Swagger, with major version 3 it was renamed to OpenAPI. Some tools developed by SmartBear are still named Swagger, you find more details here https://swagger.io/about/.

Here is a short overview of the workflow I will be providing in this tutorial. There are several other ways how to do that, one is to write the documentation first.

Image for post
Image for post

Here is a short overview of how the structure of our OpenAPI will look like.

In this tutorial I will give you a brief overview of the most used fields, if you want to go deeper or need to use more fields you find the documentation here https://swagger.io/specification/#openapi-object

Image for post
Image for post

We will use the Slim Skeleton project for integrating our Annotations, you find the project here https://github.com/slimphp/Slim-Skeleton

For the next command to work, you need to have composer globally installed if you do not visit https://getcomposer.org/doc/00-intro.md#globally and follow the instructions.

Please switch to your command line and install the project with the following command:

# switch into the appropriate folder 
cd ~/your-projects
# install project
composer create-project slim/slim-skeleton openapi-introduction

For adding our annotations we will be installing https://github.com/zircote/swagger-php with the following command:

# switch into the new project folder 
cd openapi-introduction
# install third-party package for using OpenAPI annotations
composer require zircote/swagger-php

If you want to see all available objects for annotating visit https://github.com/zircote/swagger-php/tree/master/src/Annotations

Open the newly installed project openapi-introduction in your preferred code editor. Open up the file src/Application/Actions/Action.php there we will be adding the general annotations (information) for our API.

/**
*
@OA\Server(url="http://localhost:8080")
*
@OA\Info(title="Slim OpenApi Introduction", version="0.1")
*/

abstract class Action
{
...

You see the first node of our diagram “openapi” is missing, the version number will be automatically added when generating the documentation with ./vendor/bin/openapi.

Server, we are using the PHP built-in server, you can change it to your local server for testing (you can also add the Server annotation several times, as this will be combined to an array like shown in the diagram).

As Info we provide a title and a version for our API, there are a few other options you can set, see https://swagger.io/specification/#info-object

Next, we will add the missing node of our diagram “paths”. First, we will check what routes the skeleton provides us, open app/routes.php in your editor.

There are three routes defined. We will add annotations to the two last routes.

// routes.php
...
$app->get('/', function (Request $request, Response $response) {
$response->getBody()->write('Hello world!');
return $response;
});

$app->group('/users', function (Group $group) {
$group->get('', ListUsersAction::class);
$group->get('/{id}', ViewUserAction::class);

});
...

Let’s open up the file src/Application/Actions/User/ViewUserAction.php in your editor and add our first path documentation.

...
/**
*
@OA\Get(
* tags={"user"},
* path="/users",
* operationId="getUsers",
*
@OA\Response(
* response="200",
* description="List all users",
*
@OA\JsonContent(
* type="array",
*
@OA\Items(ref="#/components/schemas/User")
* )
* )
* )
*/
protected function action(): Response
{
...

We defined that this operation is a GET method and added some details to it in the parentheses:

  • tags, we added this for grouping (if you will use the openapi-generator to generate your frontend this will be also the name for your service)
  • path, this is the relative path to your endpoint
  • operationId, this is relevant if you use the openapi-generator for generating a frontend, then this will be the name of the method in your service, this must be unique among all operations
  • Response, here we can add several response objects for defining our responses, in our case we just have one with a status 200 and a short description
  • JsonContent, is the content of our response it will be of type JSON and holds an array of user objects (bellow we go in more detail)

For more details visit the OpenAPI doc site https://swagger.io/specification/#operation-object

Next, we will define the schema we referenced in the last annotation #/components/schemas/User please open src/Domain/User/User.php

.../**
*
@OA\Schema(
* title="User",
* description="A simple user model."
* )
*/
class User implements JsonSerializable
{
/**
*
@OA\Property(type="integer", format="int64", readOnly=true, example=1)
*/
private $id;

/**
*
@OA\Property(type="string", example="johndoe")
*/
private $username;

/**
*
@OA\Property(type="string", example="John")
*/
private $firstName;

/**
*
@OA\Property(type="string", example="Doe")
*/
private $lastName;
...

Here we define a Schema object, for more details visit https://swagger.io/specification/#schemaObject

Lastly, we will define the documentation for the endpoint that will return a single user by id. Please, open up src/Application/Actions/User/ListUsersAction.php and add the following annotation

.../**
*
@OA\Get(
* tags={"user"},
* path="/users/{id}",
* operationId="getUser",
*
@OA\Parameter(
* name="id",
* in="path",
* required=true,
* description="User id",
*
@OA\Schema(
* type="integer"
* )
* ),
*
@OA\Response(
* response=200,
* description="A single user",
*
@OA\JsonContent(ref="#/components/schemas/User")
* )
* )
*/
protected function action(): Response
{
...

It is very similar to our first GET method definition. The path as changed, we added a path parameter, the operationId has changed to a unique value, we added a Parameter object, please mention that for a path parameter the required value must always be set to true and we added a schema object to define that the id will be of type integer. Our Response also changed slightly as the method only returns a single user object not an array of objects.

Now finally comes the part where we generate our OpenAPI documentation, please open up your command line and switch in our project folder:

# switch into the new project folder 
cd openapi-introduction
# generate the documentation
./vendor/bin/openapi — output ./openapi.json ./src/

We generated a json file, but you can also generate it as yaml if it more suites your needs.

Hopefully, you don’t get an error, if you get one please scroll up and check if you took all the mentioned steps.

Now, we open up our json file with a npm command to get a nice view and can test our endpoints directly from our browser. We use https://www.npmjs.com/package/open-swagger-ui to follow these steps you need to install node first, you can easily download and install it from here https://nodejs.org/en/

# install the npm package globally
npm i -g open-swagger-ui
# switch into the project folder
cd openapi-introduction
# start the API, with the PHP build-in server
composer start
# open the documentation in the browser
open-swagger-ui ./openapi.json --open

After the last command, the documentation UI automatically open up in your browser. Bellow, you see a screenshot:

Image for post
Image for post
  1. Now we can only select our development server, as we only added one server with our annotation before @OA\Server(url=”http://localhost:8080")
  2. Our endpoints are grouped like specified in our tags parameter in our Get objects
    tags={“user”}
  3. Here we see the Schemas, we only added on User Schema with
    @OA\Schema()

To test our API we can now execute a call to our API. Expand the second endpoint /users and click on the “Try it out” Button, like marked in the bellow screenshot with 1.

/users endpoint
Image for post
Image for post
Try it out
  1. Execute, this will trigger a curl command
  2. The executed curl command
  3. The called URL
  4. The response you got from the server
  5. Cancel the try out section

Here are some closing thoughts about what you should keep in mind with this workflow. Developers need to take care to update the documentation, when they change the code if they don’t the documentation shows a wrong state of the API. The OpenAPI annotations only document your API and don’t validate it. If you using the openapi-generator to generate the frontend client it might be better that you use the API documentation first approach.

Here you can find the source code to this tutorial https://github.com/pguso/openapi-php-introduction

Here is the following tutorial, that shows you how to generate an Angular-Typescript client from the generated openapi.json https://medium.com/@pguso/generate-openapi-angular-client-8c9288e8bbd4

I hope you liked this short tutorial and OpenAPI can help you and your team to have clear and nice documentation of the state of your API!

The Startup

Medium's largest active publication, followed by +721K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store