or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

api-versioning.mdasync.mdconfiguration.mdcontent-negotiation.mdcontroller-annotations.mdcore-framework.mdcors.mddata-binding.mdexception-handling.mdflash-attributes.mdfunctional-endpoints.mdi18n.mdindex.mdinterceptors.mdmultipart.mdrequest-binding.mdresource-handling.mdresponse-handling.mduri-building.mdview-resolution.md
tile.json

functional-endpoints.mddocs/

Functional Endpoints

Functional programming model for defining routes and handlers as an alternative to annotation-based controllers, providing a lightweight and composable approach to request handling.

Capabilities

HandlerFunction

Represents a function that handles a request and produces a response.

/**
 * Represents a function that handles a ServerRequest.
 *
 * @param <T> the type of the response of the function
 */
@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
    /**
     * Handle the given request.
     *
     * @param request the request to handle
     * @return the response
     * @throws Exception if an error occurs during handling
     */
    T handle(ServerRequest request) throws Exception;
}

RouterFunction

Represents a function that routes to a handler function.

/**
 * Represents a function that routes to a handler function.
 *
 * @param <T> the type of the response of the handler function
 */
@FunctionalInterface
public interface RouterFunction<T extends ServerResponse> {
    /**
     * Return the handler function that matches the given request.
     *
     * @param request the request to route
     * @return an Optional describing the HandlerFunction that matches this request,
     * or an empty Optional if there is no match
     */
    Optional<HandlerFunction<T>> route(ServerRequest request);

    /**
     * Return a composed routing function that first invokes this function,
     * and then invokes the given function if this route had no result.
     *
     * @param other the function to apply when this function has no result
     * @return a composed function
     */
    default RouterFunction<T> and(RouterFunction<T> other) {}

    /**
     * Return a composed routing function that routes to the given handler function
     * if this route does not match and the given request predicate applies.
     *
     * @param predicate the predicate to test
     * @param handlerFunction the handler function to route to
     * @return a composed function
     */
    default RouterFunction<T> andRoute(RequestPredicate predicate,
                                       HandlerFunction<T> handlerFunction) {}

    /**
     * Filter all handler functions routed by this function with the given filter function.
     *
     * @param filterFunction the filter to apply
     * @param <S> the filter return type
     * @return the filtered routing function
     */
    default <S extends ServerResponse> RouterFunction<S> filter(
            HandlerFilterFunction<T, S> filterFunction) {}
}

ServerRequest

Represents an HTTP request in functional style.

/**
 * Represents a server-side HTTP request.
 */
public interface ServerRequest {
    /**
     * Get the HTTP method.
     *
     * @return the HTTP method as an HttpMethod enum value
     */
    HttpMethod method();

    /**
     * Get the request URI.
     *
     * @return the URI of the request
     */
    URI uri();

    /**
     * Get the headers of this request.
     *
     * @return the headers
     */
    ServerRequest.Headers headers();

    /**
     * Extract the body as an object of the given type.
     *
     * @param bodyType the class of the body
     * @param <T> the body type
     * @return the body
     * @throws IOException if an I/O error occurs
     */
    <T> T body(Class<T> bodyType) throws IOException;

    /**
     * Extract the body as an object of the given type.
     *
     * @param bodyType the type of the body
     * @param <T> the body type
     * @return the body
     * @throws IOException if an I/O error occurs
     */
    <T> T body(ParameterizedTypeReference<T> bodyType) throws IOException;

    /**
     * Get the request parameter value for the given name, if present.
     *
     * @param name the parameter name
     * @return the parameter value
     */
    Optional<String> param(String name);

    /**
     * Get all parameters for this request.
     *
     * @return a map of all parameter names to parameter values
     */
    MultiValueMap<String, String> params();

    /**
     * Get the path variable with the given name, if present.
     *
     * @param name the variable name
     * @return the variable value
     */
    String pathVariable(String name);

    /**
     * Get all path variables for this request.
     *
     * @return a map of all path variable names to path variable values
     */
    Map<String, String> pathVariables();

