The Resource-First API Pattern That Saved Our Sanity (And Why You’ve Never Heard of It)

When Your API Feels Like A Hostage Negotiation

Last month, I watched a frontend engineer spend forty-five minutes hunting through three different endpoints just to display a user’s profile picture. The image URL lived in `/users/{id}/avatar`, the display preferences were buried in `/users/{id}/settings/ui`, and the fallback avatar logic required a separate call to `/assets/defaults`. By the time she stitched it all together, she’d made six HTTP requests for what should have been a single, obvious operation.

This wasn’t bad engineering. This was textbook resource-oriented API design following RESTful principles to the letter. And it was driving everyone insane. The problem isn’t REST itself, but how religiously we’ve been applying resource thinking without considering the human cost. After fifteen years of building APIs that make perfect architectural sense but terrible developer experiences, I’ve started moving toward patterns that prioritize workflow over purity.

The Resource-First Trap (And Why We Keep Falling Into It)

Traditional REST teaches us to think in resources: users, posts, comments, likes. Each gets its own endpoint, its own CRUD operations, its own neat little box in our mental model. GitHub’s API shows this approach beautifully with `/repos/{owner}/{repo}`, `/repos/{owner}/{repo}/issues`, `/repos/{owner}/{repo}/pulls`. Clean, predictable, infinitely cacheable.

But watch someone actually use these APIs in production. They’re not thinking about resources. They’re thinking about tasks: “show me everything I need for the project dashboard,” or “give me the data to render this user’s activity feed.” The mismatch between how we design APIs and how people consume them creates constant friction where simple UI updates require complex orchestration.

The breaking point usually comes when you realize your mobile app is making twelve requests on startup. Each one a perfectly designed RESTful resource call that collectively murder your performance metrics. That’s when smart teams start building facade endpoints, which is just an admission that your resource model doesn’t match your usage patterns.

Workflow-Oriented Endpoints: The Pattern You’re Already Using

Here’s the thing: you’re probably already building workflow endpoints, you just haven’t named them yet. That `/api/dashboard/summary` endpoint that returns user stats, recent activity, and pending notifications in one response? That’s workflow-oriented design. The `/api/onboarding/progress` endpoint that tells you exactly which steps a user has completed? Same pattern.

Spotify’s Web API does this brilliantly with endpoints like `/me/player` which returns not just the current playback state, but the device information, track details, and context all in one response. They could have made you hit `/me/player/state`, `/me/player/device`, `/me/player/track` separately, but they understand that 99% of the time, you want it all.

The insight here is designing around user journeys instead of data models. When someone opens your app, what do they actually need to see? When they click “edit profile,” what data makes that screen useful? Build endpoints that address those specific moments, even if it means breaking your perfect resource hierarchy.

Implementation Patterns That Actually Work

The most successful workflow-oriented APIs I’ve seen follow a few common patterns. First, they use query parameters aggressively to customize responses. Instead of forcing clients to make multiple calls, give them `/api/users/123?include=avatar,preferences,recent_activity`. This lets different clients request exactly what they need without over-fetching.

Second, they embrace compound operations. Slack’s API lets you post a message and upload a file in the same request with `/api/files.upload` accepting both file data and channel information. Compare that to the alternative: upload the file, get the file ID, then post a message referencing that ID. The compound approach eliminates race conditions and reduces the surface area for failures.

Third, they design for common UI patterns explicitly. If your app has an infinite scroll feed, don’t make developers piece together pagination, content, and metadata from separate endpoints. Build `/api/feeds/timeline?cursor=xyz&include=reactions,author_details` that returns everything needed to render the next page. GraphQL popularized this thinking, but you don’t need GraphQL to apply it.

The Pragmatic Balance

This doesn’t mean abandoning resource-oriented thinking entirely. The best APIs I’ve worked with maintain clean resource endpoints for basic CRUD operations while layering workflow endpoints on top. Stripe does this perfectly: you can still GET `/customers/{id}` or POST `/charges`, but they also provide compound endpoints like `/payment_intents` that handle the entire payment flow in one coordinated operation.

The trick is knowing when to break the rules. If you find yourself recommending that clients make more than three requests to accomplish a common task, that’s a red flag. If your API documentation includes a lot of “first do this, then do that, then do this other thing” sequences, you’re probably missing some workflow endpoints.

Start by auditing your actual API usage. Look at your logs and identify the request patterns your clients are actually making. Those patterns of 3-4 requests that always happen together? Those are your workflow endpoint candidates. Build them intentionally, document them clearly, and watch your developer experience improve overnight.

The goal isn’t architectural purity. It’s building APIs that feel like natural extensions of human intent rather than complex puzzles that need solving. Sometimes the most elegant technical solution is the one that makes your 3 AM debugging sessions shorter and your frontend engineers slightly less likely to mutter under their breath about “whoever designed this thing.”