Elide supports the generation of OpenAPI documentation from Elide annotated beans. Specifically, it generates a JSON or YAML document conforming to the OpenAPI specification that can be used by tools like Swagger UI (among others) to explore, understand, and compose queries against your Elide API.
Only JSON-API endpoints are documented. The GraphQL API schema can be explored directly with tools like Graphiql.
Schema
annotations.If you are not using Elide Spring Starter or Elide Standalone (which package swagger as a dependency), you will need to pull in the following elide dependencies :
<dependency>
<groupId>com.yahoo.elide</groupId>
<artifactId>elide-swagger</artifactId>
</dependency>
<dependency>
<groupId>com.yahoo.elide</groupId>
<artifactId>elide-core</artifactId>
</dependency>
Pull in swagger core :
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-core-jakarta</artifactId>
</dependency>
If you are using Elide Spring Autoconfigure, you can customize the OpenAPI
document by using a OpenApiDocumentCustomizer
bean:
@Configuration
public class ElideConfiguration {
@Bean
public OpenApiDocumentCustomizer openApiDocumentCustomizer() {
return openApi -> {
Info info = new Info()
.title("My Title");
openApi.setInfo(info);
};
}
}
The application yaml file has settings:
elide:
api-docs:
enabled: true
path: /doc
version: openapi_3_0
If you want Swagger UI to acquire & use a bearer token from an OAuth identity provider, you can configure the OpenAPI document by using annotations:
@SpringBootApplication
@OpenAPIDefinition(info = @Info(title = "My Title"), security = @SecurityRequirement(name = "bearerAuth"))
@SecurityScheme(
name = "bearerAuth",
type = SecuritySchemeType.HTTP,
bearerFormat = "JWT",
scheme = "bearer"
)
public class App {
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
}
Elide contributes to Springdoc’s OpenAPI document by exposing a Springdoc OpenApiCustomizer
bean.
If API Versioning is used, only the Path strategy is supported when integrating with Springdoc as the other strategies are difficult to document with OpenAPI.
The default implementation is implemented in DefaultElideOpenApiCustomizer
. To override the behavior a ElideOpenApiCustomizer
bean can be created which will cause the DefaultElideOpenApiCustomizer
not to be configured.
@Configuration
public class ElideConfiguration {
@Bean
public ElideOpenApiCustomizer elideOpenApiCustomizer() {
return new MyCustomElideOpenApiCustomizer();
}
}
When GroupedOpenApi
is used, the ElideOpenApiCustomizer
is not applied to the groups. Instead Elide has a DefaultElideGroupedOpenApiCustomizer
that will customize the GroupedOpenApi
to set the appropriate OpenApiCustomizers
on the GroupedOpenApi
that matches the paths to match and exclude. To override the behavior a ElideGroupedOpenApiCustomizer
can be defined that will need to process the OpenApiCustomizers
and remove the ones automatically added by Elide.
@Configuration
public class ElideConfiguration {
@Bean
public ElideGroupedOpenApiCustomizer elideGroupedOpenApiCustomizer() {
return new MyCustomElideGroupedOpenApiCustomizer();
}
}
If you are using Elide Standalone, you can extend ElideStandaloneSettings
to:
public abstract class Settings implements ElideStandaloneSettings {
/**
* Enable OpenAPI documentation.
* @return whether OpenAPI is enabled;
*/
@Override
public boolean enableApiDocs() {
return true;
}
/**
* API root path specification for the OpenAPI endpoint. Namely, this is the root uri for OpenAPI docs.
*
* @return Default: /api-docs/*
*/
@Override
public String getApiDocsPathSpec() {
return "/api-docs/*";
}
/**
* OpenAPI documentation requires an API name.
* @return open api service name;
*/
@Override
public String getApiTitle() {
return "Elide Service";
}
/**
* The OpenAPI Specification Version to generate.
* @return the OpenAPI Specification Version to generate
*/
@Override
public OpenApiVersion getOpenApiVersion() {
return OpenApiVersion.OPENAPI_3_0;
}
/**
* Creates a singular OpenAPI document for JSON-API.
* @param dictionary Contains the static metadata about Elide models. .
* @return list of OpenAPI registration objects.
*/
@Override
public List<ApiDocsEndpoint.ApiDocsRegistration> buildApiDocs(EntityDictionary dictionary) {
List<ApiDocsEndpoint.ApiDocsRegistration> docs = new ArrayList<>();
dictionary.getApiVersions().stream().forEach(apiVersion -> {
Info info = new Info()
.title(getApiTitle())
.version(apiVersion);
OpenApiBuilder builder = new OpenApiBuilder(dictionary).apiVersion(apiVersion);
String moduleBasePath = getJsonApiPathSpec().replace("/*", "");
OpenAPI openApi = builder.build().info(info).addServersItem(new Server().url(moduleBasePath));
docs.add(new ApiDocsEndpoint.ApiDocsRegistration("test", () -> openApi, getOpenApiVersion().getValue(),
apiVersion));
});
return docs;
}
}
If you are using Elide directly as a library (and not using Elide Standalone), follow these instructions:
Create and initialize an entity dictionary.
EntityDictionary dictionary = EntityDictionary.builder().build();
dictionary.bindEntity(Book.class);
dictionary.bindEntity(Author.class);
dictionary.bindEntity(Publisher.class);
Create a Info object.
Info info = new Info().title("My Service").version("1");
Initialize a OpenAPI builder.
OpenApiBuilder builder = new OpenApiBuilder(dictionary);
Build the OpenAPI document
OpenAPI document = builder.build().info(info);
You can directly convert to JSON:
OpenApiDocument openApiDocument = new OpenApiDocument(document, OpenApiDocument.Version.from("3.0"));
String jsonOutput = openApiDocument.of(OpenApiDocument.MediaType.APPLICATION_JSON);
You can directly convert to YAML:
OpenApiDocument openApiDocument = new OpenApiDocument(document, OpenApiDocument.Version.from("3.0"));
String jsonOutput = openApiDocument.of(OpenApiDocument.MediaType.APPLICATION_YAML);
Or you can use the OpenAPI document directly to configure the provided JAX-RS Endpoint:
List<ApiDocsEndpoint.ApiDocsRegistration> apiDocs = new ArrayList<>();
OpenAPI openApi = new OpenAPI();
apiDocs.add(new ApiDocsEndpoint.ApiDocsRegistration("test", () -> openApi, "3.0", ""));
//Dependency Inject the ApiDocsEndpoint JAX-RS resource
bind(apiDocs).named("apiDocs").to(new TypeLiteral<List<ApiDocsEndpoint.ApiDocsRegistration>>() {
});
If you want Swagger UI to acquire & use a bearer token from an OAuth identity provider, you can configure the OpenAPI document similar to:
SecurityScheme oauthDef = new SecurityScheme()
.name("bearerAuth")
.type(SecurityScheme.Type.HTTP)
.bearerFormat("JWT")
.scheme("bearer");
SecurityRequirement oauthReq = new SecurityRequirement().addList("bearerAuth");
OpenApiBuilder builder = new OpenApiBuilder(entityDictionary);
OpenAPI document = builder.build();
document.addSecurityItem(oauthReq);
document.getComponents().addSecuritySchemes("bearerAuth", oauthDef);
A query or header parameter can be added globally to all Elide API endpoints:
Parameter oauthParam = new Parameter()
.in("Header")
.name("Authorization")
.schema(new StringSchema())
.description("OAuth bearer token")
.required(false);
OpenApiBuilder builder = new OpenApiBuilder(dictionary)
.globalParameter(oauthParam);
An HTTP response can be added globally to all Elide API endpoints:
ApiResponse rateLimitedResponse = new ApiResponse().description("Too Many Requests");
OpenApiBuilder builder = new OpenApiBuilder(dictionary)
.globalResponse(429, rateLimitedResponse);
The Swagger UI is very slow when the number of generated URL paths exceeds a few dozen. For large, complex data models, it is recommended to generate separate OpenAPI documents for subgraphs of the model.
Set<Type<?>> entities = Set.of(
ClassType.of(Book.class),
ClassType.of(Author.class),
ClassType.of(Publisher.class)
);
OpenApiBuilder builder = new OpenApiBuilder(dictionary)
.managedClasses(entities);
In the above example, the OpenApiBuilder
will only generate paths that exclusively traverse the provided set of entities.
The size of the OpenAPI document can be reduced significantly by limiting the number of filter operators that are used to generate query parameter documentation.
OpenApiBuilder builder = new OpenApiBuilder(dictionary)
.filterOperators(Set.of(Operator.IN));
In the above example, filter query parameters are only generated for the IN operator.
Elide extracts the model description and title from the Schema
and Include
annotations and adds them to the OpenAPI documentation. Schema
has precedence over Include
if both are present.
@Schema(description = "A book model description", title = "Book")
class Book {
}
For Schema
only the description and title property is extracted. For the Include
annotation, the friendlyName is used as the title.
Elide extracts properties from the Schema
annotation and adds them to the OpenAPI documentation.
class Book {
@Schema(requiredMode = RequiredMode.REQUIRED)
public String title;
}
Only the required, value, example, readOnly, writeOnly, requiredProperties, requiredMode and accessMode properties are extracted. This is currently only supported for attributes on Elide models.
OpenAPI documents are tied to an explicit API version. When constructing a OpenAPI document, the API version must be set to match the API version of the models it will describe:
OpenApiBuilder builder = new OpenApiBuilder(dictionary).apiVersion("1");
OpenAPI openApi = builder.build();