I made them a contract they couldn’t refuse.

Talking about backend and frontend development lol. Let’s do some API contracts.


Well, the first step is always to have an idea and then convert it into robust database schema. Do not have any duplicates. Do not have anything stored that can be computed. Have time stamps in your tables. And always keep in mind that this database is your one source of truth. Do not let it get bastardized by duplicates. Well, okay, you have your pretty table, but now you need to have an application that properly updates this database. Each significant action should have its impact registered in the database, and all its aspects should be recorded. If some action happens and it does not get recorded, then your Database is no longer the source of the truth. Thus, this database design makes it a source of truth, and proper code maintains it as a source of truth.

Basically, bad database makes your application worthless downstream, and bad code can make your database go useless upstream. And thus, a bad database will make your application worthless downstream.

So now let’s talk about how we can make our code good. We made our database good, now what should we do to make sure our code will come out good? So that’s why we will start with the layer 1 that is API Design & Contracts.

Just like the database is the source of truth for DATA, the API contract is the source of truth for ACTIONS.

Basically, when the front-end is asking the back-end for something, it is called an API call. Back-end has end-points which are coupled with routes. Routes are our own code. So when an endpoint is tapped, our route gets activated, it talks with our database and gets us some response. We structure it and we send it back. That is called API response.

Based on the endpoint activated, the database will either update something or return some value.

Endpoint = method + URI, when an endpoint is tapped, the route which is associated with it, which is essentially just a piece of code, gets activated. So, if your application is properly made, if someone does post/api/tweet, then it should activate a route in your application that posts a tweet and sends back confirmation. If someone does get/api/sweet, then it should send you back some Tweets.

And this is why it is necessary that your application should be properly coded.

  • If action has taken place, the database should get the appropriate changes reflected in it.
  • If you have bought some subscription, it should reflect in our database.
  • If you have used some credits, it should reflect in our database.
  • If you changed your name, it should reflect in our database.

If the code fails, if the proper routes are not activated, or if the route itself was incompetent in doing the act it was supposed to do, then your database is out of sync. To not let that happen, you have to be extremely clear about what endpoints should exist based on the features you want and the database you have.

You shall have:

  • The appropriate number of endpoints planned out for the features you have
  • Appropriate number of features planned out for the app idea

These two things together will help you design proper contracts and thus API. And contracts are not some piece of code; it’s just a document that you store with yourself so that you don’t forget or leave out anything.

A good API contract should specify:

  • Feature name at top with overview, then the end point.
  • Then within the endpoint:
    • The route activated.
    • Purpose of the endpoint?
    • Authentication
    • The request structure.
    • Database operations.
    • Validation rules.
    • And response structure.
    • Also, should specify if it is a transaction or not.

I will add a template at the bottom of the article. But now comes the question of how do you split the idea into features and features into end points. Assuming you already have the database schema, you are fairly sure about what should be the features. The only thing you have to worry about here is how do you design the endpoints?

Endpoints should map to USER ACTIONS, not database tables.

For each feature, write down what the users can actually do. If you are making a Twitter clone and the feature is Tweets, then the user can:

  • Seek details about a specific Tweet
  • Create a Tweet
  • Look at the Tweets in their feed
  • Search for a specific Tweet
  • Edit their own Tweet
  • Delete their own Tweet
  • They can like, unlike, retweet, report etc.

Then convert those actions into CRUD.

POST   /api/tweets                 - Create tweet
GET    /api/feed                   - Personalized feed
GET    /api/tweets/:id             - View specific tweet
PUT    /api/tweets/:id             - Edit tweet
DELETE /api/tweets/:id             - Delete tweet
POST   /api/tweets/:id/like        - Like tweet
DELETE /api/tweets/:id/like        - Unlike tweet
POST   /api/tweets/:id/retweet     - Retweet
GET    /api/tweets/:id/replies     - View replies
POST   /api/tweets/:id/replies     - Reply to tweet


From claude


The Two-Part System of Truth

Part 1: Design Makes It POSSIBLE to Be Truth

Good schema design = CAN be source of truth
- No duplicates = Can't contradict itself
- No stored calculations = Can't get out of sync
- Timestamps = Can track history
- Foreign keys = Enforces relationships

Part 2: Code Makes It ACTUALLY Truth

Proper code = IS source of truth
- Every action recorded = Nothing happens "off the books"
- All aspects captured = Complete picture
- Consistent updates = Stays accurate over time

Your insight: You can have the most beautiful database schema in the world, but if your code doesn’t respect it, it’s worthless.

You can’t fix bad database design with clever code. You can’t maintain good database design with sloppy code. Both must be good, or the entire system is worthless.

API workflow

FRONTEND makes an API CALL
         ↓
    "Hey backend, give me tweets"
         ↓
API CALL hits an ENDPOINT
         ↓
    GET /api/tweets
         ↓
ENDPOINT activates a ROUTE
         ↓
    app.get('/api/tweets', handler)
         ↓
ROUTE (our code) runs
         ↓
    - Validates request
    - Talks to DATABASE
    - Gets data back
    - Structures/formats it
         ↓
Sends back API RESPONSE
         ↓
    { tweets: [...] }
         ↓
FRONTEND receives response
         ↓
Updates UI with data

What “Out of Sync” Means

Example 1: User Buys Subscription

What SHOULD happen:

1. User clicks "Buy Premium"
2. Frontend: POST /api/subscriptions
3. Route activates
4. Route logic:
   - Charge credit card
   - IF payment successful:
     - UPDATE users SET plan='premium' WHERE user_id=5
     - INSERT INTO subscriptions (user_id, plan, start_date)
   - IF payment failed:
     - Don't update database
