Skip to content

Instantly share code, notes, and snippets.

@audinue
Last active April 6, 2025 22:21
Show Gist options
  • Save audinue/cd1fc655bcc252a0d07d7d758feadf01 to your computer and use it in GitHub Desktop.
Save audinue/cd1fc655bcc252a0d07d7d758feadf01 to your computer and use it in GitHub Desktop.
Java Middleware Pattern Using Lambdas. A minimalist middleware system inspired by Express-style chaining, using Java functional interfaces.

Java Middleware Pattern (Lambda-Based)

This project demonstrates a lightweight middleware system in Java, inspired by Express.js-style chaining. It uses functional interfaces and lambdas to build a flexible, composable controller pipeline.

✨ Features

  • Chainable use() methods for building middleware stacks
  • Request and response modification support
  • Side effects (e.g. logging) via middleware
  • Grouping via MiddlewareGroup for reusable stacks
  • Clean, functional design with no external dependencies

πŸš€ Example Usage

new MainController()
    .use((req, next) -> new Response("BEFORE " + next.fetch(req).getMessage()))
    .use((req, next) -> new Response(next.fetch(req).getMessage() + " AFTER"))
    .fetch(new Request("Hello world!"));
// Output: BEFORE Hello world! AFTER

🧱 MiddlewareGroup

Compose multiple middleware layers into a single reusable group:

Middleware group = new MiddlewareGroup()
    .use((req, next) -> new Response("BEFORE " + next.fetch(req).getMessage()))
    .use((req, next) -> new Response(next.fetch(req).getMessage() + " AFTER"));

new MainController()
    .use(group)
    .fetch(new Request("Hello world!"));
// Output: BEFORE Hello world! AFTER
// Java Middleware Pattern (Lambda-Based)
// Inspired by web frameworks like Express.js, this pattern demonstrates
// how to chain middleware in Java using functional interfaces and lambdas.
package middleware;
class Request {
private final String message;
Request(String message) {
this.message = message;
}
String getMessage() {
return message;
}
@Override
public String toString() {
return message;
}
}
class Response {
private final String message;
Response(String message) {
this.message = message;
}
String getMessage() {
return message;
}
@Override
public String toString() {
return message;
}
}
@FunctionalInterface
interface Controller {
Response fetch(Request req);
}
@FunctionalInterface
interface Middleware {
Response fetch(Request req, Controller next);
}
class MainController implements Controller {
// The core (innermost) controller β€” called last in the chain.
// By default, it just echoes the request message as the response.
private Controller outermost = req -> new Response(req.getMessage());
public MainController use(Middleware middleware) {
Controller previous = outermost;
outermost = req -> middleware.fetch(req, previous);
return this;
}
@Override
public Response fetch(Request req) {
return outermost.fetch(req);
}
}
class MiddlewareGroup implements Middleware {
private Middleware outermost = (req, next) -> next.fetch(req);
public MiddlewareGroup use(Middleware middleware) {
Middleware previous = outermost;
outermost = (req, next) ->
middleware.fetch(req, r -> previous.fetch(r, next));
return this;
}
@Override
public Response fetch(Request req, Controller next) {
return outermost.fetch(req, next);
}
}
class Example {
public static void main(String[] args) {
// Basic echo
System.out.println(
new MainController()
.fetch(new Request("Hello world!"))
);
// Output: Hello world!
// Modify the response
System.out.println(
new MainController()
.use((req, next) -> new Response("BEFORE " + next.fetch(req).getMessage()))
.use((req, next) -> new Response(next.fetch(req).getMessage() + " AFTER"))
.fetch(new Request("Hello world!"))
);
// Output: BEFORE Hello world! AFTER
// Modify the request
System.out.println(
new MainController()
.use((req, next) -> next.fetch(new Request("BEFORE " + req.getMessage())))
.use((req, next) -> next.fetch(new Request(req.getMessage() + " AFTER")))
.fetch(new Request("Hello world!"))
);
// Output: BEFORE Hello world! AFTER
// Log the request/response
System.out.println(
new MainController()
.use((req, next) -> {
System.out.println("Requesting: " + req);
Response res = next.fetch(req);
System.out.println("Responding with: " + res);
return res;
})
.fetch(new Request("Hello world!"))
);
// Logs and Output:
// Requesting: Hello world!
// Responding with: Hello world!
// Hello world!
// Use a group of middleware
System.out.println(
new MainController()
.use(new MiddlewareGroup()
.use((req, next) -> new Response("BEFORE " + next.fetch(req).getMessage()))
.use((req, next) -> new Response(next.fetch(req).getMessage() + " AFTER")))
.fetch(new Request("Hello world!"))
);
// Output: BEFORE Hello world! AFTER
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment