A resume project proves one thing: that you can build something real, end to end. This page lists 10 Spring Boot project ideas, each with a minimum scope you can finish in a weekend or two, a stretch goal that makes it stand out, the data model, and the existing guides on this site that cover the hard parts. Three ideas include the small algorithm at their core, as real code with real output.
| # | Project | Level | Shows |
|---|---|---|---|
| 1 | URL shortener with analytics | Beginner | REST, caching, redirects |
| 2 | Expense splitter | Beginner to intermediate | Money, transactions, algorithm |
| 3 | Task board with JWT | Intermediate | Security, ownership |
| 4 | Order service with outbox | Advanced | Events, idempotency |
| 5 | Document vault | Intermediate | Streaming, storage |
| 6 | Rate limiter / gateway | Intermediate | Algorithms, filters |
| 7 | Real-time chat | Intermediate | WebSocket, STOMP |
| 8 | Notification service | Intermediate | Queues, retries |
| 9 | Document Q&A (RAG) | Intermediate to advanced | Spring AI, vectors |
| 10 | Job tracker with batch import | Intermediate | Spring Batch, reports |
Tested on OpenJDK 21.0.12. The three algorithms (ideas 1, 2 and 6) are in ProjectIdeasCores.java and every output line below is from running it. The rest of each idea is a design, not a delivered project: the linked guides are tested on Spring Boot 4.1, but nobody has built these ten projects end to end for this page. The resume-line templates have placeholders in brackets: put in numbers you measured, never numbers you hope for.
How to pick one
Pick the project that matches the job you want, not the one that sounds biggest. Applying for a backend role? Choose 3, 4 or 8: security and messaging are what interviewers probe. Aiming at data or AI? Choose 9 or 10. New to Spring? Start with 1 or 2, finish it, then do a harder one. One finished project with tests, a README and a running deployment beats three half-built ones.
What makes a project resume-worthy
| Do this | Why it matters |
|---|---|
| Write tests that fail when the code is wrong | Shows you know the behaviour, not only the happy path. See Testing Spring Boot 4 Without Containers: WireMock, MockWebServer and @MockitoBean |
| Ship a README with run steps and an API example | The reviewer decides in two minutes |
| Run it with Docker Compose | One command to start it; see Spring Boot Docker Compose Support: Real Containers for Local Development |
| Add health and metrics | Shows production thinking: Spring Boot Actuator in Production: Every Endpoint, Securing It, and Custom Health Indicators |
| Measure one thing and report it honestly | “Handles N requests per second on my laptop with tool X” is credible; “highly scalable” is not |
| Handle errors with a consistent format | See Global Exception Handling with ProblemDetail (RFC 9457) in Spring Boot 4 |
The ten ideas
1. URL shortener with click analytics
| Level | Beginner |
| What it proves | REST design, a database index, caching, redirects (HTTP 301 vs 302) |
| Stack | Spring Boot, Spring Data JPA, PostgreSQL or MySQL, Redis |
MVP:
POST /linkscreates a short codeGET /{code}redirects- Reject invalid URLs with validation
Stretch:
- Redis cache in front of the lookup
- Click counts per link and per day
- Expiry date and custom aliases
Core data: links(id, code, target_url, created_at, expires_at) and clicks(link_id, at). Derive code from the numeric id, as below.
The one clever part is the code. Encode the database id in base 62 (digits, lower and upper case). The program ran these values:
static final String ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
static String encode(long id) {
if (id == 0) return "0";
StringBuilder sb = new StringBuilder();
for (long n = id; n > 0; n /= 62) sb.append(ALPHABET.charAt((int) (n % 62)));
return sb.reverse().toString();
}
static long decode(String code) {
long n = 0;
for (char c : code.toCharArray()) n = n * 62 + ALPHABET.indexOf(c);
return n;
}-- 1. URL shortener: base62 ids --
0 -> 0 -> 0
61 -> Z -> 61
62 -> 10 -> 62
125 -> 21 -> 125
1000000 -> 4c92 -> 1000000
3521614606207 -> ZZZZZZZ -> 3521614606207
7 characters cover 62^7 = 3521614606208 idsSeven characters cover 627 = 3,521,614,606,208 ids. Source: ProjectIdeasCores.java, section base62.
Build it with these guides:
- Redis with Spring Boot 4.1: RedisTemplate vs StringRedisTemplate, @RedisHash, Pub/Sub and TTL
- The Spring Cache Abstraction: @Cacheable, @CacheEvict, Key Generators and the Self-Invocation Trap
- springdoc-openapi (document the API)
- Spring Boot Actuator in Production: Every Endpoint, Securing It, and Custom Health Indicators
Resume line template: Built a URL shortener on Spring Boot 4 with Base62 codes, Redis caching and click analytics; handled [N] requests per second in a local load test of [tool].
Trap: Using random codes and checking for collisions on every insert. Encoding the database id has no collisions by construction.
2. Expense splitter (Splitwise-style)
| Level | Beginner to intermediate |
| What it proves | Money handling, transactions, an algorithm that matters |
| Stack | Spring Boot, Spring Data JDBC or JPA, PostgreSQL |
MVP:
- Groups, members and expenses
- Compute each member’s balance
- Return the settle-up transfers
Stretch:
- Unequal and percentage splits
- Currency conversion
- Export as CSV or PDF
Core data: group, member, expense(payer, amount_minor_units), share(expense, member, amount). Store money as integer minor units, never double.
The interesting part is turning balances into the fewest payments. A greedy match of the largest creditor with the largest debtor is simple and good in practice (finding the true minimum is a hard problem in general):
record Transfer(String from, String to, long amount) {}
/** Turns "who paid what" balances into the fewest transfers the greedy method finds. */
static List<Transfer> settle(Map<String, Long> balance) { // positive = is owed money, negative = owes money
PriorityQueue<Map.Entry<String, Long>> creditors = new PriorityQueue<>((x, y) -> Long.compare(y.getValue(), x.getValue()));
PriorityQueue<Map.Entry<String, Long>> debtors = new PriorityQueue<>(Comparator.comparingLong(Map.Entry::getValue));
for (var e : balance.entrySet()) {
if (e.getValue() > 0) creditors.add(new AbstractMap.SimpleEntry<>(e));
else if (e.getValue() < 0) debtors.add(new AbstractMap.SimpleEntry<>(e));
}
List<Transfer> out = new ArrayList<>();
while (!creditors.isEmpty() && !debtors.isEmpty()) {
var c = creditors.poll(); var d = debtors.poll();
long pay = Math.min(c.getValue(), -d.getValue());
out.add(new Transfer(d.getKey(), c.getKey(), pay));
c.setValue(c.getValue() - pay); d.setValue(d.getValue() + pay);
if (c.getValue() > 0) creditors.add(c);
if (d.getValue() < 0) debtors.add(d);
}
return out;
}-- 2. Expense splitter: settle up --
balances {Asha=3000, Ravi=-1000, Meera=-2500, Kabir=500}, sum=0
Meera pays Asha 2500
Ravi pays Kabir 500
Ravi pays Asha 500In this example four people need three transfers, and no smaller number is possible: no proper subset of the four balances sums to zero, which was checked by brute force. Source: ProjectIdeasCores.java, section settle.
Build it with these guides:
- @Transactional in Spring: Propagation, Isolation, and the Six Ways It Silently Does Nothing
- Spring Data JDBC vs Spring Data JPA in 2026: When Dropping the ORM Is the Right Call
- Global Exception Handling with ProblemDetail (RFC 9457) in Spring Boot 4
Resume line template: Designed a settle-up algorithm that reduces [N] debts to [M] transfers; stored money as integer minor units and covered rounding with tests.
Trap: Using double for amounts. Use long minor units or BigDecimal.
3. Task board API with JWT security
| Level | Intermediate |
| What it proves | Authentication, authorization, ownership checks |
| Stack | Spring Boot, Spring Security 7, JPA, PostgreSQL |
MVP:
- Register and log in, get a JWT
- Boards, columns and cards
- Only owners and members see a board
Stretch:
- Roles per board
- Refresh tokens
- Audit log of changes
Core data: user, board, board_member(role), card.
Build it with these guides:
- Spring Security 7.1 JWT Authentication: The Complete Guide (Spring Boot 4.1)
- Spring Security OAuth2 Resource Server: JWT Validation, JWKS and Key Rotation
- Method Security in Spring Security 7: @PreAuthorize, @PostAuthorize and the Proxy Traps
- CORS, CSRF and SameSite in Spring Boot 4: The Three Settings Everyone Gets Wrong
- Password Hashing in Java: BCrypt vs Argon2 vs PBKDF2 with Spring Security 7
Resume line template: Secured a task-board API with Spring Security 7 JWT authentication and per-board authorization; covered [N] access rules with tests.
Trap: Checking the role but not the owner: any logged-in user can read another user’s board by guessing an id.
4. Order service with an outbox and idempotent payments
| Level | Advanced |
| What it proves | Distributed-systems thinking: events, retries, duplicates |
| Stack | Spring Boot, Kafka or RabbitMQ, PostgreSQL |
MVP:
- Create an order and reserve stock in one transaction
- Publish
OrderCreatedreliably - Idempotency key on the payment call
Stretch:
- Saga with compensation
- Dead-letter handling
- Read model for order history
Core data: orders, order_lines, outbox(event, published_at), idempotency(key, response).
Build it with these guides:
- Transactional Outbox with the Spring Modulith Event Publication Registry
- Saga Pattern in Spring Boot: Orchestration vs Choreography with Kafka
- Spring Boot 4.1 and Apache Kafka: Producer, Consumer and Serialisation from Scratch
- CQRS in Spring Boot Without a Framework: Separate Read Models and Projections
- Kafka Error Handling with Spring Kafka 4.1: DLT, Retry Topics and Poison Pills
Resume line template: Implemented the transactional outbox and idempotency keys so order events are never lost or duplicated under retry; verified with a failure-injection test.
Trap: Publishing to the broker inside the database transaction: if one succeeds and the other fails, they disagree. That is what the outbox solves.
5. Document vault with upload, download and virus-safe limits
| Level | Intermediate |
| What it proves | File handling, streaming, storage abstraction |
| Stack | Spring Boot, multipart, MinIO or S3, PostgreSQL |
MVP:
- Upload with size and type limits
- Stream large downloads
- Per-user quota
Stretch:
- S3-compatible storage
- Pre-signed URLs
- Thumbnails or text extraction
Core data: document(id, owner, name, size, content_type, storage_key). Keep the bytes in storage, only metadata in the database.
Build it with these guides:
- File Upload and Download in Spring Boot 4: Multipart, Streaming and S3 Presigned URLs
- Spring Boot Docker Compose Support: Real Containers for Local Development
- @DataJpaTest in Spring Boot 4.1 with Testcontainers @ServiceConnection
Resume line template: Built a document service that streams multi-gigabyte files without loading them into memory, with quota and type enforcement.
Trap: Reading the whole upload into a byte[]. Stream it.
6. Rate limiter or API gateway
| Level | Intermediate |
| What it proves | Algorithms in a real component, filters, Redis |
| Stack | Spring Boot, servlet filter or Spring Cloud Gateway, Redis |
MVP:
- Limit requests per client key
- Return 429 with
Retry-After - Configurable limits
Stretch:
- Distributed limiter on Redis
- Different limits per route
- Metrics for rejected requests
Core data: A bucket per client key: current tokens and last refill time.
A token bucket allows short bursts but caps the average rate. The version below takes the time as a parameter, so tests need no sleeping:
/** Token bucket: holds up to `capacity` tokens and refills `perSecond` tokens each second. */
static class TokenBucket {
private final double capacity, perSecond;
private double tokens;
private long lastNanos;
TokenBucket(double capacity, double perSecond, long nowNanos) {
this.capacity = capacity; this.perSecond = perSecond; this.tokens = capacity; this.lastNanos = nowNanos;
}
synchronized boolean tryAcquire(long nowNanos) {
tokens = Math.min(capacity, tokens + (nowNanos - lastNanos) / 1e9 * perSecond); // refill for the elapsed time
lastNanos = nowNanos;
if (tokens >= 1) { tokens -= 1; return true; }
return false;
}
}-- 6. Rate limiter: token bucket, capacity 3, 1 token per second --
t=0ms -> allowed
t=0ms -> allowed
t=0ms -> allowed
t=0ms -> rejected (429)
t=500ms -> rejected (429)
t=1000ms -> allowed
t=1000ms -> rejected (429)
t=5000ms -> allowedCapacity 3 lets three requests through at once; the fourth is rejected until a token refills. Half a second later still no token, one second later one. Source: ProjectIdeasCores.java, section bucket.
Build it with these guides:
- The Spring Security Filter Chain Explained: Every Filter, In Order, and How to Debug It
- Redis with Spring Boot 4.1: RedisTemplate vs StringRedisTemplate, @RedisHash, Pub/Sub and TTL
- @Scheduled, ShedLock and Distributed Cron: Scheduling That Survives Three Replicas
Resume line template: Implemented a token-bucket rate limiter as a servlet filter, tested with a fake clock and verified at [N] requests per second against a load generator.
Trap: Using the wall clock in the algorithm, which makes it untestable. Inject the time, as the demo does.
7. Real-time chat with WebSocket and STOMP
| Level | Intermediate |
| What it proves | Stateful connections, message brokers, security at the socket |
| Stack | Spring Boot, WebSocket/STOMP, a relay broker for scale-out |
MVP:
- Rooms and messages over STOMP
- Message history endpoint
- Authenticate the socket
Stretch:
- Presence (who is online)
- Typing indicators
- Scale across instances with a broker relay
Core data: room, message(room, sender, body, at).
Build it with these guides:
- Server-Sent Events and WebSocket on Spring Boot 4: SseEmitter, STOMP, and Which to Pick
- RSocket vs gRPC vs WebSocket on Spring Boot 4.1: When Each One Wins
- CORS, CSRF and SameSite in Spring Boot 4: The Three Settings Everyone Gets Wrong
Resume line template: Built a real-time chat on STOMP over WebSocket with authenticated connections and message history, scaled to [N] instances behind a broker relay.
Trap: Keeping sessions in one instance’s memory. The second instance will not know about them.
8. Notification service on a message queue
| Level | Intermediate |
| What it proves | Async messaging, retries, dead letters |
| Stack | Spring Boot, Kafka or RabbitMQ, a mail sandbox |
MVP:
- Consume events and send email or push
- Retry with backoff
- Dead-letter queue for poison messages
Stretch:
- Templates and preferences per user
- De-duplicate events
- Dashboard of failures
Core data: notification(id, user, channel, status, attempts).
Build it with these guides:
- Spring Boot 4.1 and Apache Kafka: Producer, Consumer and Serialisation from Scratch
- Spring Boot and RabbitMQ: Exchanges, Queues, Bindings and a Working Dead-Letter Queue
- Kafka Error Handling with Spring Kafka 4.1: DLT, Retry Topics and Poison Pills
- Testing Asynchronous Code with Awaitility (@Async, Kafka Listeners, Schedulers)
Resume line template: Built an event-driven notification service with retry topics and a dead-letter queue; tested asynchronous behaviour with Awaitility.
Trap: Retrying forever. A message that always fails must end up in a dead-letter destination.
9. Document Q&A with Spring AI (RAG)
| Level | Intermediate to advanced |
| What it proves | Embeddings, vector stores, prompt design, guardrails |
| Stack | Spring Boot, Spring AI 2, pgvector, a local model through Ollama or a hosted one |
MVP:
- Upload documents, split and embed them
- Ask questions and answer with sources
- Store vectors in pgvector
Stretch:
- Re-index without paying twice
- Guardrails against prompt injection
- Evaluation tests
Core data: document, chunk(document, text, embedding).
Build it with these guides:
- Spring AI 2.0 in 10 Minutes: ChatClient on Spring Boot 4.1
- Choosing a Vector Store for Spring AI: pgvector vs Redis vs Qdrant vs Elasticsearch
- Re-Index a RAG Store Without Paying for Embeddings Twice: VectorStore.upsert() in Spring AI 2.1
- Prompt Injection Defense in Spring AI: Guardrails, Tool Allow-Lists and Output Validation
- Observability for Spring AI: Tokens, Latency and Cost with Micrometer and OpenTelemetry
- Run LLMs Locally with Spring AI and Ollama: No API Key, Testcontainers Included
Resume line template: Built a retrieval-augmented Q&A service with Spring AI and pgvector that cites its sources; added prompt-injection guardrails and an evaluation suite of [N] questions.
Trap: Showing the answer with no source. A reader cannot tell a grounded answer from an invented one.
10. Job application tracker with batch import and reports
| Level | Intermediate |
| What it proves | Batch processing, migrations, reporting |
| Stack | Spring Boot, Spring Batch, Flyway, PostgreSQL |
MVP:
- CRUD for applications and statuses
- Import a CSV in a batch job
- Weekly summary report
Stretch:
- Partitioned import for large files
- Flyway migrations
- Prometheus and Grafana dashboard
Core data: application(company, role, status, applied_at), status_change(application, from, to, at).
Build it with these guides:
- Spring Batch on Boot 4.1: Jobs, Steps, Chunk Processing and Restartability
- Spring Batch Partitioning and Parallel Steps: Scaling a 10-Million-Row Job
- Flyway vs Liquibase for Spring Boot 4: Migrations, Rollbacks and Baselines
- A Practical Guide to Monitoring Spring Boot Microservices: Prometheus, Grafana, and Boot 4.1’s OpenTelemetry Starter
Resume line template: Imported [N] rows per run with a restartable Spring Batch job and versioned schema migrations; added Prometheus metrics for job duration.
Trap: A batch job that is not restartable. If row 80,000 fails, you must not start from row 1 and duplicate the first 79,999.
Bigger builds on this site
For a multi-service project see the Spring Boot microservices project (gateway, service discovery, config server, resilience, tracing, Docker Compose). For a complete CRUD API with a relational database see Spring Boot CRUD with MySQL. For how to lay out the code, Spring Boot project structure; and if you are new, start with the Spring Boot tutorial for beginners.
Common interview questions
Which Spring Boot project is best for a fresher resume?
One of ideas 1, 2 or 3 finished well, with tests and a README, beats a large unfinished one.
How many projects should I put on my resume?
Two or three finished ones. Quality and the ability to explain every decision matter more than count.
Do I need microservices on a resume?
Only if you can explain why. A well-structured monolith is a good answer; see Modular Monolith vs Microservices in 2026: A Java Decision Framework.
How do I make a simple CRUD project stand out?
Add what real systems have: validation, error format, pagination, security, tests, migrations and metrics.
Can I use AI tools to build these?
You can, but you must understand every line. Interviewers ask “why” about design choices; see AI Coding Agents on a Spring Boot Codebase: A Working Agreement That Keeps the Build Honest.
Common mistakes
- Listing technologies, not outcomes. “Used Redis” says nothing; “cut lookups from [x] ms to [y] ms with Redis” does, if you measured.
- Tutorial clones with no change. Change the domain or add one feature of your own.
- No tests. It is the fastest signal a reviewer checks.
- Secrets in the repository. Keep passwords in environment variables.
- Claiming scale you did not test. Only write numbers you measured.
FAQ
What are good Spring Boot projects for experienced developers?
Ideas 4, 6 and 9: they involve distributed consistency, algorithms and AI guardrails.
How long does each project take?
The MVP lists are sized for roughly one to two weekends; the stretch items are open-ended.
Where do I host a project?
Any container host works; at minimum keep a Docker Compose file so a reviewer can run it locally.
Further reading
- ProjectIdeasCores.java — the base62, token bucket and settle-up code
- Spring Boot tutorial for beginners
- Spring Boot project structure
- Spring Boot microservices project
- Spring Boot CRUD with MySQL
- Spring Boot annotations list
- Top 50 Spring Boot 4 interview questions
- Top 40 Java Collections interview questions
- Top 40 Java concurrency interview questions
- Top 50 Java Stream API coding interview questions
No Comments yet!