Running as a JDK http server
To expose endpoints using the
http server built into the JDK
com.sun.net.httpserver), first add the following dependency:
"com.softwaremill.sttp.tapir" %% "tapir-jdkhttp-server" % "1.9.10"
Then, import the package:
JdkHttpServer().addEndpoints to expose server endpoints.
These methods require a single, or a list of
ServerEndpoints, which can be created by adding server logic
to an endpoint. You can use shortcut extension methods exposed by the package to transform your
ServerEndpoints by calling
.handle method family on them (an equivalent to
.serverLogic family, but with the effect
type fixed, which simplifies type inference). The
handle naming convention was introduced to avoid conflicts with the
serverLogic methods and also because names are shorter.
val helloWorld = endpoint
.handle(name => Right(s"Hello, $name!"))
val server: HttpServer =
This server runs on a single worker thread by default. This is ok for testing, toy projects and things that never see any load.
If you want it to scale please read about the
executor configuration option below and set it accordingly.
com.sun.net.httpserver package is standardised and a part of public JDK API since JDK 18 (JEP 408) this server can be
The interpreter can be configured by providing an
JdkHttpServerOptions value, see server options for
Most options can be configured directly using a
JdkHttpServer instance, such as the host and port. Possible options are:
send404WhenRequestNotHandled: Should a 404 response be sent, when the request hasn’t been handled by defined endpoints. This is a safe default, but if there are multiple handlers for the same context path, this should be set to
false. In that case, you can verify if the request has been handled using
basePath: Path under which endpoints will be mounted when mounted on JdkHttpServer instance. I.e.: basePath of
/hellowill result with a real path of
port: IP port to which JdkHttpServer instance will be bound. Default is
0, which means any random port provided by the OS.
host: Hostname or IP address (ie.:
0.0.0.0) to which JdkHttpServer instance will be bound. Default is
0.0.0.0which binds the server to all network interfaces available on the OS.
executor: Allows you to configure the
Executorwhich will be used to handle HTTP requests. By default
com.sun.net.httpserver.HttpServeruses a single thread (a calling thread executor to be precise) executor to handle traffic which might be fine for local toy projects.
If you intend to use this HTTP server for any deployments that will run under load it’s absolutely necessary to set an executor that will use proper thread pool to handle the load. Recommended approach is to use
JdkHttpServerOptions.httpExecutormethod to create a ThreadPoolExecutor that will scale under load. You can also use an Executor returned from any of the constructors in the
Alternatively, if running with a JDK 19+ you can leverage Project Loom and use
Executors.newVirtualThreadPerTaskExecutor()to run each request on a virtual thread. This however means it is possible for your server to be overloaded with work as each request will be given a thread with no backpressure on how many should be executed in parallel.
httpsConfigurator: Optional HTTPS configuration. Takes an instance of
com.sun.net.httpserver.HttpsConfigurator, which is a thin wrapper around
javax.net.ssl.SSLContextto configure the SSL termination for this server.
backlogSize: Sets the size of server’s tcp connection backlog. This is the maximum number of queued incoming connections to allow on the listening socket. Queued TCP connections exceeding this limit may be rejected by the TCP implementation. If set to 0 or less the system default for backlog size will be used. Default is 0.