3.22.0 • Published 1 day ago

adonis-autoswagger v3.22.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 day ago

Auto-Generate swagger docs for AdonisJS

💻️ Install

pnpm i adonis-autoswagger #using pnpm

⭐️ Features

  • Creates paths automatically based on routes.ts
  • Creates schemas automatically based on app/Models/*
  • Creates schemas automatically based on app/Interfaces/*
  • Rich configuration via comments
  • Works also in production mode
  • node ace docs:generate command

✌️Usage

Create a file /config/swagger.ts

// for AdonisJS v6
import path from "node:path";
import url from "node:url";
// ---

export default {
  // path: __dirname + "/../", for AdonisJS v5
  path: path.dirname(url.fileURLToPath(import.meta.url)) + "/../", // for AdonisJS v6
  title: "Foo", // use info instead
  version: "1.0.0", // use info instead
  description: "", // use info instead
  tagIndex: 2,
  info: {
    title: "title",
    version: "1.0.0",
    description: "",
  },
  snakeCase: true,

  debug: false, // set to true, to get some useful debug output
  ignore: ["/swagger", "/docs"],
  preferredPutPatch: "PUT", // if PUT/PATCH are provided for the same route, prefer PUT
  common: {
    parameters: {}, // OpenAPI conform parameters that are commonly used
    headers: {}, // OpenAPI conform headers that are commonly used
  },
  securitySchemes: {}, // optional
  authMiddlewares: ["auth", "auth:api"], // optional
  defaultSecurityScheme: "BearerAuth", // optional
  persistAuthorization: true, // persist authorization between reloads on the swagger page
  showFullPath: false, // the path displayed after endpoint summary
};

In your routes.ts

6️⃣ for AdonisJS v6

import AutoSwagger from "adonis-autoswagger";
import swagger from "#config/swagger";
// returns swagger in YAML
router.get("/swagger", async () => {
  return AutoSwagger.default.docs(router.toJSON(), swagger);
});

// Renders Swagger-UI and passes YAML-output of /swagger
router.get("/docs", async () => {
  return AutoSwagger.default.ui("/swagger", swagger);
  // return AutoSwagger.default.scalar("/swagger", swagger); to use Scalar instead
  // return AutoSwagger.default.rapidoc("/swagger", swagger); to use RapiDoc instead
});

5️⃣ for AdonisJS v5

import AutoSwagger from "adonis-autoswagger";
import swagger from "Config/swagger";
// returns swagger in YAML
Route.get("/swagger", async () => {
  return AutoSwagger.docs(Route.toJSON(), swagger);
});

// Renders Swagger-UI and passes YAML-output of /swagger
Route.get("/docs", async () => {
  return AutoSwagger.ui("/swagger", swagger);
});

👍️ Done!

Visit http://localhost:3333/docs to see AutoSwagger in action.

Functions

  • async docs(routes, conf): get the specification in YAML format
  • async json(routes, conf): get the specification in JSON format
  • ui(path, conf): get default swagger UI
  • rapidoc(path, style): get rapidoc UI
  • scalar(path): get scalar UI
  • jsonToYaml(json): can be used to convert json() back to yaml

💡 Compatibility

For controllers to get detected properly, please load them lazily.

✅ const TestController = () => import('#controllers/test_controller')
❌ import TestController from '#controllers/test_controller'

🧑‍💻 Advanced usage

Additional configuration

info See Swagger API General Info for details.

securitySchemes

Add/Overwrite security schemes Swagger Authentication for details.

// example to override ApiKeyAuth
securitySchemes: {
  ApiKeyAuth: {
    type: "apiKey"
    in: "header",
    name: "X-API-Key"
  }
}

defaultSecurityScheme

Override the default security scheme.

  • BearerAuth
  • BasicAuth
  • ApiKeyAuth
  • your own defined under securitySchemes

authMiddlewares

If a route uses a middleware named auth, auth:api, AutoSwagger will detect it as a Swagger security method. However, you can implement other middlewares that handle authentication.

Modify generated output

Route.get("/myswagger", async () => {
  const json = await AutoSwagger.json(Route.toJSON(), swagger);
  // modify json to your hearts content
  return AutoSwagger.jsonToYaml(json);
});

Route.get("/docs", async () => {
  return AutoSwagger.ui("/myswagger", swagger);
});

Custom Paths in adonisJS v6

AutoSwagger supports the paths set in package.json. Interfaces are expected to be in app/interfaces. However, you can override this, by modifying package.json as follows.

//...
"imports": {
  // ...
  "#interfaces/*": "./app/custom/path/interfaces/*.js"
  // ...
}
//...

📃 Configure

tagIndex

Tags endpoints automatically

  • If your routes are /api/v1/products/... then your tagIndex should be 3
  • If your routes are /v1/products/... then your tagIndex should be 2
  • If your routes are /products/... then your tagIndex should be 1

ignore

Ignores specified paths. When used with a wildcard (*), AutoSwagger will ignore everything matching before/after the wildcard. /test/_will ignore everything starting with/test/, whereas \*/testwill ignore everything ending with/test.

