在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):yandooo/spring-graphql-common开源软件地址(OpenSource Url):https://github.com/yandooo/spring-graphql-common开源编程语言(OpenSource Language):Java 100.0%开源软件介绍(OpenSource Introduction):Table of Contents Spring Framework GraphQL LibraryIntroInitially project was inspired by multiple projects
and main idea was transformed into generic lightweight mechanism for schema definition in java. It will be helpful for those who starts coding schema from scratch or upgrading technology stack moving to GraphQL. The library facilitates GraphQL schema development in SpringFramework environment heavily using annotations. The entire schema lives in Spring context which means developer can leverage all Spring features like AOP, IO etc. There is one important dependency on graphql-java for schema imperative building and GraphQL query execution. The query execution strategy for the READ and MUTATE queries is based on Reactor Reactive Streams with high level of parallelism. Note: process has been started on moving to complete Reactor RS Stack leveraging Netty GraphQL NIO server (spring boot starters should be updated as well). Reactor execution strategy will be strategic one available in the future releases. RxJava-based strategies are deprecated and won't be maintained anymore.
Requires
some of the dependencies can be removed in the future. repositories {
// stable build
jcenter()
// development build
maven { url "http://dl.bintray.com/oembedler/maven" }
} Dependency: dependencies {
compile 'com.embedler.moon.graphql:spring-graphql-common:INSERT_LATEST_VERSION_HERE'
} How to use the latest build with Maven: <repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-oembedler-maven</id>
<name>bintray</name>
<url>http://dl.bintray.com/oembedler/maven</url>
</repository> Dependency: <dependency>
<groupId>com.embedler.moon.graphql</groupId>
<artifactId>spring-graphql-common</artifactId>
<version>LATEST_VERSION_HERE</version>
</dependency> UsageThe entire schema definition is annotations driven.
There is a good support for generic types - so don't miss chance to use them if it's appropriate (see @GraphQLSchema
public class TodoSchema {
@GraphQLSchemaQuery
private RootObjectType root;
public static class AddTodoIn {
private String text;
}
// default value provider for an input mutation parameter
public AddTodoIn getAddTodoInputDefaultValue() {
AddTodoIn addTodoInput = new AddTodoIn();
addTodoInput.setText("--- default text ---");
return addTodoInput;
}
@GraphQLMutation
public
@GraphQLOut("todoEdge")
TodoObjectType.TodoEdgeObjectType addTodoMutation(@GraphQLIn("addTodoInput", defaultProvider = "getAddTodoInputDefaultValue") AddTodoIn addTodoInput) {
// mutation implementaion goes here
return todoEdgeObjectType;
} Use Supported typesAll Java wrapper types directly map to GraphQL types:
same for corresponding Java primitives.
Extensions:
Creating a new Object Type
@GraphQLObject("Root")
public class RootObjectType {
@GraphQLNonNull
@GraphQLDescription("Root query version number")
private String version;
@GraphQLField
@GraphQLDescription("Root viwer node as per Relay spec")
public UserObjectType viewer(/** no input expected **/) {
// custom data fetcher for the field with name 'viewer'
return userObjectType;
}
} If class field is accessible through getters \ setters - define field as a class member.
All class fields are included into object definition unless Creating a new Interface TypeIf interface must be considered as a part of the object hierarchy use @GraphQLInterface("Node")
public interface RelayNode {
@GraphQLID("id")
@GraphQLNonNull
@GraphQLDescription("GraphQL Relay global object unique identifier")
String getId(RelayNode relayNode);
} Creating a new Enum TypeJava enums are automatically discovered and mapped to // annotation is not required if enum names are acceptable as values
@GraphQLEnum(valueProvider = "getValue")
public enum Episode {
@GraphQLDescription("Released in 1977.")
NEWHOPE,
@GraphQLDescription("Released in 1980.")
EMPIRE,
@GraphQLDescription("Released in 1983.")
JEDI;
// enum field value provider must be static method with 1 input argument - its own enumeration type
public static Object getValue(Episode self) {
if (self == NEWHOPE)
return 4;
else if (self == EMPIRE)
return 5;
else
return 6;
}
}
// avaliable context objects are `obj` (enum instance itself) and `cls` - enum class
@GraphQLEnum(defaultSpel = "#obj.getActualValue()")
public enum EpisodeV2 {
@GraphQLDescription("Released in 1977.")
NEWHOPE(4),
@GraphQLDescription("Released in 1980.")
EMPIRE(5),
@GraphQLDescription("Released in 1983.")
JEDI(6);
int actualValue;
EpisodeV2(int val) {
actualValue = val;
}
public int getActualValue() {
return actualValue;
}
} Creating a new Union TypeGraphQL Union type is a java interface marked with @GraphQLUnion(value = "Pet", possibleTypes = {Dog.class, Cat.class})
public interface PetsUnionType {
// empty marker
// possible types must implement this interface
}
//...
@GraphQLField("pets")
public List<PetsUnionType> getPets() {
List<PetsUnionType> pets = new ArrayList<>();
pets.addAll(cats);
pets.addAll(dogs);
return pets;
} Creating a Object-Input TypeInput method arguments are automatically converted into @GraphQLField
public TodoObjectType.TodoConnectionObjectType todos(@GraphQLIn RelayConnectionArguments args) {
// `args` is extracted from arguments context
} @GraphQLField("manager")
public UserObjectType getManager(@GraphQLIn UserObjectType employee) {
// `employee` is extracted from upstream 'source' element as input parameters are empty
} MutationsMutation must be declared in the class marked as // default value provider for an input mutation parameter
public AddTodoIn getAddTodoInputDefaultValue() {
AddTodoIn addTodoInput = new AddTodoIn();
addTodoInput.setText("--- default text ---");
return addTodoInput;
}
@GraphQLMutation("addTodoMutation")
public
@GraphQLOut("todoEdge")
TodoObjectType.TodoEdgeObjectType addTodoMutation(@GraphQLIn("addTodoInput", defaultProvider = "getAddTodoInputDefaultValue") AddTodoIn addTodoInput, AddTodoIn2 addTodoInput2) {
// `addTodoInput` created based on input `variables`
// `addTodoInput2` is skipped as it's not marked explicitly as `@GraphQLIn` parameter
} Value for @GraphQLField("manager")
public UserObjectType getManager(UserObjectType employee,
@GraphQLIn(value = "ids", defaultSpel = "T(java.util.Collections).EMPTY_SET") Set<String> ids) {
// omitted for simplicity
} Spring configuration // there must be either `@ComponentScan` annotation defined for a schema base package
// or all beans must be instantiated explicitly in configuration class
@Configuration
@ComponentScan(basePackages = "com.oembedler.moon.graphql.test.todoschema")
public static class TodoSchemaConfiguration {
// use as is
@Bean
public GraphQLSchemaBeanFactory graphQLSchemaBeanFactory() {
return new SpringGraphQLSchemaBeanFactory();
}
// configuration can be customized depending on the case
@Bean
public GraphQLSchemaConfig graphQLSchemaConfig() {
GraphQLSchemaConfig graphQLSchemaConfig = new GraphQLSchemaConfig();
return graphQLSchemaConfig;
}
// use as is
@Bean
public GraphQLSchemaBuilder graphQLSchemaBuilder() {
return new GraphQLSchemaBuilder(graphQLSchemaConfig(), graphQLSchemaBeanFactory());
}
// use as is
@Bean
public GraphQLSchemaHolder graphQLSchemaHolder() {
return graphQLSchemaBuilder().buildSchema(TodoSchema.class);
}
} Executing queries async: RxExecutionResult result = GraphQLQueryExecutor
.create(graphQLSchemaHolder)
.query("{viewer{ id }}")
.execute();
// work with execution result Executing queries async with concurrent fields resolution
(see GraphQLRxExecutionResult result =
GraphQLQueryExecutor.builder()
.create(graphQLSchemaHolder)
.forkJoinExecutorService()
.query("{viewer{ id }}")
.execute(); check Schema build process can be customized using private String clientMutationIdName = "clientMutationId";
private boolean injectClientMutationId = true;
private boolean allowEmptyClientMutationId = false;
private String mutationInputArgumentName = "input";
private String outputObjectNamePrefix = "Payload";
private String inputObjectNamePrefix = "Input";
private String schemaMutationObjectName = "Mutation";
private boolean dateAsTimestamp = true;
private String dateFormat = "yyyy-MM-dd'T'HH:mm'Z'";
// there will be more config options added in the future Protection Against Malicious QueriesSince typical GraphQL schemas contain recursive types and circular dependencies, clients are able to send infinitely deep queries which may have high impact on server performance. The library provides two mechanisms to protect your GraphQL server from malicious or too expensive queries. Query Complexity AnalysisQuery complexity analysis makes an estimation of the query complexity during execution.
The complexity is public Integer getFirstDefaultValue() {
return 1;
}
public Integer getLastDefaultValue() {
return 1;
}
// `before`, `after`, `first`, `last` and `childScore` are avaliable in SpEL expression
@GraphQLField
@GraphQLComplexity("1 + first * #childScore")
public TodoConnectionObjectType todos(@GraphQLIn(value = "before") String before,
@GraphQLIn(value = "after") String after,
@GraphQLIn(value = "first", defaultProvider = "getFirstDefaultValue") Integer first,
@GraphQLIn(value = "last", defaultProvider = "getLastDefaultValue") Integer last) {
// implementation ommitted for the sake of simplicity
return todoConnectionObjectType;
} Note that above example has To set GraphQLQueryExecutor.create(graphQLSchemaHolder).maxQueryComplexity(1500); The query complexity algorithm is dynamic so typical During execution when maximum query complexity reached - library throws an Limiting Query DepthLimiting query depth can be done by providing the GraphQLQueryExecutor.create(graphQLSchemaHolder).maxQueryDepth(4); When maximum query depth is reached library does not throw any exception but returns Relay SupportLibrary adds abstractions for the Relay support.
Please look at the tests for Some tips: @GraphQLObject("Todo")
public class TodoObjectType extends BaseObjectType {
// fields definitions are omitted for clarity
@GraphQLObject
public static class TodoEdgeObjectType extends EdgeObjectType<TodoObjectType> {
// `EdgeObjectType` is generic class that can be extended to add custom behaviour
}
@GraphQLObject
public static class TodoConnectionObjectType extends ConnectionObjectType<TodoEdgeObjectType, PageInfoObjectType> {
// `ConnectionObjectType` is generic class that can be extended to add custom behaviour
}
} Node interface (given schema uses proper hierarchy of objects): // defined in library `relay` package
@GraphQLInterface("Node")
public interface RelayNode {
@GraphQLID("id")
@GraphQLNonNull
@GraphQLDescription("GraphQL Relay global object unique identifier")
String getId(RelayNode relayNode);
} All custom objects implement that interface through intermediate base class (no need to implement default bahaviour in each class): public class BaseObjectType implements RelayNode {
@GraphQLIgnore
private String id;
@GraphQLID("id")
@GraphQLNonNull
@GraphQLDescription("Global object unique identifier")
public String getId(RelayNode relayNode) {
BaseObjectType baseObjectType = (BaseObjectType) relayNode;
// `id` can be encoded into base64 if opaque value is required
return baseObjectType.id;
}
} Data resolver is defined in a root object query as follows: @GraphQLObject("Root")
public class RootObjectType {
@GraphQLField
public RelayNode node(@GraphQLID @GraphQLNonNull @GraphQLIn("id") final String id) {
// data resolver by global ID goes here
return null;
}
} Relay @GraphQLField
public TodoConnectionObjectType todos(@GraphQLIn(value = "before") String before,
@GraphQLIn(value =
全部评论
专题导读
上一篇:Novvum/36-graphql-concepts: 发布时间:2022-06-22下一篇:alexitaylor/angular-graphql-nestjs-postgres-starter-kit: 发布时间:2022-06-22热门推荐
热门话题
阅读排行榜
|
请发表评论