image
api-design

Why Some APIs Should Be Append-Only

Sarvar Musazadeviews

Most backend applications follow the traditional CRUD model:

This approach works perfectly for many types of data. Users update their profiles, products change prices, and tasks are deleted after they're completed.

However, not every piece of data should be editable.

Some information represents history, and history should never be rewritten.

Examples include:

For these cases, an append-only API is often a much better design.

What Does Append-Only Mean?

An append-only system allows new records to be created, but existing records can never be modified or deleted.

Instead of changing history, you simply add another event.

Imagine a user's role changes from USER to ADMIN.

A traditional database might only store the latest value:

Role = ADMIN

But several important questions remain unanswered:

An append-only system stores every change as a separate event.

Actor: admin-42
Action: ROLE_CHANGED
Target User: user-17
Old Role: USER
New Role: ADMIN
Timestamp: 2026-07-24T10:30:00Z

If the role changes again tomorrow, the previous record remains untouched.

A new event is simply appended.

Why Prevent UPDATE and DELETE?

The primary purpose of an audit log is to preserve the truth.

If someone can modify or delete historical records, the system loses credibility.

Imagine an administrator executing:

DELETE FROM audit_events
WHERE actor_id = 42;

Every trace of their actions disappears.

Or even worse:

UPDATE audit_events
SET action = 'PROFILE_VIEWED'
WHERE action = 'ROLE_GRANTED';

The event still exists, but it no longer reflects reality.

This is why append-only APIs follow a simple rule:

INSERT ✅
SELECT ✅
UPDATE ❌
DELETE ❌

History should be preserved, not rewritten

image-1

Designing an Append-Only API

A simple Audit Event entity might look like this:

@Entity
public class AuditEvent {

@Id
private Long id;

private Long actorId;

private String action;

private String resourceType;

private String resourceId;

private String ipAddress;

private Instant occurredAt;
}

The service only exposes creation and read operations.

public AuditEvent create(AuditEvent event);

public Page<AuditEvent> findAll(Pageable pageable);

Notice what's missing.

update()

delete()

deleteById()

These operations simply do not exist.

Likewise, the REST API exposes only:

POST /audit-events

GET /audit-events

GET /audit-events/{id}

There are intentionally no endpoints for:

PUT

PATCH

DELETE

The API itself communicates that audit history is immutable.

Application-Level Protection Isn't Enough

Removing update endpoints is a good start.

But what if someone connects directly to the database?

Or another service accidentally executes an UPDATE statement?

Business rules that are truly critical should not rely only on application code.

They should also be enforced by the database itself.

Enforcing Append-Only in PostgreSQL

A PostgreSQL trigger can reject every UPDATE and DELETE automatically.

CREATE OR REPLACE FUNCTION reject_audit_event_mutation()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
RAISE EXCEPTION 'audit_events is append-only';
END;
$$;

Then attach the trigger.

CREATE TRIGGER audit_events_append_only
BEFORE UPDATE OR DELETE
ON audit_events
FOR EACH STATEMENT
EXECUTE FUNCTION reject_audit_event_mutation();

Now any attempt to modify history fails immediately.

DELETE FROM audit_events;

Result:

ERROR:
audit_events is append-only

The database itself becomes the final guardian of your business rule.

imag

How Do You Correct Mistakes?

One common question is:

"What if someone inserts incorrect data?"

You don't modify the existing record.

Instead, you append another event that corrects the previous one.

Example:

PAYMENT_CREDITED +100 USD

Correction:

PAYMENT_REVERSAL -100 USD

Both events remain in history.

Nothing disappears.

This creates a complete and trustworthy audit trail.

Advantages of Append-Only APIs

Complete History

Every event remains available for investigation.

Better Security

Critical actions cannot be hidden.

Regulatory Compliance

Many industries require immutable records for legal reasons.

Easier Auditing

Security teams can reconstruct exactly what happened.

Higher Trust

The system never rewrites historical events.

Challenges

Append-only isn't free.

As data grows, storage requirements increase.

Common solutions include:

Another challenge is determining the current state.

Sometimes the latest value must be reconstructed from multiple events.

For example:

ORDER_CREATED

ORDER_PAID

ORDER_SHIPPED

ORDER_DELIVERED

Many systems solve this by maintaining two models:

This combines fast queries with complete traceability.

Should Every API Be Append-Only?

Definitely not.

User profiles should be editable.

Products need price updates.

Tasks should be marked as completed.

CRUD remains the correct choice for many business domains.

Append-only is most valuable when answering one question matters:

"What exactly happened in the past?"

If preserving that history is essential, append-only is likely the better design.

Final Thoughts

Append-only APIs are more than just a database pattern.

They are a design philosophy centered around trust, traceability, and data integrity.

Whenever your system handles:

consider whether your data should be updated or simply grow over time.

Sometimes the most reliable system isn't the one that edits history.

It's the one that never allows history to change.

If you enjoy backend engineering content covering Java, Spring Boot, System Design, Distributed Systems, and Software Architecture, feel free to connect with me here on LinkedIn.

📸 Instagram: @the.code.architect — Short visual explanations and engineering diagrams.

✍️ Medium: Long-form deep dives into backend architecture, performance, and system design.

Question for you: Have you ever designed an append-only system? If so, what business problem did it solve?

Comments (0)

0/1000

Loading comments...