    /**
     * Get the cookies of this request.
     *
     * @return a map of cookie names to cookie values
     */
    MultiValueMap<String, Cookie> cookies();

    /**
     * Get the session associated with the request.
     *
     * @return the session
     */
    Optional<ServerRequest.Session> session();

    /**
     * Get the remote address to which this request is connected, if available.
     *
     * @return the remote address
     */
    Optional<InetSocketAddress> remoteAddress();

    /**
     * Get the header values for the given header name, if any.
     *
     * @param headerName the header name
     * @return the list of header values, or an empty list
     */
    List<String> header(String headerName);
}

ServerResponse

Represents an HTTP response in functional style.

/**
 * Represents a server-side HTTP response.
 */
public interface ServerResponse {
    /**
     * Return the status code of this response.
     *
     * @return the status as an HttpStatusCode value
     */
    HttpStatusCode statusCode();

    /**
     * Return the headers of this response.
     *
     * @return the headers
     */
    HttpHeaders headers();

    /**
     * Return the cookies of this response.
     *
     * @return the cookies
     */
    MultiValueMap<String, Cookie> cookies();

    /**
     * Create a builder with the status set to 200 OK.
     *
     * @return the created builder
     */
    static BodyBuilder ok() {}

    /**
     * Create a builder with a CREATED status and a location header
     * set to the given URI.
     *
     * @param location the location URI
     * @return the created builder
     */
    static BodyBuilder created(URI location) {}

    /**
     * Create a builder with an ACCEPTED status.
     *
     * @return the created builder
     */
    static BodyBuilder accepted() {}

    /**
     * Create a builder with a NO_CONTENT status.
     *
     * @return the created builder
     */
    static HeadersBuilder<?> noContent() {}

    /**
     * Create a builder with a BAD_REQUEST status.
     *
     * @return the created builder
     */
    static BodyBuilder badRequest() {}

    /**
     * Create a builder with a NOT_FOUND status.
     *
     * @return the created builder
     */
    static HeadersBuilder<?> notFound() {}

    /**
     * Create a builder with the given status.
     *
     * @param status the response status
     * @return the created builder
     */
    static BodyBuilder status(HttpStatusCode status) {}

    /**
     * Create a builder with the given status.
     *
     * @param status the response status
     * @return the created builder
     */
    static BodyBuilder status(int status) {}

    /**
     * Defines a builder that adds headers to the response.
     *
     * @param <B> the builder subclass
     */
    interface HeadersBuilder<B extends HeadersBuilder<B>> {
        B header(String headerName, String... headerValues);
        B headers(Consumer<HttpHeaders> headersConsumer);
        B cookie(Cookie cookie);
        B cookies(Consumer<MultiValueMap<String, Cookie>> cookiesConsumer);
        B allow(HttpMethod... allowedMethods);
        B eTag(String etag);
        B lastModified(Instant lastModified);
        B location(URI location);
        B cacheControl(CacheControl cacheControl);
        B varyBy(String... requestHeaders);
        ServerResponse build();
    }

    /**
     * Defines a builder that adds a body to the response.
     */
    interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
        BodyBuilder contentLength(long contentLength);
        BodyBuilder contentType(MediaType contentType);
        ServerResponse body(Object body);
        ServerResponse body(Object body, ParameterizedTypeReference<?> bodyType);
    }
}

RouterFunctions

Utility class for creating RouterFunction instances.

/**
 * Central class for creating RouterFunction instances through the builder-style
 * RouterFunctions.route() method.
 */
public abstract class RouterFunctions {
    /**
     * Route to the given handler function if the given request predicate applies.
     *
     * @param predicate the predicate to test
     * @param handlerFunction the handler function to route to
     * @param <T> the type of response returned by the handler function
     * @return a router function that routes to handlerFunction if predicate evaluates to true
     */
    public static <T extends ServerResponse> RouterFunction<T> route(
            RequestPredicate predicate, HandlerFunction<T> handlerFunction) {}

