{"id":30719847,"url":"https://github.com/dankinsoid/vaportoopenapi","last_synced_at":"2025-09-03T10:42:26.204Z","repository":{"id":63907532,"uuid":"446154776","full_name":"dankinsoid/VaporToOpenAPI","owner":"dankinsoid","description":"OpenAPI specification generator for Vapor based Swift projects.","archived":false,"fork":false,"pushed_at":"2025-04-04T19:15:24.000Z","size":2561,"stargazers_count":122,"open_issues_count":4,"forks_count":15,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-15T21:44:02.224Z","etag":null,"topics":["openapi","swagger","swift","vapor"],"latest_commit_sha":null,"homepage":"","language":"CSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dankinsoid.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"dankinsoid","open_collective":"voidilov-daniil","ko_fi":"dankinsoid","custom":["https://paypal.me/voidilovuae","https://www.buymeacoffee.com/dankinsoid"]}},"created_at":"2022-01-09T17:18:59.000Z","updated_at":"2025-07-08T18:10:20.000Z","dependencies_parsed_at":"2024-10-31T10:30:48.639Z","dependency_job_id":"352c114b-8254-4a41-8f00-4279f9feb02d","html_url":"https://github.com/dankinsoid/VaporToOpenAPI","commit_stats":{"total_commits":84,"total_committers":2,"mean_commits":42.0,"dds":0.0714285714285714,"last_synced_commit":"a52f5a6a16e255f270013a5baaf0093ae05b14da"},"previous_names":[],"tags_count":134,"template":false,"template_full_name":null,"purl":"pkg:github/dankinsoid/VaporToOpenAPI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2FVaporToOpenAPI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2FVaporToOpenAPI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2FVaporToOpenAPI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2FVaporToOpenAPI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dankinsoid","download_url":"https://codeload.github.com/dankinsoid/VaporToOpenAPI/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2FVaporToOpenAPI/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273431361,"owners_count":25104491,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["openapi","swagger","swift","vapor"],"created_at":"2025-09-03T10:42:25.313Z","updated_at":"2025-09-03T10:42:26.187Z","avatar_url":"https://github.com/dankinsoid.png","language":"CSS","funding_links":["https://github.com/sponsors/dankinsoid","https://opencollective.com/voidilov-daniil","https://ko-fi.com/dankinsoid","https://paypal.me/voidilovuae","https://www.buymeacoffee.com/dankinsoid"],"categories":[],"sub_categories":[],"readme":"# VaporToOpenAPI\n\n[![build](https://github.com/dankinsoid/VaporToOpenAPI/actions/workflows/test.yml/badge.svg)](https://github.com/dankinsoid/VaporToOpenAPI/actions/workflows/test.yml)\n\nVaporToOpenAPI is a Swift library that generates output compatible with [OpenAPI version 3.0.1](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md) from Vapor code. You can use the generated files with [Swagger UI](https://swagger.io/swagger-ui/) or [Stoplight](https://stoplight.io).\nThe library is based on [SwiftOpenAPI](https://github.com/dankinsoid/SwiftOpenAPI).\n\n## Usage\n\n### Create a route with an `OpenAPI` specification.\nAdd a route that returns an `OpenAPIObject` using the `app.routes.openAPI` method. This method generates a complete OpenAPI specification from your Vapor routes. It accepts parameters like your API's title, description, available paths, operations, and security requirements.\n```swift\n// generate OpenAPI documentation\nroutes.get(\"Swagger\", \"swagger.json\") { req in\n  req.application.routes.openAPI(\n    info: InfoObject(\n      title: \"Example API\",\n      description: \"Example API description\",\n      version: \"0.1.0\",\n    )\n  )\n}\n.excludeFromOpenAPI()\n```\n#### Configure a web page\n- Use `stoplightDocumentation(openAPI:)` helper method to generate a [Stoplight](https://stoplight.io) page. This method also can return an `OpenAPIObject`.\n- [Here is an example](##short-example) how to configure SwaggerUI.\n\n### Specify all details for routes\nUse the `.openAPI` modifier on routes to specify parameters like operation summaries, descriptions, request/response bodies, query parameters, and headers.\n  Here's an example of how you might use it to document a `POST` request:\n```swift\nroutes.post(\"users\") { req -\u003e EventLoopFuture\u003cUser\u003e in\n    let user = try req.content.decode(User.self)\n    return user.save(on: req.db).map { user }\n}\n.openAPI(\n    summary: \"Create User\",\n    description: \"Create a new user with the provided data\",\n    body: .type(User.self),\n    response: .type(User.self)\n)\n```\n\n\u003e [!IMPORTANT]\nAll enums in your models must implement `CaseIterable`.\n\n## Advanced Usage\nVaporToOpenAPI offers several advanced features for customizing your OpenAPI documentation:\n\n- `response`: This method is used to specify an additional response body for a Vapor route. It takes a variety of parameters, such as the status code, description, and response body type.\n- `groupedOpenAPI`: These methods are used to group Vapor routes together based on OpenAPI metadata, such as tags or security requirements.\\\nHere's an example of how you might use it to group routes with the same security requirements:\n```swift\nlet routes = app.routes.groupedOpenAPI(auth: .apiKey())\n```\n- `groupedOpenAPIResponse`: These methods are used to group Vapor routes together based on common response.\n\n- `groupedOpenAPI(server:)`: These methods are used to group Vapor routes together based on common servers.\n\n- `excludeFromOpenAPI`: This method is used to exclude a Vapor route or routes group from the generated OpenAPI specification.\n   \n- `openAPINoAuth`: This method is used to specify that an operation does not require any authentication.\n   \n- `openAPI(custom:)`: These methods are used to customize a specific aspect of the OpenAPI metadata for a Vapor route, such as a specific security scheme or callback.\n\n- `stoplightDocumentation(openAPI:)`: These methods are used to generate a [Stoplight documentation](https://stoplight.io/) from your Vapor routes.\n\n- `operationID` and `operationRef`: These properties are used to generate unique identifiers for OpenAPI operations and to create references to them in other parts of the specification.\n\n#### Customizing OpenAPI schemas and parameters\nYou can customize OpenAPI schemas and parameters result by implementing `OpenAPIDescriptable` and `OpenAPIType` protocols.\n1. `OpenAPIDescriptable` protocol allows you to provide a custom description for the type and its properties. `@OpenAPIDescriptable` macro implements this protocol with your comments.\n```swift\nimport SwiftOpenAPI\n\n@OpenAPIDescriptable\n/// Login request body.\nstruct LoginBody: Codable {\n    \n    /// Username string.\n    let username: String\n    /// Password string. Encoded.\n    let password: String\n}\n```\nManually:\n```swift\nstruct LoginBody: Codable, OpenAPIDescriptable {\n    \n    let username: String\n    let password: String\n    \n    static var openAPIDescription: OpenAPIDescriptionType? {\n        OpenAPIDescription\u003cCodingKeys\u003e(\"Login request body.\")\n            .add(for: .username, \"Username string.\")\n            .add(for: .password, \"Password string. Encoded.\")\n    }\n}\n```\n2. `OpenAPIType` protocol allows you to provide a custom schema for the type.\n```swift\nimport SwiftOpenAPI\n\nstruct Color: Codable, OpenAPIType {\n    \n    static var openAPISchema: SchemaObject {\n        .string(format: \"hex\", description: \"Color in hex format\")\n    }\n}\n```\n\n#### Link\nLinks are one of the new features of OpenAPI 3.0. Using links, you can describe how various values returned by one operation can be used as input for other operations.\nTo create a Link:\n1. create `LinkKey` type identifying some reusable parameter.\n2. specify the type in each route using the related parameter\n\n```swift\nenum PetID: LinkKey {\n}\n```\n```swift\nroute.get(\"pet\", use: getPet).openAPI(\n  links: [\n    Link(\"id\", in: .response): PetID.self\n  ]\n)\n\nroute.post(\"pet\", \":petID\", use: renamePer).openAPI(\n  links: [\n    Link(\"petID\", in: .path): PetID.self\n  ]\n)\n```\n\n## [Example project](Example/)\n## Short example\n### 1. SwaggerUI page \n\n1. Set up a [SwaggerUI page](https://github.com/swagger-api/swagger-ui) in your Vapor project downloading the `dist` folder and placing its content in the `Public/Swagger` directory.\n2. Describe all of your routes and register all controllers as described in [Vapor docs](https://docs.vapor.codes/basics/routing). Add OpenAPI details to each route using the `route.openAPI` method.\n3. Add a route to return a [SwaggerUI index.html](https://github.com/swagger-api/swagger-ui/blob/master/dist/index.html). Or configure your middlewares to use 'index.html' as default page.\n4. Add a route to return an `OpenAPIObject` instance via the `app.routes.openAPI` method. Make sure the path of this route matches the `swagger.json` URL in your SwaggerUI page method.\n\n5. Change `url` in [`swagger-initializer.js`](https://github.com/swagger-api/swagger-ui/blob/master/dist/swagger-initializer.js)\n```js\nwindow.onload = function() {\n  //\u003ceditor-fold desc=\"Changeable Configuration Block\"\u003e\n\n  // the following lines will be replaced by docker/configurator, when it runs in a docker-container\n  var jsonURL = document.location.origin + \"/Swagger/swagger.json\";\n  window.ui = SwaggerUIBundle({\n    url: jsonURL,\n    dom_id: '#swagger-ui',\n    deepLinking: true,\n    presets: [\n      SwaggerUIBundle.presets.apis,\n      SwaggerUIStandalonePreset\n    ],\n    plugins: [\n      SwaggerUIBundle.plugins.DownloadUrl\n    ],\n    layout: \"StandaloneLayout\"\n  });\n\n  //\u003c/editor-fold\u003e\n};\n```\n### 2. Routes\n```swift\nroutes = routes\n  .groupedOpenAPI(auth: .basic)\n  .groupedOpenAPIResponse(\n    statusCode: 400,\n    body: .type(of: ErrorResponse())\n  )\n\nroutes.post(\"login\") { req in\n  try await loginService.makeLoginRequest(\n    query: req.query.decode(LoginQuery.self),\n    content: req.content.decode(LoginRequestBody.self)\n  )\n}\n.openAPI(\n  summary: \"Login\",\n  description: \"Login request\",\n  query: .type(LoginQuery.self),\n  headers: [\"X-SOME_VALUE\": .string],\n  body: .type(LoginRequestBody.self),\n  response: .type(LoginResponse.self),\n  auth: .apiKey()\n)\n```\n### 3. SwaggerUI page routing\n```swift\nFileMiddleware(publicDirectory: app.directory.publicDirectory, defaultFile: \"index.html\")\n```\n### 4. OpenAPIObject route\n```swift\n// generate OpenAPI documentation\nroutes.get(\"Swagger\", \"swagger.json\") { req in\n  req.application.routes.openAPI(\n    info: InfoObject(\n      title: \"Example API\",\n      description: \"Example API description\",\n      version: \"0.1.0\",\n    )\n  )\n}\n.excludeFromOpenAPI()\n```\n\n## Installation\n1. [Swift Package Manager](https://github.com/apple/swift-package-manager)\n\nCreate a `Package.swift` file.\n```swift\n// swift-tools-version:5.9\nimport PackageDescription\n\nlet package = Package(\n  name: \"SomeProject\",\n  dependencies: [\n    .package(url: \"https://github.com/dankinsoid/VaporToOpenAPI.git\", from: \"4.8.1\")\n  ],\n  targets: [\n    .target(name: \"SomeProject\", dependencies: [\"VaporToOpenAPI\"])\n  ]\n)\n```\n```ruby\n$ swift build\n```\n\n## Contributing\nContributions to VaporToOpenAPI are welcome! If you find a bug or have a feature request, please raise an issue or submit\n\n## Author\n\ndankinsoid, voidilov@gmail.com\n\n## License\n\nVaporToOpenAPI is available under the MIT license. See the LICENSE file for more info.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdankinsoid%2Fvaportoopenapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdankinsoid%2Fvaportoopenapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdankinsoid%2Fvaportoopenapi/lists"}