John Roest

What building Orevox taught me

What Building Orevox Taught Me

Two months of building, failing, and figuring things out as a developer and founder


From idea to geopolitical API#

Orevox started as a gut feeling. I was following the escalation between Iran and Israel closely, and I kept thinking: why is there no API for this? Compliance officers, risk analysts, investors — they all work with expensive, outdated data. Half of it lives in PDFs. The other half requires a vendor contract nobody wants to sign.

So I decided to build it myself. Spring Boot 3, Java 21, Next.js, scrapers, pipelines, GDELT integrations, OFAC lists, EU sanctions data. It took longer than expected. It always does. But I learned a lot, and not just technical stuff.

This is my honest write-up.


1. Vision first#

The first lesson was also the most painful one: technical skill without a clear product vision produces feature drift.

In the early weeks I built things because I could, not because they were needed. An extra aggregation source. Another field in the response schema. A second scoring model just to see if it would work.

What snapped me out of it was forcing myself to test every feature idea against one question:

Does this help a compliance officer who needs to make a call at 4:30 PM about a transaction with a Lebanese counterparty?

If the answer was no or maybe, the feature went on a list and stayed there. That one question saved me weeks of wasted work.

Product vision is not something only big teams need. As a solo builder it is your only real protection against building in circles. Write it down. Read it back before you start something new.


2. How I actually used AI#

Claude changed how I work, but probably not in the way people expect when they hear that.

I never used it as a code generator I blindly copy from. I used it as someone to think out loud with. A technical sparring partner that asks better follow-up questions than most people I know.

Thinking through architecture decisions

Whenever I was stuck on a choice, say whether to process GDELT scores synchronously or asynchronously, I wrote out the full situation. What I was trying to do, what I had tried, what was worrying me. Half the time the act of writing it out solved the problem before I even got a response. The other half, I got back a question I had not thought to ask myself.

Code reviews on my own code

I got into the habit of pasting my own code and asking: what would a senior reviewer flag here? The feedback on edge cases, missing null checks, inconsistent error handling was genuinely sharper than what I could produce myself after a long day.

Documentation as a deliverable, not an afterthought

After every build session I wrote docs. AI helped me keep them consistent and structured in the /docs folder of the repo. That discipline paid off months later when I needed to write landing page copy and API documentation. Most of the thinking was already done.

The thing I keep telling people: AI is most useful for the tasks where you are the bottleneck, not the knowledge. Thinking, structuring, reviewing, writing. It accelerates those things without doing them for you.


3. OWASP in a real Spring Boot application#

One of the most valuable things I did was walk through the OWASP Top 10 systematically and think about how Orevox could be attacked in each category. I did this with Claude as a sounding board. For every category I asked: which attack vectors matter for a REST API with JWT auth, external data scraping, and public endpoints?

Here is what actually changed in the codebase as a result.

A01: Broken Access Control

Orevox has endpoints that are only available to Enterprise users. My first implementation only enforced this at the controller level. After the OWASP review I added a service layer check and started using @PreAuthorize annotations with Spring Security to enforce authorization at the method level.

@PreAuthorize("hasRole('ENTERPRISE') or hasRole('ADMIN')")
public CountryRiskResponse getFullRiskProfile(String isoCode) {
    // ...
}

Never trust the controller alone. Every layer should do its own check.

A03: Injection

My scrapers pull data from external sources and persist it to PostgreSQL via JPA. With dynamic queries built on user input like ISO codes and filter parameters, injection risk was real. I migrated everything to the JPA Criteria API and named parameters, then wrote integration tests that deliberately sent broken input to verify the application held up.

A07: Authentication Failures

JWT looks simple until it bites you. Things I learned the hard way:

  • Always validate iss, aud, and exp claims
  • Never put sensitive data in the token payload
  • Store refresh tokens hashed, not in plaintext
  • Rate limit the login endpoint or someone will hammer it
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request, HttpServletRequest httpRequest) {
    String ip = httpRequest.getRemoteAddr();
    Bucket bucket = rateLimiter.resolveBucket(ip);

    if (!bucket.tryConsume(1)) {
        return ResponseEntity.status(429).body("Too many attempts. Please try again later.");
    }
    // authentication logic...
}

A09: Security Logging

I had logging. I did not have useful logging. After the review I added structured security events: failed login attempts, access to sensitive endpoints, unexpected status codes from external sources. The kind of log entries you can actually act on.

