OpenAPI Specification Bridge
This package converts Flow PHP schemas to OpenAPI 3.0 specifications, and back. You can generate OpenAPI documentation from Flow schemas, and Flow schemas from OpenAPI documentation. This helps API-first development and documentation workflows.
Installation
For the installation instructions, read the installation page.
Usage
With this bridge, you can convert Flow PHP schemas to the OpenAPI specification format, and back.
Converting Flow Schema to OpenAPI
<?php
use function Flow\ETL\DSL\{schema, int_schema, str_schema, bool_schema};
use function Flow\Bridge\OpenAPI\Specification\DSL\schema_to_openapi_specification;
use Flow\Bridge\OpenAPI\Specification\OpenAPIMetadata;
$userSchema = schema(
int_schema('id', false,
OpenAPIMetadata::description('Unique user identifier')
->merge(OpenAPIMetadata::example(123))
),
str_schema('name', true,
OpenAPIMetadata::description('User full name')
->merge(OpenAPIMetadata::example('John Doe'))
),
bool_schema('active', false,
OpenAPIMetadata::description('Account status')
)
);
$openApiSpec = schema_to_openapi_specification($userSchema);
// Output:
// [
// 'type' => 'object',
// 'properties' => [
// 'id' => [
// 'type' => 'integer',
// 'nullable' => false,
// 'description' => 'Unique user identifier',
// 'example' => 123
// ],
// 'name' => [
// 'type' => 'string',
// 'nullable' => true,
// 'description' => 'User full name',
// 'example' => 'John Doe'
// ],
// 'active' => [
// 'type' => 'boolean',
// 'nullable' => false,
// 'description' => 'Account status'
// ]
// ]
// ]
Converting OpenAPI to Flow Schema
<?php
use function Flow\Bridge\OpenAPI\Specification\DSL\schema_from_openapi_specification;
$openApiSpec = [
'type' => 'object',
'properties' => [
'id' => ['type' => 'integer', 'nullable' => false],
'email' => [
'type' => 'string',
'format' => 'email',
'nullable' => false,
'description' => 'User email address'
],
'profile' => [
'type' => 'object',
'properties' => [
'name' => ['type' => 'string', 'nullable' => false],
'bio' => ['type' => 'string', 'nullable' => true]
],
'nullable' => false
],
'tags' => [
'type' => 'array',
'items' => ['type' => 'string'],
'nullable' => true
]
]
];
$flowSchema = schema_from_openapi_specification($openApiSpec);
// Now you can use $flowSchema in Flow ETL pipelines
OpenAPI Metadata
The OpenAPI Specification Bridge handles special metadata through the OpenAPIMetadata enum. With it, you can add OpenAPI-specific properties to your Flow schema definitions. The schema conversion uses this metadata to generate detailed OpenAPI specifications.
Available OpenAPI Metadata
The OpenAPIMetadata enum has these metadata types:
DESCRIPTION: human-readable description of the propertyFORMAT: openAPI format specification (for example 'email', 'date-time', 'uuid')EXAMPLE: example value for the propertyEXAMPLES: multiple named examplesDEPRECATED: mark property as deprecatedTITLE: short title for the propertyDEFAULT: default value for the propertyREAD_ONLY: mark property as read-onlyWRITE_ONLY: mark property as write-onlyNULLABLE: override schema nullability
Using OpenAPI Metadata
<?php
use function Flow\ETL\DSL\{schema, int_schema, str_schema};
use Flow\Bridge\OpenAPI\Specification\{OpenAPIConverter, OpenAPIMetadata};
$userSchema = schema(
int_schema('id', false,
OpenAPIMetadata::description('Unique user identifier')
->merge(OpenAPIMetadata::format('int64'))
->merge(OpenAPIMetadata::example(12345))
->merge(OpenAPIMetadata::readOnly())
),
str_schema('email', false,
OpenAPIMetadata::description('User email address')
->merge(OpenAPIMetadata::format('email'))
->merge(OpenAPIMetadata::example('user@example.com'))
->merge(OpenAPIMetadata::title('Email Address'))
),
str_schema('password', false,
OpenAPIMetadata::description('User password')
->merge(OpenAPIMetadata::format('password'))
->merge(OpenAPIMetadata::writeOnly())
),
str_schema('status', false,
OpenAPIMetadata::description('Account status')
->merge(OpenAPIMetadata::default('active'))
->merge(OpenAPIMetadata::examples([
'active' => 'active',
'inactive' => 'inactive',
'suspended' => 'suspended'
]))
)
);
$converter = new OpenAPIConverter();
$openApiSpec = $converter->toOpenAPI($userSchema);
// Results in:
// [
// 'type' => 'object',
// 'properties' => [
// 'id' => [
// 'type' => 'integer',
// 'nullable' => false,
// 'description' => 'Unique user identifier',
// 'format' => 'int64',
// 'example' => 12345,
// 'readOnly' => true
// ],
// 'email' => [
// 'type' => 'string',
// 'nullable' => false,
// 'description' => 'User email address',
// 'format' => 'email',
// 'example' => 'user@example.com',
// 'title' => 'Email Address'
// ],
// 'password' => [
// 'type' => 'string',
// 'nullable' => false,
// 'description' => 'User password',
// 'format' => 'password',
// 'writeOnly' => true
// ],
// 'status' => [
// 'type' => 'string',
// 'nullable' => false,
// 'description' => 'Account status',
// 'default' => 'active',
// 'examples' => [
// 'active' => 'active',
// 'inactive' => 'inactive',
// 'suspended' => 'suspended'
// ]
// ]
// ]
// ]
Metadata Priority System
The OpenAPI bridge uses a priority system for metadata:
- OpenAPI-specific metadata (from
OpenAPIMetadata) has the highest priority. - Without OpenAPI metadata, the bridge uses the legacy metadata keys.
<?php
use Flow\ETL\Values\Schema\Metadata;
use Flow\Bridge\OpenAPI\Specification\OpenAPIMetadata;
// OpenAPI metadata takes priority over legacy metadata
$metadata = Metadata::with('description', 'Legacy description')
->merge(OpenAPIMetadata::description('OpenAPI description')) // This wins
->merge(Metadata::with('example', 'Legacy example'))
->merge(OpenAPIMetadata::example('OpenAPI example')); // This wins
$schema = schema(
str_schema('field', false, $metadata)
);
// Result will use OpenAPI values: "OpenAPI description" and "OpenAPI example"
Common OpenAPI Formats
These are common OpenAPI formats for OpenAPIMetadata::format():
String formats:
email: email addressuri: URI/URLuuid: UUID stringdate: date (YYYY-MM-DD)date-time: date and time (RFC 3339)password: password fieldbyte: base64 encoded stringbinary: binary data
Integer formats:
int32: 32-bit integerint64: 64-bit integer
Number formats:
float: floating point numberdouble: double precision number
Usage Example
The properties object of a structure keeps the declared field order of the structure type. An element
declared with structure_element(..., optional: true) emits nullable: true in its property.
<?php
use function Flow\ETL\DSL\{schema, int_schema, str_schema, list_schema, structure_schema};
use function Flow\Types\DSL\{type_list, type_string, type_structure};
use function Flow\Bridge\OpenAPI\Specification\DSL\schema_to_openapi_specification;
use Flow\Bridge\OpenAPI\Specification\OpenAPIMetadata;
// Define your data schema with rich OpenAPI metadata
$productSchema = schema(
int_schema('id', false,
OpenAPIMetadata::description('Product identifier')
->merge(OpenAPIMetadata::format('int64'))
->merge(OpenAPIMetadata::example(123))
->merge(OpenAPIMetadata::readOnly())
),
str_schema('name', false,
OpenAPIMetadata::description('Product name')
->merge(OpenAPIMetadata::title('Name'))
->merge(OpenAPIMetadata::example('iPhone 15'))
),
str_schema('description', true,
OpenAPIMetadata::description('Detailed product description')
->merge(OpenAPIMetadata::example('Latest smartphone with advanced features'))
),
structure_schema('category', type_structure([
'id' => type_string(),
'name' => type_string()
]), false,
OpenAPIMetadata::description('Product category information')
),
list_schema('tags', type_list(type_string()), true,
OpenAPIMetadata::description('Product tags for categorization')
->merge(OpenAPIMetadata::examples([
'electronics' => 'electronics',
'smartphone' => 'smartphone',
'apple' => 'apple'
]))
)
);
$apiSpec = schema_to_openapi_specification($productSchema);
// Use in your API documentation
$fullApiSpec = [
'openapi' => '3.0.0',
'info' => [
'title' => 'Product API',
'version' => '1.0.0'
],
'paths' => [
'/products' => [
'get' => [
'summary' => 'List products',
'responses' => [
'200' => [
'description' => 'A list of products',
'content' => [
'application/json' => [
'schema' => [
'type' => 'array',
'items' => $apiSpec
]
]
]
]
]
]
]
]
];
Found a typo or an outdated section? Edit this page on GitHub