Day 8 of 30. Our week 2 begins. Today we define the interface before building the implementation.
One of the most challenging things to change when developing an application like ours, is our API contract. Any change to the API might have a chance of affecting one or more clients, so once developers integrate with your API, changing the API is painful, so our API requires a bit of thinking.
So, let's design something we won't regret (too much...).
API design principles we're following
There are different technologies and different approaches for designing remote APIs. One of the most common approaches would be a REST API over HTTP, but even when using REST, there are different maturity levels of defining APIs.
While we value a REST API, in our case, our focus in more making an API which makes integration easier, and when it makes sense, we'll use a REST approach, but when it doesn't make sense, we're taking a conscious approach to delivering an API which still makes a lot of sense.
We use the following criteria for our API:
1. Be predictable. Follow REST conventions as much as we can. We use standard HTTP methods and status codes, and we don't want to surprise developers.
2. Be consistent. If one endpoint returns created_at, all endpoints have to return created_at with the same naming, same formats, everywhere.
3. Be explicit. We shouldn't introduce magic. If a parameter affects behavior, we will require it explicitly rather than inferring from a context.
4. Be versioned. While maintaining multiple versions of our API is not our goal, we will start with /api/v1/.
While we hope to never need v2, and we don't really want to cater for all "what if" scenarios, the API is quite important, and having the option to add a v2 in the future could prove useful.
5. Return useful errors. One of the most frustrating things is getting errors without a clear explanation on how to resolve the error. We had our fair share in that, so we don't just want to say "Bad Request.". Instead, we'll say what's wrong and how to fix it,
with a link to our documentation portal where needed.
The actual API
We'll spare you the full spec, but here's the gist:
-
POST /api/v1/screenshots- submit a URL, get back a job ID -
GET /api/v1/screenshots/{id}- check if it's done, get the image URL -
GET /api/v1/screenshots- list your recent screenshots -
GET /api/v1/usage- see how many screenshots you've used this month
Four endpoints. That's it. We can always add more, but we can never take them away.
Implementation notes
A few decisions that affect how we'll build this:
IDs are prefixed. scr_ for screenshots makes debugging easier. You immediately know what kind of ID you're looking at.
Timestamps are ISO 8601 UTC. Always to prevent ambiguity.
Pagination uses offset, not cursors. Pagination like this is simpler to implement, and while cursor-based pagination might be better at scale, but this scale is beyond our usecase.
Image URLs are signed and temporary. Image URLs expire after 24 hours. This lets us clean up storage and prevents hotlinking.
What we built today
- Wrote the full API specification (this document, basically)
- Created request/response DTOs in Kotlin
- Set up validation annotations
- Stubbed out the controller endpoints (returning mock data)
- Tested with curl to verify the contract
No actual screenshot integration yet - that comes tomorrow. But the interface is locked.
When we wire up the real implementation, we will be less tempted to "just quickly change" the API shape.
Tomorrow: building the screenshot queue
Tomorrow, day 9, we will build the real thing. Job queue in Postgres, worker processing, status updates. The contract we defined today becomes real.
Book of the day
REST API Design Rulebook by Mark Massé
Short, practical, opinionated. This book gives you concrete rules for designing REST APIs that don't suck.
Some highlights: use nouns not verbs in URLs, use HTTP methods correctly, be consistent with naming conventions, design for extensibility without breaking changes.
We don't agree with everything (the book is a bit dogmatic about HATEOAS), but as a quick reference for "what's the right way to do X in a REST API," it's invaluable.
If you're designing an API and want to avoid common mistakes, read this first. It's only 100 pages.
Current stats:
- Hours spent: 17 (14 + 3 today)
- Lines of code: ~650
- Monthly hosting cost: $5.50
- Revenue: $0
- Paying customers: 0
- API endpoints defined: 4
- API endpoints implemented: 0 (stubs only)
Top comments (0)