Working with Routes¶
Create custom web routes and API endpoints in your Shiina-Web plugins.
What Are Routes?¶
Routes define URL endpoints that users can access. They can serve:
- 🌐 Web Pages — HTML templates with dynamic content
- 🔌 REST APIs — JSON responses for integrations
- 📥 Form Handlers — Process POST requests
- 🔄 Redirects — Route users to different pages
Registering Routes¶
Routes are registered in your plugin's onEnable() method using the WebServer class.
Basic Registration¶
package com.example.plugin;
import com.example.plugin.routes.ExampleRoute;
import ch.qos.logback.classic.Logger;
import dev.osunolimits.main.WebServer;
import dev.osunolimits.plugins.ShiinaPlugin;
public class Plugin extends ShiinaPlugin {
@Override
protected void onEnable(String pluginName, Logger logger) {
// Register GET route
WebServer.get("/example", new ExampleRoute());
// Register POST route
WebServer.post("/example", new ExampleRoute());
logger.info("Routes registered successfully!");
}
@Override
protected void onDisable(String pluginName, Logger logger) {
// Routes are automatically unregistered
}
}
HTTP Methods¶
| Method | Usage | Example |
|---|---|---|
WebServer.get() |
Retrieve data, display pages | Profile pages, leaderboards |
WebServer.post() |
Submit forms, create resources | Login, registration |
WebServer.put() |
Update existing resources | Edit profile |
WebServer.delete() |
Remove resources | Delete account |
Creating API Routes¶
API routes return JSON data without HTML templates — perfect for integrations and AJAX requests.
Simple API Example¶
Create routes/ExampleRoute.java:
package com.example.plugin.routes;
import dev.osunolimits.modules.Shiina;
import dev.osunolimits.modules.ShiinaRoute;
import dev.osunolimits.modules.ShiinaRoute.ShiinaRequest;
import spark.Request;
import spark.Response;
public class ExampleRoute extends Shiina {
@Override
public Object handle(Request req, Response res) throws Exception {
// Initialize Shiina request context
ShiinaRequest shiina = new ShiinaRoute().handle(req, res);
// Set response type to JSON
res.type("application/json");
// Return JSON response
return "{\"status\": \"success\", \"message\": \"Hello from API!\"}";
}
}
Accessing the Route¶
Once registered, access your API at:
Database Access in Routes¶
Access MySQL database through the shiina.mysql object.
Query Examples¶
package com.example.plugin.routes;
import dev.osunolimits.modules.Shiina;
import dev.osunolimits.modules.ShiinaRoute;
import dev.osunolimits.modules.ShiinaRoute.ShiinaRequest;
import spark.Request;
import spark.Response;
import java.sql.ResultSet;
public class UserStatsRoute extends Shiina {
@Override
public Object handle(Request req, Response res) throws Exception {
ShiinaRequest shiina = new ShiinaRoute().handle(req, res);
// Get user ID from URL parameter
String userId = req.params(":id");
// Query database with prepared statement
ResultSet result = shiina.mysql.Query(
"SELECT * FROM users WHERE id = ?",
Integer.parseInt(userId)
);
if (result.next()) {
String username = result.getString("username");
int playcount = result.getInt("playcount");
res.type("application/json");
return String.format(
"{\"username\": \"%s\", \"playcount\": %d}",
username, playcount
);
}
return notFound(res, shiina);
}
}
Database Methods¶
| Method | Purpose | Example |
|---|---|---|
shiina.mysql.Query(sql, params...) |
SELECT queries | Read data |
shiina.mysql.Exec(sql, params...) |
INSERT, UPDATE, DELETE | Modify data |
Security: Always Use Prepared Statements
✅ DO: shiina.mysql.Query("SELECT * FROM users WHERE id = ?", userId)
❌ DON'T: shiina.mysql.Query("SELECT * FROM users WHERE id = " + userId)
Prepared statements prevent SQL injection attacks!
:material-redirect: Redirects and Response Helpers¶
Shiina provides helper methods for common responses.
Redirect to Another Page¶
Show 404 Not Found¶
Custom Error Messages¶
Handling Form Data¶
Process POST requests with form data.
Example Form Handler¶
package com.example.plugin.routes;
import dev.osunolimits.modules.Shiina;
import dev.osunolimits.modules.ShiinaRoute;
import dev.osunolimits.modules.ShiinaRoute.ShiinaRequest;
import spark.Request;
import spark.Response;
public class SubmitFormRoute extends Shiina {
@Override
public Object handle(Request req, Response res) throws Exception {
ShiinaRequest shiina = new ShiinaRoute().handle(req, res);
// Get form data
String username = req.queryParams("username");
String email = req.queryParams("email");
// Validate input
if (username == null || username.isEmpty()) {
res.status(400);
res.type("application/json");
return "{\"error\": \"Username is required\"}";
}
// Process the data
shiina.mysql.Exec(
"INSERT INTO submissions (username, email) VALUES (?, ?)",
username, email
);
// Redirect to success page
return redirect(res, shiina, "/success");
}
}
Register as POST Route¶
Authentication in Routes¶
Check if a user is logged in and access their data.
Example with Authentication¶
package com.example.plugin.routes;
import dev.osunolimits.modules.Shiina;
import dev.osunolimits.modules.ShiinaRoute;
import dev.osunolimits.modules.ShiinaRoute.ShiinaRequest;
import spark.Request;
import spark.Response;
public class ProtectedRoute extends Shiina {
@Override
public Object handle(Request req, Response res) throws Exception {
ShiinaRequest shiina = new ShiinaRoute().handle(req, res);
// Check if user is logged in
if (shiina.loggedIn) {
// Access user data
int userId = shiina.user.id;
String username = shiina.user.name;
res.type("application/json");
return String.format(
"{\"message\": \"Hello, %s!\", \"id\": %d}",
username, userId
);
} else {
// Redirect to login page
return redirect(res, shiina, "/login");
}
}
}
ShiinaRequest Properties¶
| Property | Type | Description |
|---|---|---|
shiina.loggedIn |
boolean | Whether user is authenticated |
shiina.user.id |
int | User's database ID |
shiina.user.name |
String | Username |
shiina.user.country |
String | Country code |
shiina.mysql |
Object | Database connection |
JSON Response Builder¶
For complex JSON responses, use a JSON library or build programmatically.
Using Gson (Recommended)¶
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class JsonApiRoute extends Shiina {
@Override
public Object handle(Request req, Response res) throws Exception {
ShiinaRequest shiina = new ShiinaRoute().handle(req, res);
JsonObject response = new JsonObject();
response.addProperty("status", "success");
response.addProperty("timestamp", System.currentTimeMillis());
response.addProperty("user", shiina.user.name);
res.type("application/json");
return new Gson().toJson(response);
}
}
URL Parameters¶
Capture dynamic values from the URL.
Route with Parameters¶
// Register route with parameter
WebServer.get("/user/:id", new UserProfileRoute());
// In your route handler
public class UserProfileRoute extends Shiina {
@Override
public Object handle(Request req, Response res) throws Exception {
ShiinaRequest shiina = new ShiinaRoute().handle(req, res);
// Get parameter value
String userId = req.params(":id");
// Use the parameter...
return "User ID: " + userId;
}
}
Query String Parameters¶
// URL: /search?q=keyword&page=2
String query = req.queryParams("q"); // "keyword"
String page = req.queryParams("page"); // "2"
Best Practices¶
Route Development Tips
- ✅ Use descriptive route paths —
/api/user-statsnot/usr - ✅ Return appropriate HTTP status codes — 200, 404, 400, 500
- ✅ Validate all input data — Never trust user input
- ✅ Use prepared statements — Prevent SQL injection
- ✅ Handle errors gracefully — Use try-catch blocks
- ✅ Set correct content types —
application/jsonfor APIs - ✅ Log important events — Help with debugging
Common Mistakes
- ❌ Don't expose sensitive data in API responses
- ❌ Don't use string concatenation for SQL queries
- ❌ Don't forget to validate user permissions
- ❌ Don't return stack traces to users
- ❌ Don't ignore error handling
© 2026 Marc Andre Herpers. All rights reserved.