A lightweight HTTP framework for Java, inspired by Express.js
Heo is a minimalist HTTP framework built entirely with Java Core. It brings the simplicity and convenience of Express.js to Java, perfect for learning, rapid prototyping, and building lightweight web applications.
- Easy to learn: Familiar syntax like Express.js
- Lightweight: No external dependencies
- Flexible: Supports middleware and dynamic routing
- π Express-like API:
app.get()
,app.post()
,app.use()
,app.listen()
,... - β‘ Middleware Support: CORS, JSON parsing, logging, error handling
- π£οΈ Smart Routing: Dynamic routes with parameters (e.g.,
/users/:id
) - π§ Built-in Utilities: JSON parser, URL-encoded parser, CORS, Morgan logging
- π¦ Zero Dependencies: Only uses Java Core APIs
- Java 22 or higher
- Maven
The easiest way to use Heo is through Maven with JitPack. Follow these steps:
-
Create a new Maven project or open your existing project
-
Add the JitPack repository to your
pom.xml
:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
- Add Heo dependency:
<dependency>
<groupId>com.github.HeoJV</groupId>
<artifactId>heo</artifactId>
<version>v1.0.0</version>
</dependency>
- Reload Maven project to download dependencies
If you want to modify the framework or contribute:
- Clone the repository:
git clone https://github.com/HeoJV/heo.git
cd heo
- Build with Maven:
mvn clean install
- Run the example (optional):
mvn compile exec:java -Dexec.mainClass="examples.HelloWorld"
import heo.server.Heo;
public class HelloWorld {
public static void main(String[] args) {
Heo app = new Heo();
// Simple route
app.get("/", (req, res, next) -> {
res.send("Hello from Heo! π·");
});
// Start server
app.listen(3000, () -> {
System.out.println("π Server running at http://localhost:3000");
});
}
}
Heo app = new Heo();
// GET route
app.get("/users", (req, res, next) -> {
res.json(Map.of("users", Arrays.asList("Alice", "Bob", "Charlie")));
});
// POST route
app.post("/users", (req, res, next) -> {
Map<String, Object> newUser = req.getBody();
res.status(201).json(Map.of(
"message", "User created successfully",
"user", newUser
));
});
// Dynamic route with parameters
app.get("/users/:id", (req, res, next) -> {
String userId = req.params("id");
res.json(Map.of("userId", userId, "name", "User " + userId));
});
// Route with multiple parameters
app.get("/users/:id/posts/:postId", (req, res, next) -> {
String userId = req.params("id");
String postId = req.params("postId");
res.json(Map.of("userId", userId, "postId", postId));
});
import heo.middleware.Middleware;
import heo.http.Request;
import heo.http.Response;
class AuthMiddleware implements Middleware {
@Override
public void handle(Request req, Response res, Middleware next) {
String authHeader = req.getHeader("Authorization");
if (authHeader != null && authHeader.equals("Bearer secret-token")) {
next.handle(req, res);
} else {
res.status(401).json(Map.of("error", "Unauthorized"));
}
}
}
import heo.middleware.*;
Heo app = new Heo();
// Global middleware
app.use(new Morgan("dev")); // Request logging
app.use(new Json()); // Parse JSON body
app.use(new Cors(
List.of("*"), // Allowed origins
List.of("GET", "POST", "PUT"), // Allowed methods
List.of("*"), // Allowed headers
true // Allow credentials
));
// Route-specific middleware
app.get("/protected", authMiddleware, (req, res, next) -> {
res.send("Protected content");
});
app.post("/api/data", (req, res, next) -> {
// Getting information from Request
String method = req.getMethod(); // GET, POST, etc.
String path = req.path; // /api/data
String userId = req.params("id"); // URL parameters
String searchQuery = req.getQuery("q"); // Query parameter ?q=...
Map<String, Object> body = req.getBody(); // Parsed JSON body
String authHeader = req.getHeader("Authorization");
String clientIP = req.getIpAddress();
// Sending Response
res.status(200) // Set status code
.setHeader("Custom-Header", "value") // Set custom header
.json(Map.of( // Send JSON response
"success", true,
"data", body,
"timestamp", System.currentTimeMillis()
));
// Or send plain text
// res.send("Hello World!");
});
// Global error handler
app.use((error, req, res) -> {
System.err.println("Error occurred: " + error.getMessage());
res.status(500).json(Map.of("error", "Internal server error"));
});
// Throwing custom errors
app.get("/error-demo", (req, res, next) -> {
throw new NotFoundError("Resource not found");
});
// Handling specific errors
app.use((error, req, res) -> {
if (error instanceof BadRequest) {
res.status(400).json(Map.of("error", "Invalid request"));
} else if (error instanceof NotFoundError) {
res.status(404).json(Map.of("error", "Not found"));
} else {
res.status(500).json(Map.of("error", "Server error"));
}
});
Router apiRouter = new Router();
// Define API routes
apiRouter.get("/users", (req, res, next) -> {
res.json(Map.of("users", getUserList()));
});
apiRouter.post("/users", (req, res, next) -> {
Map<String, Object> newUser = req.getBody();
// User creation logic
res.status(201).json(Map.of("message", "User created"));
});
// Mount sub-router to main app
app.use("/api/v1", apiRouter); // All routes will have /api/v1 prefix
Middleware | Description | Usage |
---|---|---|
Json() |
Parse JSON request body | app.use(new Json()) |
Urlencoded() |
Parse URL-encoded forms | app.use(new Urlencoded()) |
Morgan() |
HTTP request logger | app.use(new Morgan("dev")) |
Cors() |
Enable CORS | app.use(new Cors(...)) |
"dev"
- Colored output for development, shows method, path, status, time"tiny"
- Minimal format"short"
- Short format"combined"
- Apache combined log format for production
import heo.server.Heo;
import heo.middleware.*;
import java.util.*;
public class UserAPI {
private static List<Map<String, Object>> users = new ArrayList<>();
public static void main(String[] args) {
Heo app = new Heo();
// Setup middleware
app.use(new Morgan("dev"));
app.use(new Json());
app.use(new Cors(List.of("*"), List.of("*"), List.of("*"), true));
// Get all users
app.get("/users", (req, res, next) -> {
res.json(Map.of("users", users));
});
// Create new user
app.post("/users", (req, res, next) -> {
Map<String, Object> newUser = req.getBody();
newUser.put("id", users.size() + 1);
users.add(newUser);
res.status(201).json(Map.of("message", "User created", "user", newUser));
});
// Get user by ID
app.get("/users/:id", (req, res, next) -> {
int userId = Integer.parseInt(req.params("id"));
users.stream()
.filter(user -> user.get("id").equals(userId))
.findFirst()
.ifPresentOrElse(
user -> res.json(user),
() -> res.status(404).json(Map.of("error", "User not found"))
);
});
app.listen(3000, () -> {
System.out.println("π User API server running on port 3000");
});
}
}
# .env file
PORT=8080
DEBUG=true
DB_URL=localhost:5432
import heo.config.Dotenv;
public class App {
public static void main(String[] args) {
Dotenv.config(); // Load .env file
int port = Dotenv.get("PORT") != null ?
Integer.parseInt(Dotenv.get("PORT")) : 3000;
Heo app = new Heo();
// ... setup routes
app.listen(port, () -> {
System.out.println("Server running on port " + port);
});
}
}
Run the included examples:
# HelloWorld example
cd examples
javac -cp ../out HelloWorld.java
java -cp ../out:. HelloWorld
# Visit http://localhost:3000
heo/
βββ src/main/java/heo/
β βββ server/ # Core server implementation
β βββ router/ # Routing system
β βββ middleware/ # Built-in middleware
β βββ http/ # HTTP request/response handling
β βββ exception/ # Error handling
β βββ config/ # Configuration utilities
β βββ core/ # Core utilities
βββ examples/ # Example applications
βββ docs/ # Documentation
We welcome all contributions! Please see CONTRIBUTING.md for detailed guidelines.
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by Express.js
- Built with β€οΈ for the Java community
- Thanks to all contributors
Happy coding with Heo! π·