Skip to content

Repository files navigation

Selency OpenAPI

The OpenApi component provides a user-friendly, object-oriented API to build OpenAPI specifications using PHP.

Sponsor

Selency is a team of 80 people, each more committed than the last to promoting second-hand goods and doing something positive for our planet. Open-Source is perfectly aligned with our values, leading us to contribute back to the Symfony ecosystem through a series of components.

We're always looking for talented people, join us!

Usage

composer require selency/openapi

Selency OpenApi implements the OpenApi standard in its version 3.1. It allows to build documentations using PHP objects, giving more flexibility and reusability to OpenApi definitions. All features of OpenApi 3.1 are supported.

Writing documentation

The OpenApi component provides object-oriented PHP tools to build the documentation definition:

// openapi/Documentation.phpuseSelency\OpenApi\Documentation\AbstractDocumentation;
class Documentation extends AbstractDocumentation
{
publicfunctiongetIdentifier(): string
{
return'myapi';
}
publicfunctiongetVersion(): string
{
return'1.3.4';
}
publicfunctionconfigure(DocumentationConfigurator$doc): void
{
$doc->info($this->openApi->info()
->title('Monolith API')
->description(file_get_contents(__DIR__.'/Resources/info_description.md'))
->contact(name: 'API support', url: 'https://symfony.com', email: 'contact@symfony.com')
->specificationExtension('x-logo', [
'url' => 'https://symfony.com/logos/symfony_black_02.png',
'altText' => 'Symfony logo',
])
->license('MIT')
);
$doc->externalDocs(url: 'https://github.com/symfony/openapi', description: 'OpenApi component');
$doc->server($this->openApi->server('https://api.symfony.local')->description('Local'))
->server($this->openApi->server('https://api.symfony-staging.com')->description('Staging'))
->server($this->openApi->server('https://api.symfony.com')->description('Prod'));
$doc->securityRequirement(self::REF_SECURITY_USER_JWT);
$doc->path('/health', $this->openApi->pathItem()
->get($this->openApi->operation()
->tag('Health')
->operationId('app.health.check')
->summary('Health check')
->description('Check the API is up and available.')
->securityRequirement(null)
->responses($this->openApi->responses()
->response('200', $this->openApi->response()
->description('When the API is up and available.')
->content('application/json', $this->openApi->schema()
->property('name', $this->openApi->schema()->type('string')->description('Name for this API')->example('Selency API'))
->property('env', $this->openApi->schema()->type('string')->description('Current environment of this instance of the API')->example('prod'))
)
)
->response('500', $this->openApi->response()->description('When the API is unavailable due to a backend problem.'))
)
)
);
// ...
}
}
// Build a read-only model representing the documentation$compiler = newDocumentationCompiler();
$openApiDefinition = $compiler->compile($doc);
// Compile it as YAML or JSON for usage in other tools$openApiYaml = (newDumper\YamlDumper())->dump($openApiDefinition);
$openApiJson = (newDumper\JsonDumper())->dump($openApiDefinition);

Splitting documentations in multiple files

The component provides a concept of Partial documentations to allow splitting the documentation in multiple files for readability:

