Fork me on GitHub


Documentation here is always for the latest version of Spark. We don’t have the capacity to maintain separate docs for each version, but Spark is always backwards compatible.

Docs for (spark-kotlin) will arrive here ASAP. You can follow the progress of spark-kotlin on (GitHub)

If you like spark-kotlin star us and help the community grow:

Getting started

1: Create a new maven project and add the dependency to your POM.xml:





Not familiar with Maven? Click here for more detailed instructions.

Other dependency managers:

Gradle : compile "com.sparkjava:spark-core:2.6.0" // add to build.gradle (for Java users)
Gradle : compile "com.sparkjava:spark-kotlin:1.0.0-alpha" // add to build.gradle (for Kotlin users)
   Ivy : <dependency org="com.sparkjava" name="spark-core" rev="2.6.0" conf="build" /> // ivy.xml
   SBT : libraryDependencies += "com.sparkjava" % "spark-core" % "2.6.0" // build.sbt

2: Start coding:

import static spark.Spark.*;

public class HelloWorld {
    public static void main(String[] args) {
        get("/hello", (req, res) -> "Hello World");

3: Run and view:


To see more console output from Spark (debug info, etc), you have to add a logger to your project.

Stopping the Server

By calling the stop() method the server is stopped and all routes are cleared.


Wait, what about starting the server?

The server is automatically started when you do something that requires the server to be started (i.e. declaring a route or setting the port).
You can also manually start the server by calling init().

You can specify what should happen if initialization fails:

initExceptionHandler((e) -> System.out.println("Uh-oh"));

The default behaviour is to log and shut down:

private Consumer<Exception> initExceptionHandler = (e) -> {
    LOG.error("ignite failed", e);


The main building block of a Spark application is a set of routes. A route is made up of three simple pieces:

Routes are matched in the order they are defined. The first route that matches the request is invoked.
Always statically import Spark methods to ensure good readability:

get("/", (request, response) -> {
    // Show something

post("/", (request, response) -> {
    // Create something

put("/", (request, response) -> {
    // Update something

delete("/", (request, response) -> {
    // Annihilate something

options("/", (request, response) -> {
    // Appease something

Route patterns can include named parameters, accessible via the params() method on the request object:

// matches "GET /hello/foo" and "GET /hello/bar"
// request.params(":name") is 'foo' or 'bar'
get("/hello/:name", (request, response) -> {
    return "Hello: " + request.params(":name");

Route patterns can also include splat (or wildcard) parameters. These parameters can be accessed by using the splat() method on the request object:

// matches "GET /say/hello/to/world"
// request.splat()[0] is 'hello' and request.splat()[1] 'world'
get("/say/*/to/*", (request, response) -> {
    return "Number of splat parameters: " + request.splat().length;

Path groups

If you have a lot of routes, it can be helpful to separate them into groups. This can be done by calling the path() method, which takes a String prefix and gives you a scope to declare routes and filters (or nested paths) in:

path("/api", () -> {
    before("/*", (q, a) ->"Received api call"));
    path("/email", () -> {
        post("/add",       EmailApi.addEmail);
        put("/change",     EmailApi.changeEmail);
        delete("/remove",  EmailApi.deleteEmail);
    path("/username", () -> {
        post("/add",       UserApi.addUsername);
        put("/change",     UserApi.changeUsername);
        delete("/remove",  UserApi.deleteUsername);


Request information and functionality is provided by the request parameter:

request.attributes();             // the attributes list
request.attribute("foo");         // value of foo attribute
request.attribute("A", "V");      // sets value of attribute A to V
request.body();                   // request body sent by the client
request.bodyAsBytes();            // request body as bytes
request.contentLength();          // length of request body
request.contentType();            // content type of request.body
request.contextPath();            // the context path, e.g. "/hello"
request.cookies();                // request cookies sent by the client
request.headers();                // the HTTP header list
request.headers("BAR");           // value of BAR header;                   // the host, e.g. ""
request.ip();                     // client IP address
request.params("foo");            // value of foo path parameter
request.params();                 // map with all parameters
request.pathInfo();               // the path info
request.port();                   // the server port
request.protocol();               // the protocol, e.g. HTTP/1.1
request.queryMap();               // the query map
request.queryMap("foo");          // query map for a certain parameter
request.queryParams();            // the query param list
request.queryParams("FOO");       // value of FOO query param
request.queryParamsValues("FOO")  // all values of FOO query param
request.raw();                    // raw request handed in by Jetty
request.requestMethod();          // The HTTP method (GET, ..etc)
request.scheme();                 // "http"
request.servletPath();            // the servlet path, e.g. /result.jsp
request.session();                // session management
request.splat();                  // splat (*) parameters
request.uri();                    // the uri, e.g. ""
request.url();                    // the url. e.g. ""
request.userAgent();              // user agent


Response information and functionality is provided by the response parameter:

response.body();               // get response content
response.body("Hello");        // sets content to Hello
response.header("FOO", "bar"); // sets header FOO with value bar
response.raw();                // raw response handed in by Jetty
response.redirect("/example"); // browser redirect to /example
response.status();             // get the response status
response.status(401);          // set status code to 401
response.type();               // get the content type
response.type("text/xml");     // set content type to text/xml

Query Maps

Query maps allows you to group parameters to a map by their prefix. This allows you to group two parameters like user[name] and user[age] to a user map.

request.queryMap().get("user", "name").value();


request.cookies();                         // get map of all request cookies
request.cookie("foo");                     // access request cookie by name
response.cookie("foo", "bar");             // set cookie with a value
response.cookie("foo", "bar", 3600);       // set cookie with a max-age
response.cookie("foo", "bar", 3600, true); // secure cookie
response.removeCookie("foo");              // remove cookie


Every request has access to the session created on the server side, provided with the following methods:

request.session(true);                     // create and return session
request.session().attribute("user");       // Get session attribute 'user'
request.session().attribute("user","foo"); // Set session attribute 'user'
request.session().removeAttribute("user"); // Remove session attribute 'user'
request.session().attributes();            // Get all session attributes
request.session().id();                    // Get session id
request.session().isNew();                 // Check if session is new
request.session().raw();                   // Return servlet object


To immediately stop a request within a filter or route use halt():

halt();                // halt 
halt(401);             // halt with status
halt("Body Message");  // halt with message
halt(401, "Go away!"); // halt with status and message

halt() is not intended to be used inside exception-mappers.


Before-filters are evaluated before each request, and can read the request and read/modify the response.
To stop execution, use halt():

before((request, response) -> {
    boolean authenticated;
    // ... check if authenticated
    if (!authenticated) {
        halt(401, "You are not welcome here");

After-filters are evaluated after each request, and can read the request and read/modify the response:

after((request, response) -> {
    response.header("foo", "set by after filter");

After-after-filters are evaluated after after-filters. Think of it as a “finally” block.

afterAfter((request, response) -> {
    response.header("foo", "set by afterAfter filter");

Filters optionally take a pattern, causing them to be evaluated only if the request path matches that pattern:

before("/protected/*", (request, response) -> {
    // ... check if authenticated
    halt(401, "Go Away!");


You can trigger a browser redirect with the redirect method on the response:


You can also trigger a browser redirect with specific HTTP 3XX status code:

response.redirect("/bar", 301); // moved permanently

Redirect API

There is also a convenience API for redirects which can be used directly without the response:

// redirect a GET to "/fromPath" to "/toPath"
redirect.get("/fromPath", "/toPath");

// redirect a POST to "/fromPath" to "/toPath", with status 303"/fromPath", "/toPath", Redirect.Status.SEE_OTHER);

// redirect any request to "/fromPath" to "/toPath" with status 301
redirect.any("/fromPath", "/toPath", Redirect.Status.MOVED_PERMANENTLY);

Remember to import Spark statically instead of prefixing it as Spark.redirect

Custom error handling

Not found (code 404) handling

// Using string/html
notFound("<html><body><h1>Custom 404 handling</h1></body></html>");
// Using Route
notFound((req, res) -> {
    return "{\"message\":\"Custom 404\"}";

Internal server error (code 500) handling

// Using string/html
internalServerError("<html><body><h1>Custom 500 handling</h1></body></html>");
// Using Route
internalServerError((req, res) -> {
    return "{\"message\":\"Custom 500 handling\"}";

Exception Mapping

To handle exceptions of a configured type for all routes and filters:

get("/throwexception", (request, response) -> {
    throw new YourCustomException();

exception(YourCustomException.class, (exception, request, response) -> {
    // Handle the exception here

Static Files

You can assign a folder in the classpath serving static files with the staticFiles.location() method. Note that the public directory name is not included in the URL.
A file /public/css/style.css is made available as http://{host}:{port}/css/style.css

// root is 'src/main/resources', so put files in 'src/main/resources/public'
staticFiles.location("/public"); // Static files

You can also assign an external folder (a folder not in the classpath) to serve static files by using the staticFiles.externalLocation() method.\


Static files location must be configured before route mapping. If your application has no routes, init() must be called manually after location is set.

Cache/Expire time

You can specify the expire time (in seconds). By default there is no caching.

staticFiles.expireTime(600); // ten minutes

Setting custom headers

staticFiles.header("Key-1", "Value-1");
staticFiles.header("Key-1", "New-Value-1"); // Using the same key will overwrite value
staticFiles.header("Key-2", "Value-2");
staticFiles.header("Key-3", "Value-3");


Mapped routes that transform the output from the handle method. This is done by extending the ResponseTransformer object and passing it to the mapping method. Example of a route transforming output to JSON using Gson:


public class JsonTransformer implements ResponseTransformer {

    private Gson gson = new Gson();

    public String render(Object model) {
        return gson.toJson(model);


and how it is used (MyMessage is a bean with one member ‘message’):

get("/hello", "application/json", (request, response) -> {
    return new MyMessage("Hello World");
}, new JsonTransformer());

You can also use Java 8 method references, since ResponseTransformer is an interface with one method:

Gson gson = new Gson();
get("/hello", (request, response) -> new MyMessage("Hello World"), gson::toJson);

Views and Templates

Spark has community-provided wrappers for a lot of popular template engines:

There are two main ways of rendering a template in Spark. You can either call render directly in a standard route declaration (recommended), or you can provide the template-engine as a third-route parameter (likely to be removed in the future):

// do this
get("template-example", (req, res) -> {
    Map<String, Object> model = new HashMap<>();
    return new VelocityTemplateEngine().render(
        new ModelAndView(model, "path-to-template")
// don't do this
get("template-example", (req, res) -> {
    Map<String, Object> model = new HashMap<>();
    return new ModelAndView(model, "path-to-template");
}, new VelocityTemplateEngine());

It can be helpful to create a static utility method for rendering:

get("template-example", (req, res) -> {
    Map<String, Object> model = new HashMap<>();
    return render(model, "path-to-template");

// declare this in a util-class
public static String render(Map<String, Object> model, String templatePath) {
    return new VelocityTemplateEngine().render(new ModelAndView(model, templatePath));


Renders HTML using the Velocity template engine. Source and example on GitHub.



Renders HTML using the Freemarker template engine. Source and example on GitHub.



Renders HTML using the Mustache template engine. Source and example on GitHub.



Renders HTML using the Handlebars template engine. Source and example on GitHub.



Renders HTML using the Jade template engine. Source and example on GitHub.



Renders HTML using the Thymeleaf template engine. Source and example on GitHub.



Renders HTML using the Pebble template engine. Source and example on GitHub.



Renders HTML using the Water template engine. Source and example on GitHub.



Renders HTML using the Jtwig template engine. Source and example on GitHub.



Renders HTML using the Jinjava template engine. Source and example on GitHub.



Renders HTML using the Jetbrick template engine. Source and example on GitHub.


Embedded web server

Standalone Spark runs on an embedded Jetty web server.


By default, Spark runs on port 4567. If you want to set another port, use port(). This has to be done before declaring routes and filters:

port(8080); // Spark will run on port 8080

Secure (HTTPS/SSL)

You can set the connection to be secure via the secure() method.
This has to be done before any route mapping:

secure(keystoreFilePath, keystorePassword, truststoreFilePath, truststorePassword);

If you need more help, check out the FAQ.


You can set the maximum number of threads easily:

int maxThreads = 8;

You can also configure the minimum numbers of threads, and the idle timeout:

int maxThreads = 8;
int minThreads = 2;
int timeOutMillis = 30000;
threadPool(maxThreads, minThreads, timeOutMillis);

Waiting for Initialization

You can use the method awaitInitialization() to check if the server is ready to handle requests. This is usually done in a separate thread, for example to run a health check module after your server has started.
The method causes the current thread to wait until the embedded Jetty server has been initialized. Initialization is triggered by defining routes and/or filters. So, if you’re using just one thread don’t put this before you define your routes and/or filters.

awaitInitialization(); // Wait for server to be initialized


WebSockets provide a protocol full-duplex communication channel over a single TCP connection, meaning you can send message back and forth over the same connection.

WebSockets only works with the embedded Jetty server, and must be defined before regular HTTP routes. To create a WebSocket route, you need to provide a path and a handler class:

webSocket("/echo", EchoWebSocket.class);
init(); // Needed if you don't define any HTTP routes after your WebSocket routes
import org.eclipse.jetty.websocket.api.*;
import org.eclipse.jetty.websocket.api.annotations.*;
import java.util.*;
import java.util.concurrent.*;

public class EchoWebSocket {

    // Store sessions if you want to, for example, broadcast a message to all users
    private static final Queue<Session> sessions = new ConcurrentLinkedQueue<>();

    public void connected(Session session) {

    public void closed(Session session, int statusCode, String reason) {

    public void message(Session session, String message) throws IOException {
        System.out.println("Got: " + message);   // Print message
        session.getRemote().sendString(message); // and send it back


Other web server

To run Spark on another web server (instead of the embedded jetty server), an implementation of the interface spark.servlet.SparkApplication is needed. You have to initialize your routes in the init() method, and the following filter might have to be configured in your web.xml:



GZIP is done automatically if it’s in both the request and the response headers. This usually only means that you have to set it in your response headers.

If you want to GZIP a single response, you can add it manually to your route:

get("/some-path", (request, response) -> {
    // code for your get
    response.header("Content-Encoding", "gzip");

If you want to GZIP everything, you can use an after-filter

after((request, response) -> {
    response.header("Content-Encoding", "gzip");


Javadoc is available at

Build it yourself

After getting the source from GitHub run:

mvn javadoc:javadoc

The result is put in /target/site/apidocs

Examples and FAQ

Examples can be found on the project’s page on GitHub.

How do I upload something?

Note: This applies to the standard configuration of Spark (embedded jetty). If you’re using Spark with some other webserver, this might not apply to you.

To upload a file you need a form and a post handler. First, create a form with the correct enctype, and an input field with the type “file” and a name of your choice (here “upoaded_file”):

<form method='post' enctype='multipart/form-data'>
    <input type='file' name='uploaded_file'>
    <button>Upload picture</button>"

For Spark to be able to extract the uploaded file, you have to set a specific request attribute, which allows to use the getPart() method on the raw request:

post("/yourUploadPath", (request, response) -> {
    request.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/temp"));
    try (InputStream is = request.raw().getPart("uploaded_file").getInputStream()) {
        // Use the input stream to create a file
    return "File uploaded";

The Java-IO-stuff is left out as it’s not Spark-specific, but you can see a fully working example here.

How do I enable SSL/HTTPS?

Enabling HTTPS/SSL requires you to have a keystore file, which you can generate using the Java keytool (→ oracle docs). Once you have the keystore file, just point to its location and include its password.

String keyStoreLocation = "deploy/keystore.jks";
String keyStorePassword = "password";
secure(keyStoreLocation, keyStorePassword, null, null);

Check out the fully working example on GitHub if you need more guidance.

How do I enable logging?

You might have seen this message when starting Spark:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See for further details.

To enable logging, just add the following dependency to your project:


How do I enable automatic refresh of static files?

If you use staticFiles.location(), meaning you keep your static files in the classpath, static resources are copied to a target folder when you build your application. This means you have to make/build your project in order to refresh static files. A workaround for this is to tell Spark to read static files from the absolute path to the src-directory. If you do this you will see changes instantly when you refresh, but if you build a jar file it will only work on your computer (because of the absolute path). So, only use this during development.

if (localhost) {
    String projectDir = System.getProperty("user.dir");
    String staticDir = "/src/main/resources/public";
    staticFiles.externalLocation(projectDir + staticDir);
} else {