Search code examples
javaspringspring-webfluxreactivespring-webclient

Return null or something nullable from lambda in transformation mehtod WebFlux


In a Spring WebFlux chain I use a map operation which sometimes may return null and I get a warning :
Return null or something nullable from lambda in transformation mehtod.
I believe when data is null it doesn't actually map input to null but it will raise an exception instead.
What is the best way to handle the scenario ?

Map method which is nullable :

public Pojo parseJson(String json) {
    try {
        // parse 
        return pojo;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

My reactive chain :

public Mono<Pojo> query(long id) {

    Integer value = idMapper.getValue(id);

    if (value != null) {
        return repo.query(value)

                        Parse Method
                             |
                             v
                .map(this::parse);
    }
    return null;
}

Solution

  • When working in a functional/reactive world you should try to avoid all null checks and try to never return null from any methods.

    Instead return Optional<T> when there is a risk for a null return, and return Mono.error when there is an error in a function that returns Mono. Or return Mono.empty if you just want to skip returning something.

    By using optional you can rewrite the code to something much cleaner.

    public Optional<Pojo> parseJson(String json) {
        // Return an optional if there is risk for a null.
        return Optional.ofNullable(new Pojo());
    }
    
    private final IdMapper idMapper = new IdMapper();
    private final Repo repo = new Repo();
    
    public Mono<Pojo> query(long id) {
    
        // By returning an optional from the idMapper 
        // you can chain on and avoid null checks.
        return idMapper.getValue(id).map(integer -> repo.query(integer)
                .map(s -> parseJson(s).map(Mono::just)
                        .orElse(Mono.empty())))
                .orElse(Mono.error(() -> new NotFoundException("No Pojo with ID could be found")));
    }
    
    class Pojo {
        // Some pojo class
    }
    
    class Repo {
        public Mono<String> query(long id) {
            return Mono.just("Foo");
        }
    }
    
    class IdMapper {
    
        public Optional<Integer> getValue(long id) {
            // Here return an Optional instead of null!
            return Optional.of(1);
        }
    }
    

    Here I return Optionals and make decisions to either return a Mono.empty or a Mono.error depending on what happens.