Skip to content

3. πŸ“„ Generating OpenApi REST Definitions

Cesar Parra edited this page Sep 9, 2024 · 1 revision

ApexDocs supports generating OpenApi 3.1.0 REST definitions based on any @RestResource classes in your source code.

Usage

To create an OpenApi specification file, run apexdocs openapi When using this generator, you can also pass a custom title through the --title parameter. This title will be placed in the output file's info.title property, as defined by the OpenApi documentation for the Info Object

apexdocs openapi -s ./src -t docs --title "Custom OpenApi Title"

How It Works

When generating an OpenApi document, ApexDocs will run through all classes annotated with @RestResource and add it to the output OpenApi file.

Once it finishes running, a file named openapi.json (unless a different name is specified) will be created in the specified --targetDir.

Configuring What Gets Created

ApexDocs will automatically parse your source code and generate the OpenApi definition based on the HTTP related Apex annotations (RestResource, HttpDelete, HttpGet, HttpPatch, HttpPost, HttpGet). The different HTTP annotations will be used to generate a file that complies with the OpenApi Specification v3.1.0

Besides these annotations, the ApexDocs tool will also use any information provided through your code's Apexdocs, relying on some custom annotations that are specific to generating OpenApi definitions:

  • @http-request-body
  • @http-parameter
  • @http-response

@http-request-body

Allows you to specify the HTTP request's expected request body. It supports receiving a description, whether it is required or not, and a schema, which defines the shape of the object that is expected.

πŸ“ Note that only one @http-request-body should be defined per method. If you add more than one, only a single one will be used when generating the OpenApi definition.

The schema can either be a reference to another class in your source code (see the Class References section below) or a fully defined custom schema (See the Custom Schemas section below).

Example

/**
 * @description This is a sample HTTP Post method
 * @http-request-body
 * description: This is an example of a request body
 * required: true
 * schema: ClassName
 */
@HttpPost
global static void doPost() {
    ///...
}

πŸ“ Note that each parameter of this annotation is expected to be on its own line. Parameters are treated as YAML, so spacing is important.

@http-parameter

Allows you to specify any HTTP parameter expected by your method. It supports receiving a name, an in as defined by the supported Parameter Locations, whether it is required or not, a description, and a schema.

πŸ“ Note that you can specify as many @http-parameter annotations as needed.

Example

/**
 * @description This is a sample HTTP Post method
 * @return A String SObject.
 * @http-parameter
 * name: limit
 * in: query
 * required: true
 * description: Limits the number of items on a page
 * schema:
 *   type: integer
 * @http-parameter
 * name: complex
 * in: cookie
 * schema: MyClassName
 */
@HttpPost
global static String doPost() {
    // ..
}

πŸ“ Note that each parameter of this annotation is expected to be on its own line. Parameters are treated as YAML, so spacing is important.

@http-response

Allows you to specify any HTTP response returned by your method. It supports receiving a statusCode with the response code, a description, and a schema.

If no description is provided then one will be automatically built using the statusCode.

πŸ“ Note that you can specify as many @http-parameter annotations as needed.

/**
 * @description This is a sample HTTP Post method
 * @return A String SObject.
 * @http-response
 * statusCode: 200
 * schema: SuccessfulResponseClassName
 * @http-response
 * statusCode: 500
 * description: Status Code 500 - An internal server error occurred.
 * schema:
 *   type: string
 */
@HttpPost
global static String doPost() {
    // ...
}

Class References

Whenever specifying a schema parameter, you can pass as a string the name of any class in your source code. This class will be parsed by the ApexDocs tool and automatically converted to a reference in the resulting OpenApi definition.

The tool will parse the class and create a reference that complies with Apex's support for User-Defined Types

Reference Overrides

When dealing with references, there might be cases when you want to manually tell the parser what type of object a property or field is. For example, let's say we have a class that looks as follows

public class MyClass {
    public Object myObject;
    public Account myAccountRecord;
}