OWASP is not a checklist. It is a way of thinking about your own system as an attacker would. Do it early.


4. Spring Boot: what was new, what I understood better#

I have worked with Spring Boot for years, at Volksbank, MoneyMonk, and now at the NFI. But Orevox pushed me into territory I had never touched in a professional project.

Spring Batch for data pipelines

Orevox processes data from a lot of external sources: GDELT event feeds, OFAC SDN lists, travel advisory pages. I started with @Scheduled methods and manual loops. That broke down fast.

Moving to Spring Batch was a proper revelation. The Job, Step, ItemReader, ItemProcessor, ItemWriter model gave me a solid structure for large dataset processing with retry logic, skip policies, and monitoring via the JobRepository. The best part is that Spring Batch tracks which JobInstance has already run, so idempotency came almost for free.

@Bean
public Job gdeltIngestionJob(JobRepository jobRepository, Step parseStep, Step scoreStep) {
    return new JobBuilder("gdeltIngestionJob", jobRepository)
        .start(parseStep)
        .next(scoreStep)
        .build();
}

@Scheduled: fixedDelay vs fixedRate

This sounds like a minor detail but it caused real problems. With fixedRate, if a scraping run takes longer than the interval, the next run starts anyway. You stack up runs. You get database race conditions. fixedDelay waits until the current run finishes before scheduling the next one. Switch to it for scrapers and never look back.

Conditional beans and Spring profiles

I needed real API clients in production and mock implementations locally. The solution was combining @ConditionalOnProperty with @Profile:

@Bean
@Profile("local")
public GdeltClient mockGdeltClient() {
    return new MockGdeltClient();
}

@Bean
@Profile("!local")
public GdeltClient realGdeltClient(GdeltProperties properties) {
    return new GdeltHttpClient(properties);
}

No API keys needed locally. No if-statements polluting the business logic. Clean separation.

Exception handling done properly

I already knew @ControllerAdvice. What I did not have was a proper taxonomy of errors. Orevox now has three categories:

  1. Domain errors — expected, like an invalid ISO code
  2. Integration errors — temporary, like an external service being down
  3. System errors — unexpected, logged with a full stack trace but never exposed to the caller

Each category gets its own response format, HTTP status, and logging behavior. That consistency made writing the API documentation much easier.


5. What building taught me about myself#

I prefer shipping over perfecting. When I spend too long on a single feature I lose momentum. Working in focused sessions of two hours max, then closing with a commit and docs, kept me going over months rather than burning out after weeks.

I always underestimate scraping work. Every external source is an assumption. Pages change their structure. APIs add fields without notice. GDELT updates its schema. Writing parsers that survive real-world drift costs more time than the feature you are building on top of them.

Talking to users feels uncomfortable but is irreplaceable. I sent Orevox to ten potential users without explaining what it was. The questions they asked surfaced assumptions I had not even known I was making.


6. What AI actually changes about solo projects#

An experienced developer with a clear vision and modern AI tools can now build things that used to need a small team. I say that not as a prediction but as something I lived through.

The conditions still matter though.

Domain knowledge is not optional. I could build Orevox because I had spent real time studying geopolitics and understanding how risk analysts work. Without that background the API would have been generic and no one would have cared.

AI accelerates decisions, it does not make them. Choosing Spring Batch, walking through OWASP, picking fixedDelay over fixedRate — those were my calls. AI helped me understand the implications faster, not choose for me.

Documentation and structure determine how far you can go. Close every session with a commit and updated docs and you can pick things back up six months later as if no time passed. Without that discipline, you build up debt that quietly compounds until the project dies.


Wrapping up#

Orevox is not finished. That was never really the point.

The point was to build something real, learn from the responsibility of owning the whole thing, and ship a product I would actually want to use. I learned more from this project than from most of my professional work, because there was nobody else to catch my mistakes.

If you are considering building your own product, here is what I would tell you:

Start with a sharp vision. Use AI to think more clearly, not to avoid thinking. Take security seriously before you need to. Write documentation because your future self will not remember why you made the decisions you made.

Orevox is in beta and Enterprise accounts are free for early adopters. If you work with geopolitical risk and are willing to give honest feedback, reach out.


John, Senior Java Developer and Team Lead at Sopra Steria, currently working at the NFI, and founder of Orevox.

@StackMasterJohn on YouTube for Java content.