    /**
     * Create a RouterFunctions.Builder to build a RouterFunction.
     *
     * @return the builder
     */
    public static RouterFunctions.Builder route() {}

    /**
     * Defines a builder for creating a composed RouterFunction.
     */
    public interface Builder {
        Builder GET(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder GET(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);
        Builder HEAD(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder POST(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder POST(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);
        Builder PUT(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder PUT(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);
        Builder PATCH(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder DELETE(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder OPTIONS(String pattern, HandlerFunction<ServerResponse> handlerFunction);
        Builder route(RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction);
        Builder add(RouterFunction<ServerResponse> routerFunction);
        Builder filter(HandlerFilterFunction<ServerResponse, ServerResponse> filterFunction);
        Builder before(Function<ServerRequest, ServerRequest> requestProcessor);
        Builder after(BiFunction<ServerRequest, ServerResponse, ServerResponse> responseProcessor);
        Builder onError(Predicate<Throwable> predicate, BiFunction<Throwable, ServerRequest, ServerResponse> responseProvider);
        RouterFunction<ServerResponse> build();
    }
}

Usage Example:

@Configuration
public class RouterConfig {

    @Bean
    public RouterFunction<ServerResponse> productRoutes(ProductHandler handler) {
        return RouterFunctions.route()
            .GET("/api/products", handler::getAllProducts)
            .GET("/api/products/{id}", handler::getProductById)
            .POST("/api/products", handler::createProduct)
            .PUT("/api/products/{id}", handler::updateProduct)
            .DELETE("/api/products/{id}", handler::deleteProduct)
            .filter((request, next) -> {
                log.info("Request: {} {}", request.method(), request.path());
                return next.handle(request);
            })
            .onError(IllegalArgumentException.class, (error, request) ->
                ServerResponse.badRequest().body(error.getMessage()))
            .build();
    }
}

@Component
public class ProductHandler {

    public ServerResponse getAllProducts(ServerRequest request) {
        List<Product> products = productService.findAll();
        return ServerResponse.ok().body(products);
    }

    public ServerResponse getProductById(ServerRequest request) {
        Long id = Long.parseLong(request.pathVariable("id"));
        Product product = productService.findById(id);
        return ServerResponse.ok().body(product);
    }

    public ServerResponse createProduct(ServerRequest request) throws IOException {
        Product product = request.body(Product.class);
        Product created = productService.save(product);
        URI location = URI.create("/api/products/" + created.getId());
        return ServerResponse.created(location).body(created);
    }

    public ServerResponse updateProduct(ServerRequest request) throws IOException {
        Long id = Long.parseLong(request.pathVariable("id"));
        Product product = request.body(Product.class);
        Product updated = productService.update(id, product);
        return ServerResponse.ok().body(updated);
    }

    public ServerResponse deleteProduct(ServerRequest request) {
        Long id = Long.parseLong(request.pathVariable("id"));
        productService.delete(id);
        return ServerResponse.noContent().build();
    }
}

Types

RequestPredicate

Predicate for matching requests.

@FunctionalInterface
public interface RequestPredicate {
    boolean test(ServerRequest request);
    default RequestPredicate and(RequestPredicate other) {}
    default RequestPredicate or(RequestPredicate other) {}
    default RequestPredicate negate() {}
}

RequestPredicates

Factory for common request predicates.

public abstract class RequestPredicates {
    public static RequestPredicate path(String pattern) {}
    public static RequestPredicate method(HttpMethod httpMethod) {}
    public static RequestPredicate GET(String pattern) {}
    public static RequestPredicate POST(String pattern) {}
    public static RequestPredicate PUT(String pattern) {}
    public static RequestPredicate DELETE(String pattern) {}
    public static RequestPredicate PATCH(String pattern) {}
    public static RequestPredicate contentType(MediaType... mediaTypes) {}
    public static RequestPredicate accept(MediaType... mediaTypes) {}
    public static RequestPredicate param(String name, String value) {}
    public static RequestPredicate header(String name, String value) {}
}