In this case myObject has a type of Object, and myAccountRecord is an SObject. Neither of these will be accurately parsed when building the OpenApi definition, instead they will be simple be referenced as object without any properties.

To accurately represent the shape of these objects, you can use the @http-schema annotation to essentially override its type during parsing. In this annotation you can specify the same thing you would in any schema property when dealing with any of the main @http-* methods, meaning a reference to another class, or a Custom Schema (as defined below).

public class MyClass {
    /**
     * @description This is a generic reference to another class 
     * @http-schema MyOtherClassName
     */
    public Object myObject;

    /**
     * @description This is a reference to an Account SObject
     * @http-schema
     * type: object
     * properties:
     *   Id:
     *     type: string
     *   Name:
     *     type: string
     *   CustomField__c:
     *     type: number
     */
    public Account myAccountRecord;
}

If dealing with a collection, you can also specify the name of the reference either using the List or Set syntax.

πŸ“ When using List or Set syntax in the schema of the ApexDoc @http-* annotation, only collections one level deep are supported (e.g. List<List> is not supported). This is only a limitation when referencing collections on the ApexDoc schema property directly, and is fully supported when multi-level collections are inside of a referenced class as part of your codebase.

Maps are not supported, as it is not possible to know which keys the map will contain, and thus it is not possible to convert that to a valid specification. For this use case, define a Custom Schema as explained below.

/**
 * @description This is a sample HTTP Post method
 * @http-request-body
 * description: This is an example of a request body
 * schema: List<ClassName>
 */
@HttpPost
global static void doPost() {
    ///...
}

Inner class references are also supported, but note that you need to pass the full name of the reference, by using the ParentClassName.InnerClassName syntax, even if the inner class resides on the same class as the HTTP method referencing it.

/**
 * @description This is a sample HTTP Post method
 * @http-request-body
 * description: This is an example of a request body
 * schema: ParentClass.InnerClass
 */
@HttpPost
global static void doPost() {
    ///...
}

Custom Schemas

For any schema parameter in any of the HTTP ApexDocs annotations, besides specifying the name of a class, you can also specify a custom schema definition. The schema definition can either be for a primitive type, an object or an array

Primitives

For primitives, you should specify the type and an optional format, as defined by the OpenApi Specification on Data Types

/**
 * ...
 * schema:
 *   type: string
 *   format: password
 */

Objects

To specify a custom object schema, use object as the type, and specify as many properties as follows:

/**
 * schema:
 *   type: object
 *   properties:
 *     id:
 *       type: string
 *       description: The super Id.
 *     name:
 *       type: string
 *     phone:
 *       type: string
 *       format: byte
*/

Properties can be defined as primitives (as explained above), other objects, or arrays (explained below)

Arrays

To specify a custom array schema, use array as the type, and provide an items definition. In items you can specify the definition of any other custom type (primitives, objects, or other arrays).

/**
 * schema:
 *   type: array
 *   items:
 *     type: object
 *     properties:
 *       name:
 *         type: string
 */

SObject References

ApexDocs is not able to automatically parse SObject references, as it can with class references, as it does not reach into your org to get existing SObject describes. Because of this, when dealing with SObject references you should create a Custom Schema as defined above. This will also allow you to specify which specific fields are being received or returned.

Considerations

Please be aware of the following when using ApexDocs to create an OpenApi definition:

  • Map references are resolved as object with no properties, as it is not possible to know which keys the map will contain. When using maps either create a class that better represents the shape of the object and use a Class Reference, or define a Custom Schema in the schema section of the ApexDoc itself.
  • Same thing when referencing SObjects, as SObject describe parsing is not supported by the ApexDocs tool. When referencing SObjects, consider defining a Custom Schema in the schema section of the ApexDoc.
  • ApexDoc is only able to parse through your source code, so references to other packages (namespaced classes) or any code that lives outside your source code is not supported. Consider creating a Custom Schema for those situations.
  • The return value and received parameters or your methods are currently not being considered when creating the OpenApi definition file. Instead, use the @http-response ApexDoc annotation to specify the return value, and @http-parameter to specify any expected parameter.