5. Database now says: user is premium ✓
6. Send response: { success: true, plan: 'premium' }
7. Frontend updates UI: "You are now Premium!"

What happens with SHITTY code:

1. User clicks "Buy Premium"
2. Frontend: POST /api/subscriptions
3. Route activates
4. Route logic:
   - Charge credit card ✓
   - UPDATE users SET plan='premium' ✓
   - Forgot to INSERT into subscriptions table ✗
   - OR: Insert failed but didn't rollback ✗
5. Database now says:
   - users table: user is premium ✓
   - subscriptions table: no subscription record ✗
6. App is now BROKEN:
   - Billing system thinks user is free
   - App UI thinks user is premium
   - User can't access premium features
   - Support can't figure out what's wrong

DATABASE IS OUT OF SYNC.


Example 2: User Uses Credits

What SHOULD happen:

1. User generates an AI image (costs 5 credits)
2. Frontend: POST /api/images/generate
3. Route:
   - Check if user has >= 5 credits
   - IF yes:
     - Generate image
     - UPDATE users SET credits = credits - 5 WHERE user_id=5
     - INSERT INTO credit_usage (user_id, amount, action)
     - COMMIT transaction
   - IF no:
     - Return 403 "Insufficient credits"
4. Database: credits reduced by exactly 5 ✓
5. Response: { image_url: "...", credits_remaining: 45 }

What happens with SHITTY code:

1. User generates image
2. Route:
   - Generate image ✓
   - Forgot to deduct credits ✗
3. Database: user still has 50 credits
4. User generates 100 images for free
5. Your business loses money

OR worse:

1. User clicks generate 3 times quickly
2. Each request hits route
3. Route checks credits: 50 (all pass)
4. Route deducts: 50 - 5 = 45
5. Route deducts: 50 - 5 = 45 (race condition!)
6. Route deducts: 50 - 5 = 45
7. Database ends at 45, but should be 35
8. User got 3 images but only paid for 1

DATABASE IS OUT OF SYNC.


Example 3: User Changes Name

What SHOULD happen:

1. User changes name to "John Doe"
2. Frontend: PUT /api/users/5
   Body: { name: "John Doe" }
3. Route:
   - Validate: name is 1-50 chars
   - UPDATE users SET name='John Doe' WHERE user_id=5
   - UPDATE updated_at=NOW()
4. Database reflects change ✓
5. Response: { user_id: 5, name: "John Doe", updated_at: "..." }
6. Frontend updates everywhere name appears

What happens with SHITTY code:

1. User changes name
2. Frontend: PUT /api/users/5
3. Route:
   - UPDATE users SET name='John Doe'
   - Forgot WHERE clause ✗
4. Database: ALL users now named "John Doe"
5. APP IS DESTROYED

OR:

1. User changes name
2. Frontend updates UI immediately (optimistic)
3. API call fails (network error)
4. Frontend thinks name is "John Doe"
5. Database still has old name
6. User refreshes → old name appears
7. User confused: "I changed it!"

DATABASE IS OUT OF SYNC.


Contract template

# [Feature Name]

## [METHOD] [URL]

**Route:** `functionName` in `filename.js`

**What it does:** Brief description

**Auth:** Required/Optional

**Request:**
```json
{
  "field": "type"
}
```

**Validation:**
- field: constraints

**Database:**
1. Action on table_name
2. Action on table_name

**Transaction:** Yes/No

**Response (200):**
```json
{
  "field": "value"
}
```

```

---

## How to Use This Template

### Step 1: Plan Your Features
```
My App: Tweet Clone

Features:
1. Users can create tweets
2. Users can view tweets
3. Users can delete their tweets
4. Users can like tweets
5. Users can follow other users
```

### Step 2: For Each Feature, List Endpoints
```
Feature: Tweets
- POST   /api/tweets       (create)
- GET    /api/tweets       (list all)
- GET    /api/tweets/:id   (get one)
- DELETE /api/tweets/:id   (delete)

Feature: Likes
- POST   /api/tweets/:id/like    (like)
- DELETE /api/tweets/:id/like    (unlike)
- GET    /api/tweets/:id/likes   (who liked)

Feature: Follow
- POST   /api/users/:id/follow   (follow)
- DELETE /api/users/:id/follow   (unfollow)
- GET    /api/users/:id/followers (followers)
```

### Step 3: Fill Out Template for Each Endpoint

Use the full template above for each one.

### Step 4: Review Against Database Schema

Make sure every endpoint's database operations match your schema:
```
Endpoint: POST /api/tweets

Database operations:
1. INSERT into tweets ✓ (tweets table exists)
2. UPDATE users.tweet_count ✓ (field exists)
3. INSERT into tweet_media ✓ (table exists)

All good! Contract is valid.
```

---

## Checklist: Is My Contract Complete?

For each endpoint, verify:

- [ ] Feature name listed at top
- [ ] Endpoint URL and method specified
- [ ] Route handler name and file specified
- [ ] Purpose clearly explained
- [ ] Auth requirements stated
- [ ] All request fields documented with types and constraints
- [ ] All validation rules listed
- [ ] All database operations listed in order
- [ ] Transaction requirement specified (yes/no)
- [ ] Success response format shown with status code
- [ ] All possible error responses listed with codes
- [ ] Side effects documented
- [ ] Testing checklist included

---