Quarkus
A supersonic subatomic Java framework for cloud-native development. Features fast startup times and native image optimization.
GitHub Overview
quarkusio/quarkus
Quarkus: Supersonic Subatomic Java.
Topics
Star History
Framework
Quarkus
Overview
Quarkus is a Kubernetes-native Java framework designed for high-speed startup and low memory consumption in microservices development platforms.
Details
Quarkus is a cloud-native, microservices-oriented Java framework developed by Red Hat. Since its release in 2019, it has been committed to fundamentally solving the traditional Java application challenges of long startup times and high memory consumption under the slogan "Supersonic Subatomic Java".
The key feature is GraalVM Native Image and compile-time optimization for ultra-fast startup and ultra-low memory consumption. While traditional JVM applications require several seconds to tens of seconds for startup, Quarkus native applications can achieve startup in under 100ms, realizing over 90% startup time reduction compared to Spring Boot. Memory usage also achieves 75% reduction, enabling operation from a minimum of 64MB.
Focusing on Developer Joy (improving developer experience), the live reload feature allows code changes to be reflected immediately, significantly improving development efficiency. Additionally, compile-time dependency injection and AOT (Ahead-of-Time) compilation execute many processes at build time rather than runtime, minimizing runtime overhead.
It's optimized for serverless environments (AWS Lambda, Azure Functions, etc.) and container environments (Kubernetes, Docker), solving cold start problems and enabling true cloud-native Java application development. Currently providing over 300 extensions, it also offers a rich API set to support migration from Spring developers.
Pros and Cons
Pros
- Ultra-fast startup: Achieves application startup in under 100ms
- Ultra-low memory consumption: Operates from minimum 64MB with 75% reduction
- Excellent throughput: High performance during native execution
- Kubernetes optimization: Outstanding performance in cloud-native environments
- Serverless support: Fast cold start in Lambda and similar platforms
- Developer Joy: High development efficiency through live reload feature
- GraalVM integration: Optimization through native compilation
- Rich extensions: Over 300 functional extensions
Cons
- Learning cost: Requires mastering new concepts and Quarkus-specific mechanisms
- Build time: Native compilation takes considerable time
- Ecosystem: Limited third-party libraries compared to Spring Boot
- Debugging difficulty: Complex debugging during native execution
- Reflection limitations: Constraints on reflection usage in GraalVM
- Maturity: Limited cases as a relatively new framework
- Runtime constraints: Functional limitations during native execution
Key Links
- Quarkus Official Site
- Quarkus Documentation
- Quarkus GitHub
- Quarkus Guides
- Start Coding
- Quarkus Extensions
Code Examples
Hello World (Basic Web Application)
package org.acme;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello from Quarkus!";
}
@GET
@Path("/status")
@Produces(MediaType.TEXT_PLAIN)
public String status() {
return "Service is running with Quarkus";
}
}
// Configuration in application.properties
// quarkus.http.port=8080
// quarkus.application.name=quarkus-demo
REST API Development (Using CDI)
package org.acme.model;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;
import java.util.List;
@Entity
public class Product extends PanacheEntity {
public String name;
public String description;
public Double price;
public Integer quantity;
// Panache Entity pattern for concise implementation
public static Product findByName(String name) {
return find("name", name).firstResult();
}
public static List<Product> findByPriceRange(Double minPrice, Double maxPrice) {
return find("price >= ?1 and price <= ?2", minPrice, maxPrice).list();
}
}
// REST Resource
package org.acme.resource;
import org.acme.model.Product;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.transaction.Transactional;
import java.util.List;
@Path("/api/products")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ProductResource {
@GET
public List<Product> getAllProducts() {
return Product.listAll();
}
@GET
@Path("/{id}")
public Product getProduct(@PathParam("id") Long id) {
Product product = Product.findById(id);
if (product == null) {
throw new WebApplicationException("Product not found", 404);
}
return product;
}
@POST
@Transactional
public Response createProduct(Product product) {
if (product.name == null || product.name.trim().isEmpty()) {
return Response.status(400).entity("Product name is required").build();
}
product.persist();
return Response.status(201).entity(product).build();
}
@PUT
@Path("/{id}")
@Transactional
public Product updateProduct(@PathParam("id") Long id, Product updatedProduct) {
Product product = Product.findById(id);
if (product == null) {
throw new WebApplicationException("Product not found", 404);
}
product.name = updatedProduct.name;
product.description = updatedProduct.description;
product.price = updatedProduct.price;
product.quantity = updatedProduct.quantity;
return product;
}
@DELETE
@Path("/{id}")
@Transactional
public Response deleteProduct(@PathParam("id") Long id) {
Product product = Product.findById(id);
if (product == null) {
throw new WebApplicationException("Product not found", 404);
}
product.delete();
return Response.noContent().build();
}
@GET
@Path("/search")
public List<Product> searchByPriceRange(
@QueryParam("min") Double minPrice,
@QueryParam("max") Double maxPrice) {
return Product.findByPriceRange(minPrice, maxPrice);
}
}
RESTful API Development
// User.java - Data model
package org.acme.model;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
@Entity
@Table(name = "users")
public class User extends PanacheEntity {
@NotBlank(message = "Name is required")
public String name;
@Email(message = "Please provide a valid email address")
@NotBlank(message = "Email is required")
public String email;
public Integer age;
// Leverage Panache pattern with static methods
public static User findByEmail(String email) {
return find("email", email).firstResult();
}
public static List<User> findByAgeRange(int minAge, int maxAge) {
return find("age >= ?1 and age <= ?2", minAge, maxAge).list();
}
}
// UserResource.java - REST controller
package org.acme.resource;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.acme.model.User;
import java.util.List;
@Path("/api/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
@GET
public List<User> getAllUsers() {
return User.listAll();
}
@GET
@Path("/{id}")
public Response getUser(@PathParam("id") Long id) {
User user = User.findById(id);
if (user == null) {
return Response.status(Response.Status.NOT_FOUND)
.entity("{\"error\": \"User not found\"}")
.build();
}
return Response.ok(user).build();
}
@POST
@Transactional
public Response createUser(@Valid User user) {
// Check email duplication
if (User.findByEmail(user.email) != null) {
return Response.status(Response.Status.CONFLICT)
.entity("{\"error\": \"Email already exists\"}")
.build();
}
user.persist();
return Response.status(Response.Status.CREATED).entity(user).build();
}
@PUT
@Path("/{id}")
@Transactional
public Response updateUser(@PathParam("id") Long id, @Valid User updatedUser) {
User user = User.findById(id);
if (user == null) {
return Response.status(Response.Status.NOT_FOUND)
.entity("{\"error\": \"User not found\"}")
.build();
}
user.name = updatedUser.name;
user.email = updatedUser.email;
user.age = updatedUser.age;
return Response.ok(user).build();
}
@DELETE
@Path("/{id}")
@Transactional
public Response deleteUser(@PathParam("id") Long id) {
User user = User.findById(id);
if (user == null) {
return Response.status(Response.Status.NOT_FOUND)
.entity("{\"error\": \"User not found\"}")
.build();
}
user.delete();
return Response.noContent().build();
}
@GET
@Path("/search")
public List<User> searchByAge(@QueryParam("minAge") int minAge,
@QueryParam("maxAge") int maxAge) {
return User.findByAgeRange(minAge, maxAge);
}
}
Database Configuration and Hibernate ORM
# application.properties - Database configuration
# Development H2 database
%dev.quarkus.datasource.db-kind=h2
%dev.quarkus.datasource.username=sa
%dev.quarkus.datasource.password=
%dev.quarkus.datasource.jdbc.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
%dev.quarkus.hibernate-orm.database.generation=drop-and-create
%dev.quarkus.hibernate-orm.log.sql=true
# Production PostgreSQL
%prod.quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.username=${DATABASE_USER:admin}
%prod.quarkus.datasource.password=${DATABASE_PASSWORD:password}
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://${DATABASE_HOST:localhost}:5432/${DATABASE_NAME:mydb}
%prod.quarkus.hibernate-orm.database.generation=validate
# Quarkus configuration
quarkus.http.port=8080
quarkus.log.level=INFO
quarkus.banner.enabled=false
Reactive Programming
// ReactiveUserResource.java - Reactive API
package org.acme.resource;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.pgclient.PgPool;
import io.vertx.mutiny.sqlclient.Row;
import io.vertx.mutiny.sqlclient.RowSet;
import io.vertx.mutiny.sqlclient.Tuple;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.acme.model.User;
import java.util.List;
import java.util.stream.StreamSupport;
@Path("/api/reactive/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReactiveUserResource {
@Inject
PgPool client;
@GET
public Uni<List<User>> getAllUsers() {
return client.query("SELECT id, name, email, age FROM users")
.execute()
.onItem().transform(rows -> {
return StreamSupport.stream(rows.spliterator(), false)
.map(this::mapRowToUser)
.toList();
});
}
@GET
@Path("/{id}")
public Uni<Response> getUser(@PathParam("id") Long id) {
return client.preparedQuery("SELECT id, name, email, age FROM users WHERE id = $1")
.execute(Tuple.of(id))
.onItem().transform(rows -> {
if (!rows.iterator().hasNext()) {
return Response.status(Response.Status.NOT_FOUND).build();
}
User user = mapRowToUser(rows.iterator().next());
return Response.ok(user).build();
});
}
@POST
public Uni<Response> createUser(User user) {
return client.preparedQuery(
"INSERT INTO users (name, email, age) VALUES ($1, $2, $3) RETURNING id")
.execute(Tuple.of(user.name, user.email, user.age))
.onItem().transform(rows -> {
Long id = rows.iterator().next().getLong("id");
user.id = id;
return Response.status(Response.Status.CREATED).entity(user).build();
});
}
@PUT
@Path("/{id}")
public Uni<Response> updateUser(@PathParam("id") Long id, User user) {
return client.preparedQuery(
"UPDATE users SET name = $1, email = $2, age = $3 WHERE id = $4")
.execute(Tuple.of(user.name, user.email, user.age, id))
.onItem().transform(result -> {
if (result.rowCount() == 0) {
return Response.status(Response.Status.NOT_FOUND).build();
}
user.id = id;
return Response.ok(user).build();
});
}
@DELETE
@Path("/{id}")
public Uni<Response> deleteUser(@PathParam("id") Long id) {
return client.preparedQuery("DELETE FROM users WHERE id = $1")
.execute(Tuple.of(id))
.onItem().transform(result -> {
if (result.rowCount() == 0) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.noContent().build();
});
}
private User mapRowToUser(Row row) {
User user = new User();
user.id = row.getLong("id");
user.name = row.getString("name");
user.email = row.getString("email");
user.age = row.getInteger("age");
return user;
}
}
Security Configuration (JWT Authentication)
// TokenService.java - JWT token management
package org.acme.security;
import io.smallrye.jwt.build.Jwt;
import io.smallrye.jwt.build.JwtClaimsBuilder;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.jwt.JsonWebToken;
import jakarta.inject.Inject;
import java.time.Duration;
import java.util.Set;
@ApplicationScoped
public class TokenService {
public String generateToken(String username, Set<String> roles) {
return Jwt.issuer("https://example.com/issuer")
.upn(username)
.groups(roles)
.expiresAt(System.currentTimeMillis() + Duration.ofHours(1).toMillis())
.sign();
}
}
// AuthResource.java - Authentication endpoint
package org.acme.resource;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.acme.security.TokenService;
import java.util.Set;
@Path("/auth")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class AuthResource {
@Inject
TokenService tokenService;
@POST
@Path("/login")
public Response login(LoginRequest request) {
// Simple authentication logic (implement proper authentication in production)
if ("admin".equals(request.username) && "password".equals(request.password)) {
String token = tokenService.generateToken(request.username, Set.of("admin", "user"));
return Response.ok(new TokenResponse(token)).build();
}
return Response.status(Response.Status.UNAUTHORIZED)
.entity("{\"error\": \"Authentication failed\"}")
.build();
}
public static class LoginRequest {
public String username;
public String password;
}
public static class TokenResponse {
public String token;
public TokenResponse(String token) {
this.token = token;
}
}
}
// SecuredResource.java - Protected resource
package org.acme.resource;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext;
import org.eclipse.microprofile.jwt.JsonWebToken;
@Path("/secure")
@Produces(MediaType.APPLICATION_JSON)
public class SecuredResource {
@Inject
JsonWebToken jwt;
@GET
@Path("/user")
@RolesAllowed("user")
public String userEndpoint(@Context SecurityContext ctx) {
return String.format(
"Hello %s, your roles: %s",
jwt.getName(),
jwt.getGroups()
);
}
@GET
@Path("/admin")
@RolesAllowed("admin")
public String adminEndpoint() {
return "Admin-only data: " + jwt.getName();
}
}
# JWT configuration in application.properties
mp.jwt.verify.publickey.location=META-INF/resources/publicKey.pem
mp.jwt.verify.issuer=https://example.com/issuer
smallrye.jwt.sign.key.location=META-INF/resources/privateKey.pem
Testing
// UserResourceTest.java - Integration test
package org.acme.resource;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
@QuarkusTest
public class UserResourceTest {
@Test
public void testGetAllUsers() {
given()
.when().get("/api/users")
.then()
.statusCode(200)
.contentType(ContentType.JSON);
}
@Test
public void testCreateUser() {
given()
.contentType(ContentType.JSON)
.body("{\"name\": \"John Doe\", \"email\": \"[email protected]\", \"age\": 25}")
.when().post("/api/users")
.then()
.statusCode(201)
.body("name", is("John Doe"))
.body("email", is("[email protected]"));
}
@Test
public void testGetUserNotFound() {
given()
.when().get("/api/users/999")
.then()
.statusCode(404)
.body(containsString("User not found"));
}
@Test
public void testCreateUserInvalidData() {
given()
.contentType(ContentType.JSON)
.body("{\"name\": \"\", \"email\": \"invalid-email\"}")
.when().post("/api/users")
.then()
.statusCode(400);
}
}