common

Sometimes you want to use specific parameters or headers on multiple responses.

Example: Some resources use the same filter parameters or return the same headers.

Here's where you can set these and use them with @paramUse() and @responseHeader() @use(). See practical example for further details.


💫 Extend Controllers

Add additional documentation to your Controller-files.

@summary (only one) A summary of what the action does

@tag (only one) Set a custom tag for this action

@description (only one) A detailed description of what the action does.

@operationId (only one) An optional unique string used to identify an operation. If provided, these IDs must be unique among all operations described in your API..

@responseBody (multiple)

Format: <status> - <return> - <description>

<return> can be either a <Schema>, <Schema[]>/ or a custom JSON {}

@responseHeader (multiple)

Format: <status> - <name> - <description> - <meta>

@paramType (multiple)

Type can be one of Parameter Types (first letter in uppercase)

@requestBody (only one) A definition of the expected requestBody

Format: <body>

<body> can be either a <Schema>, <Schema[]>/, or a custom JSON {}

@requestFormDataBody (only one) A definition of the expected requestBody that will be sent with formData format.

Model

Format: <Model>

Custom format

Format: {"fieldname": {"type":"string", "format": "email"}} This format should be a valid openapi 3.x json.


🤘Examples

@responseBody examples

@responseBody <status> - Lorem ipsum Dolor sit amet

@responseBody <status> // returns standard <status> message

@responseBody <status> - <Model> // returns model specification

@responseBody <status> - <Model[]> // returns model-array specification

@responseBody <status> - <Model>.with(relations, property1, property2.relations, property3.subproperty.relations) // returns a model and a defined relation

@responseBody <status> - <Model[]>.with(relations).exclude(property1, property2, property3.subproperty) // returns model specification

@responseBody <status> - <Model[]>.append("some":"valid json") // append additional properties to a Model

@responseBody <status> - <Model[]>.paginated() // helper function to return adonisJS conform structure like {"data": [], "meta": {}}

@responseBody <status> - <Model>.only(property1, property2) // pick only specific properties



@responseBody <status> - {"foo": "bar", "baz": "<Model>"} //returns custom json object and also parses the model
@responseBody <status> - ["foo", "bar"] //returns custom json array

@requestBody examples

// basicaly same as @response, just without a status
@requestBody <Model> // Expects model specification
@requestBody <Model>.with(relations) // Expects model and its relations
@requestBody <Model[]>.append("some":"valid json") // append additional properties to a Model
@requestBody {"foo": "bar"} // Expects a specific JSON

@requestFormDataBody examples