// HealthDocumentation.phpuseSelency\OpenApi\Documentation\PartialDocumentationInterface;
#[AutoconfigureTag('app.partial_documentation')]
class HealthDocumentation implements PartialDocumentationInterface
{
publicfunction__construct(privateOpenApiBuilderInterface$openApi)
{
}
publicfunctionconfigure(DocumentationConfigurator$doc): void
{
$doc->path('/health', $this->openApi->pathItem()
->get($this->openApi->operation()
->tag('Health')
->operationId('app.health.check')
->summary('Health check')
->description('Check the API is up and available. Mostly used by the infrastructure to check for readiness.')
->securityRequirement(null)
->responses($this->openApi->responses()
->response('200', $this->openApi->response()
->description('When the API is up and available.')
->content('application/json', $this->openApi->schema()
->property('name', $this->openApi->schema()->type('string')->description('Name for this API')->example('Selency API'))
->property('env', $this->openApi->schema()->type('string')->description('Current environment of this instance of the API')->example('prod'))
)
)
->response('500', $this->openApi->response()->description('When the API is unavailable due to a backend problem.'))
)
)
);
}
}
// Documentation.phpclass Documentation extends AbstractDocumentation
{
privateiterable$partialsDocs;
publicfunction__construct(
privateBuilder\OpenApiBuilder$openApi,
#[TaggedIterator(tag: 'app.partial_documentation')] iterable$partialsDocs,
) {
$this->partialsDocs = $partialsDocs;
}
publicfunctiongetIdentifier(): string
{
return'myapi';
}
publicfunctiongetVersion(): string
{
return'1.3.4';
}
publicfunctionconfigure(DocumentationConfigurator$doc): void
{
$doc->info($this->openApi->info()
->title('Monolith API')
->description(file_get_contents(__DIR__.'/Resources/info_description.md'))
->contact(name: 'API support', url: 'https://symfony.com', email: 'contact@symfony.com')
->specificationExtension('x-logo', [
'url' => 'https://symfony.com/logos/symfony_black_02.png',
'altText' => 'Symfony logo',
])
->license('MIT')
);
// ...// Apply partial documentationsforeach ($this->partialsDocsas$partialsDoc) {
$partialsDoc->configure($doc);
}
}
}

Store documentation close to your application code

The OpenApi component provides two interfaces to help maintaining documentation by storing it close to your code:

  • SelfDescribingSchemaInterface can be implemented by classes describing a schema (request, response, payloads, ...) ;
  • SelfDescribingQueryParametersInterface can be implemented by classes describing a list of query parameters ;

These interfaces are especially useful when using objects to handle inputs and outputs of your API:

class AuthRegisterPayload implements SelfDescribingSchemaInterface
{
#[Assert\Email(mode: Email::VALIDATION_MODE_STRICT)]
#[Assert\NotBlank]
public$email;
#[Assert\Type(type: 'string')]
#[Assert\NotBlank]
public$firstName;
#[Assert\Type(type: 'string')]
#[Assert\NotBlank]
public$lastName;
#[Assert\Type(type: 'string')]
#[Assert\NotBlank]
public$password;
publicstaticfunctiondescribeSchema(SchemaConfigurator$schema, OpenApiBuilderInterface$openApi): void
{
$schema
->title('AuthRegister')
->required(['email', 'firstName', 'lastName', 'password'])
->property('email', $openApi->schema()
->type('string')
->description('User\'s email')
->example('john.doe@domain.com')
)
->property('firstName', $openApi->schema()
->type('string')
->description('User\'s first name')
->example('John')
)
->property('lastName', $openApi->schema()
->type('string')
->description('User\'s last name')
->example('Doe')
)
->property('password', $openApi->schema()
->type('string')
->description('User\'s plaintext password')
)
;
}
}

You can then load these self-describing schemas/query params classes by providing the dedicated loader during compilation:

// Build a read-only model representing the documentation$compiler = newDocumentationCompiler([
newSelency\OpenApi\Loader\SelfDescribingSchemaLoader([
AuthRegisterPayload::class,
])
]);
$openApiDefinition = $compiler->compile($doc);

Note: in a Symfony application, SelfDescribingSchemaInterface and SelfDescribingQueryParametersInterface class are automatically added to the compiler, you don't need to do anything.

And use them in your definitions:

class Documentation extends AbstractDocumentation
{
// ...publicfunctionconfigure(DocumentationConfigurator$doc): void
{
// ...$doc->path('/auth/register', $this->openApi->pathItem()
->post($this->openApi->operation()
->tag('Auth')
->operationId('app.auth.register')
->summary('Auth registration')
->description('Register as a user.')
->securityRequirement(null)
->requestBody($this->openApi->requestBody()
->content('application/json', AuthRegisterPayload::class)
)
->responses($this->openApi->responses()
->response('200', $this->openApi->response()
->content('application/json', AuthRegisterOutput::class)
)
)
)
);
// ...
}
}

About

Provides a user-friendly, object-oriented API to build OpenAPI specifications using PHP

Resources

Stars

27 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages