When you need the fewest moving parts
The simplest Minum program (see more code samples below):
public class Main {
public static void main(String[] args) {
var minum = FullSystem.initialize();
var wf = minum.getWebFramework();
wf.registerPath(GET, "",
r -> Response.htmlOk("<p>Hi there world!</p>"));
minum.block();
}
}This web framework, "Minum", provides a full-powered minimalist foundation for a web application. For TDD, by TDD.
make test_coverage)make mutation_test)Minum is five thousand lines of code - the "minimalist" competitors range from 400,000 to 700,000 lines when accounting for their dependencies. I have not found a similar project.
Applying a minimalist approach enables easier debugging, maintainability, and lower overall cost. Most frameworks trade faster start-up for a higher overall cost. If you need sustainable quality, the software must be well-tested and documented from the onset. As an example, this project's ability to attain such high test coverage was greatly enabled by the minimalism paradigm.
There is a Quick start, or if you have a bit more time, consider trying the tutorial
<dependency>
<groupId>com.renomad</groupId>
<artifactId>minum</artifactId>
<version>8.0.5</version>
</dependency>Compiled size: 200 kilobytes.
Lines of production code (including required dependencies)
| Minum | Javalin | Spring Boot |
|---|---|---|
| 5,335 | 141,048 | 1,085,405 |
See details
See framework performance comparison
See the following links for sample projects that use this framework.
Smallest-possible
This project is valuable to see the minimal-possible application that can be made. This might be a good starting point for use of Minum on a new project.
Example
This is a good example to see a basic project with various functionality. It shows many of the typical use cases of the Minum framework.
Memoria project
This is a family-tree project. It demonstrates the kind of approach this framework is meant to foster.
Instantiating a new database:
var db = new Db<>(foosDirectory, context, new Foo());Adding a new object to a database:
var foo = new Foo(0L, 42, "blue");
db.write(foo); Updating an object in a database:
foo.setColor("orange");
db.write(foo); Deleting from a database:
db.delete(foo); Writing a log statement:
logger.logDebug(() -> "hello");Parsing an HTML document:
List<HtmlParseNode> results = new HtmlParser().parse("<p></p>");Searching for an element in the parsed graph:
HtmlParseNode node;
List<HtmlParseNode> results = node.search(TagName.P, Map.of());Creating a new web handler (a function that handles an HTTP request and returns a response):
public Response myHandler(Request r) {
return Response.htmlOk("<p>Hi world!</p>");
}Registering that endpoint:
webFramework.registerPath(GET, "formentry", sd::formEntry);Building and rendering a template:
TemplateProcessor foo = TemplateProcessor.buildProcessor("hello {{ name }}");
String rendered = foo.renderTemplate(Map.of("name", "world"));Getting a query parameter from a request:
String id = r.requestLine().queryString().get("id");Getting a body parameter from a request, as a string:
String personId = request.body().asString("person_id");Get a path parameter from a request as a string:
Pattern requestRegex = Pattern.compile(".well-known/acme-challenge/(?<challengeValue>.*$)");
final var challengeMatcher = requestRegex.matcher(request.requestLine().getPathDetails().isolatedPath());
// When the find command is run, it changes state so we can search by matching group
if (! challengeMatcher.find()) {
return new Response(StatusLine.StatusCode.CODE_400_BAD_REQUEST);
}
String tokenFileName = challengeMatcher.group("challengeValue");Getting a body parameter from a request, as a byte array:
byte[] photoBytes = body.asBytes("image_uploads");Checking for a log message during tests:
assertTrue(logger.doesMessageExist("Bad path requested at readFile: ../testingreadfile.txt"));