// Providing a raw JSON
@requestFormDataBody {"name":{"type":"string"},"picture":{"type":"string","format":"binary"}} // Expects a valid OpenAPI 3.x JSON
// Providing a Model, and adding additional fields
@requestFormDataBody <Model> // Expects a valid OpenAPI 3.x JSON
@requestFormDataBody <Model>.exclude(property1).append("picture":{"type":"string","format":"binary"}) // Expects a valid OpenAPI 3.x JSON

Practical example

config/swagger.ts

export default {
  path: __dirname + "../",
  title: "YourProject",
  version: "1.0.0",
  tagIndex: 2,
  ignore: ["/swagger", "/docs", "/v1", "/", "/something/*", "*/something"],
  common: {
    parameters: {
      sortable: [
        {
          in: "query",
          name: "sortBy",
          schema: { type: "string", example: "foo" },
        },
        {
          in: "query",
          name: "sortType",
          schema: { type: "string", example: "ASC" },
        },
      ],
    },
    headers: {
      paginated: {
        "X-Total-Pages": {
          description: "Total amount of pages",
          schema: { type: "integer", example: 5 },
        },
        "X-Total": {
          description: "Total amount of results",
          schema: { type: "integer", example: 100 },
        },
        "X-Per-Page": {
          description: "Results per page",
          schema: { type: "integer", example: 20 },
        },
      },
    },
  },
};

app/Controllers/Http/SomeController.ts

export default class SomeController {
  /**
   * @index
   * @operationId getProducts
   * @description Returns array of producs and it's relations
   * @responseBody 200 - <Product[]>.with(relations)
   * @paramUse(sortable, filterable)
   * @responseHeader 200 - @use(paginated)
   * @responseHeader 200 - X-pages - A description of the header - @example(test)
   */
  public async index({ request, response }: HttpContextContract) {}

  /**
   * @show
   * @paramPath id - Describe the param
   * @description Returns a product with it's relation on user and user relations
   * @responseBody 200 - <Product>.with(user, user.relations)
   * @responseBody 404
   */
  public async show({ request, response }: HttpContextContract) {}

  /**
   * @update
   * @responseBody 200
   * @responseBody 404 - Product could not be found
   * @requestBody <Product>
   */
  public async update({ request, response }: HttpContextContract) {}

  /**
   * @custom
   * @summary Lorem ipsum dolor sit amet
   * @paramPath provider - The login provider to be used - @enum(google, facebook, apple)
   * @responseBody 200 - {"token": "xxxxxxx"}
   * @requestBody {"code": "xxxxxx"}
   */
  public async custom({ request, response }: HttpContextContract) {}
}

What does it do?

AutoSwagger tries to extracat as much information as possible to generate swagger-docs for you.

Paths

Automatically generates swagger path-descriptions, based on your application routes. It also detects endpoints, protected by the auth-middlware.

paths

Responses and RequestBody

Generates responses and requestBody based on your simple Controller-Annotation (see Examples)


Schemas

Models

Automatically generates swagger schema-descriptions based on your models

alt

Interfaces

Instead of using param: any you can now use custom interfaces param: UserDetails. The interfaces files need to be located at app/Interfaces/

Extend Models

Add additional documentation to your Models properties.

SoftDelete

Either use compose(BaseModel, SoftDeletes) or add a line @swagger-softdeletes to your Model.

Attention!

The below comments MUST be placed 1 line above the property.


@no-swagger Although, autoswagger detects serializeAs: null fields automatically, and does not show them. You can use @no-swagger for other fields.

@enum(foo, bar) If a field has defined values, you can add them into an enum. This is usesfull for something like a status field.

@format(string) Specify a format for that field, i.e. uuid, email, binary, etc...

@example(foo bar) Use this field to provide own example values for specific fields

Product.js

@hasMany(() => ProductView)
// @no-swagger
public views: HasMany<typeof ProductView>


@column()
// @enum(pending, active, deleted)
public status: string

@column()
// @example(johndoe@example.com)
public email: string

Production environment

!WARNING Make sure NODE_ENV=production in your production environment

To make it work in production environments, additional steps are required

  • Create a new command for docs:generate See official documentation

    • This should create a new file in commands/DocsGenerate.ts
  • Use the provided DocsGenerate.ts.examle/DocsGeneratev6.ts.example and put its contents into your newly created DocsGenerate.ts

  • Execute the following

node ace docs:generate
node ace build --production
cp swagger.yml build/
3.20.0

1 day ago

3.17.0

11 days ago

3.21.0

1 day ago

3.18.0

11 days ago

3.22.0

1 day ago

3.19.0

11 days ago

3.14.0

13 days ago

3.15.0

13 days ago

3.16.0

13 days ago

3.12.0

17 days ago

3.13.0

17 days ago

3.10.0

19 days ago

3.11.0

18 days ago

3.9.0

25 days ago

3.8.0

26 days ago

3.7.0

26 days ago

3.6.0

1 month ago

3.5.0

1 month ago

3.4.0

1 month ago

3.4.1

1 month ago

3.3.1

1 month ago

3.3.0

1 month ago

3.2.1

1 month ago

3.2.0

1 month ago

3.1.1

1 month ago

3.1.0

1 month ago

3.0.1

1 month ago

3.0.0

1 month ago

2.15.1

1 month ago

2.16.1

1 month ago

2.16.0

1 month ago

2.13.1

2 months ago

2.14.1

2 months ago

2.14.0

2 months ago

2.13.0

2 months ago

2.11.0

2 months ago

2.11.1

2 months ago

2.12.1

2 months ago

2.10.1

2 months ago

2.10.0

2 months ago

2.9.1

2 months ago

2.9.0

2 months ago

2.8.1

3 months ago

2.8.0

3 months ago

2.7.0

3 months ago

2.7.1

3 months ago

2.6.0

3 months ago

2.2.1

3 months ago

2.2.0

3 months ago

2.0.2

3 months ago

2.4.1

3 months ago

2.4.0

3 months ago

2.0.1

3 months ago

2.0.0

3 months ago

2.3.0

3 months ago

2.5.0

3 months ago

2.3.1

3 months ago

2.5.1

3 months ago

1.9.1

4 months ago

1.10.1

4 months ago

1.8.2

5 months ago

1.8.5

5 months ago

1.8.3

5 months ago

1.8.1

1 year ago

1.8.0

1 year ago

1.7.8

2 years ago

1.7.6

2 years ago

1.2.0

2 years ago

1.6.4

2 years ago

1.6.3

2 years ago

1.6.2

2 years ago

1.6.1

2 years ago

1.6.0

2 years ago

1.4.0

2 years ago

1.6.11

2 years ago

1.6.10

2 years ago

1.7.5

2 years ago

1.7.4

2 years ago

1.6.12

2 years ago

1.7.3

2 years ago

1.7.2

2 years ago

1.7.1

2 years ago

1.7.0

2 years ago

1.5.1

2 years ago

1.5.0

2 years ago

1.3.1

2 years ago

1.3.0

2 years ago

1.1.12

2 years ago

1.1.14

2 years ago

1.1.13

2 years ago

1.6.9

2 years ago

1.6.8

2 years ago

1.6.7

2 years ago

1.6.6

2 years ago

1.6.5

2 years ago

1.1.11

2 years ago

1.1.10

2 years ago

1.1.9

2 years ago

1.1.7

2 years ago

1.1.6

2 years ago

1.1.5

2 years ago

1.1.4

2 years ago

1.1.3

2 years ago

1.1.2

2 years ago

1.1.1

2 years ago

1.1.0

2 years ago

1.0.12

2 years ago

1.0.11

2 years ago

1.0.10

2 years ago

1.0.9

2 years ago

1.0.8

2 years ago

1.0.7

2 years ago

1.0.6

2 years ago

1.0.5

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago