Federated Entity Data Fetcher implementation (Spring for GraphQL users)

Problem statement: Need to write more code for implementing federated entity Data Fetcher using Spring for GraphQL framework.

Apollo-federation-jvm-spring-example: Spring GraphQL Federation Example

Expectation: New mapping annotation like @SchemaMapping to handle federated entity Data Fetcher

Our Custom Approach: To separate Data Fetchers based on cencerns, we followed the below approach. With this approach,

  • Teams can concentrate on their business logic.
  • Improve developer experience as the GraphQlConfig implementation can be done once in a common lib
  1. Create an Interface
public interface EntityDataFetcher {
	public static final String TYP_NAME = "__typename";
	public String getTypeName();
	public Class<?> getTypeClass();
	public Object fetch(@NotNull Map<String, Object> reference);

  1. Sample entity DataFetcher implementation
public class SampleDataFetcher implements EntityDataFetcher {
    public static final String SCHEMA_TYP_NM = "Sample";
    private SampleService sampleService;
    public String getTypeName() {
        return SCHEMA_TYP_NM; // Graph type, defined in schema
    public Class<?> getTypeClass() {
        return SampleResponseDTO.class; // Return object class, return by the data fetcher
    public Object fetch(@NotNull Map<String, Object> reference) {
        // Business logic to retrieve Sample entity
  1. GraphQlConfig implementation to register DataFetchers
public class GraphQlConfig {
	private List<EntityDataFetcher> dataFetchers;
	public GraphQlSourceBuilderCustomizer federationTransform() {
		return builder -> builder.schemaFactory((registry, wiring) ->
	        Federation.transform(registry, wiring)
	private DataFetcher<?> getEntityDataFetchers() {
		Map<String, EntityDataFetcher> map = getEntityDataFetchersMap();

		DataFetcher<?> entityDataFetcher = env -> {
			List<Map<String, Object>> representations = env.getArgument(_Entity.argumentName);
			return representations.stream().map(representation -> {
				final String typeName = (String) representation.get(EntityDataFetcher.TYP_NAME);
				if (map.containsKey(typeName)) {
					return map.get(typeName).fetch(representation);
				} else {
					return null;
		return entityDataFetcher;
	private ClassNameTypeResolver getClassNameTypeResolver() {
		ClassNameTypeResolver classNameTypeResolver = new ClassNameTypeResolver();
		if (dataFetchers != null) {
			dataFetchers.stream().forEach(d -> {			
				classNameTypeResolver.addMapping(d.getTypeClass(), d.getTypeName());
		return classNameTypeResolver;
	private Map<String, EntityDataFetcher> getEntityDataFetchersMap() {
		Map<String, EntityDataFetcher> map = null;
		if (dataFetchers != null) {
			map = dataFetchers.stream().collect(Collectors.toMap(EntityDataFetcher::getTypeName, d -> d));
		} else {
			map = Collections.emptyMap();
		return map;