Graph APIs are an evolution of web service APIs that serve and manipulate data for mobile & web applications. They have a number of characteristics that make them well suited to this task:
Elide supports the two most widely adopted standards for graph APIs:
All Elide APIs share a common set of concepts:
Elide allows multiple versions of the same models to coexist and for clients to request a particular instance. Elide JAX-RS endpoints (elide-standalone) and Spring conrollers (Spring) support an API version header (‘ApiVersion’) that can be set to match the model annotation (@ApiVersion
) version.
If no version is specified by the client, Elide only exposes the models that lack an @ApiVersion
annotation.
Swagger endpoints (JSON-API) and GraphQL schemas are also scoped by the ApiVersion
header. They only return the schema corresponding to the requested API version.
Details of how to version Elide models can be found here. Details of how to configure versioned Swagger documents can be found here.
Elide attempts to deserialize and coerce fields in the client payload into the underlying type defined in the data model. Similarly, Elide will serialize the data model fields into the text format defined by the schema of the client payload.
Beyond primitive, numeric, and String types, Elide can serialize and deserialize complex and user defined types.
To register a new type for serialization and deserialization, define a Serde
(short for Serializer/Deserializer):
/**
* Bidirectional conversion from one type to another.
* @param <S> The serialized type
* @param <T> The deserialized type
*/
public interface Serde<S, T> {
/**
* Deserialize an instance of type S to type T.
* @param val The thing to deserialize
* @return The deserialized value
*/
T deserialize(S val);
/**
* Serializes an instance of type T as type S.
* @param val The thing to serialize
* @return The serialized value
*/
S serialize(T val);
}
At startup, Elide will automatically discover any Serde
classes annotated with ElideTypeConverter
:
@ElideTypeConverter(type = OffsetDateTime.class, name = "OffsetDateTime")
public class OffsetDateTimeSerde implements Serde<String, OffsetDateTime> {
@Override
public OffsetDateTime deserialize(String val) {
return OffsetDateTime.parse(val, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
@Override
public String serialize(OffsetDateTime val) {
return val.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
}
Elide has built-in support for either:
Elide Spring Boot is configured by default to use IS08601 dates.
This can be toggled by overriding the Elide
autoconfigure bean:
@Bean
public Elide initializeElide(EntityDictionary dictionary, DataStore dataStore, ElideConfigProperties settings) {
ElideSettingsBuilder builder = new ElideSettingsBuilder(dataStore)
...
.withEpochDates();
return new Elide(builder.build());
Elide standalone defaults to ISO8601 dates. This can be toggled by overriding the following setting in ElideStandaloneSettings
:
/**
* Whether Dates should be ISO8601 strings (true) or epochs (false).
* @return
*/
default boolean enableISO8601Dates() {
return true;
}
If using Elide as a library, the following date serdes can be registered:
Elide has built in support for converting between String and UUIDs. The conversion leverages UUID.fromString
.
Elide has built in support for converting between Strings or Integers to enumeration types (by name or value respectively).
For normal error handling, Elide throws runtime exceptions which are mapped to error responses. You can override any error response in Elide by providing a custom ErrorMapper
:
/**
* The ErrorMapper allows mapping any RuntimeException of your choice into more meaningful
* CustomErrorExceptions to improved your error response to the client.
*/
@FunctionalInterface
public interface ErrorMapper {
/**
* @param origin any Exception not caught by default
* @return a mapped CustomErrorException or null if you do not want to map this error
*/
@Nullable CustomErrorException map(Exception origin);
}
The mapper returns a CustomErrorException
which allows the developer complete control over the error objects returned in the ‘errors’ array for both JSON-API and GraphQL.
ErrorObjects.ErrorObjectsBuilder builder = ErrorObjects.builder()
.withCode("506")
.with("customKey", "customValue")
.withDetail("A detailed message")
.addError();
// Add a second error to the 'errors' array:
builder
.withCode("540")
.with("customKey", "customValue")
.withDetail("A different message")
.addError();
throw new CustomErrorException(500, "Exception while doing something", builder.build());
You can configure a custom ErrorMapper as follows:
//Override the following bean:
@Bean
@Override
public ErrorMapper getErrorMapper() {
return error -> null;
}
//Override ElideStandaloneSettings
@Override
public ErrorMapper getErrorMapper() {
return error -> null;
}