Erdmates Logo
Erdmates
Back to Blog
APIRESTGraphQLDatabaseERD

API Design with ERD: How Your Database Schema Shapes Your API

Understand how to translate a well-designed ERD into a clean REST or GraphQL API, including resource modeling, relationships, and pagination.

April 17, 20268 min read

The Connection Between Schema and API

Your API is a public contract with clients — mobile apps, third-party integrations, and frontend teams. Your database schema is a private implementation detail. The most common mistake in API design is exposing the database schema directly as the API surface, coupling internal implementation to external contracts.

However, a well-designed ERD is still the best starting point for API design. The entities in your ERD map naturally to API resources, and relationships in your schema translate into nested resources or query parameters. This guide shows how to make that translation thoughtfully.

Mapping ERD Entities to REST Resources

Each primary entity in your ERD typically becomes a top-level REST resource. Relationships determine the nesting strategy.

Consider this schema:

CREATE TABLE projects (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name        VARCHAR(255) NOT NULL,
  owner_id    UUID NOT NULL REFERENCES users(id),
  visibility  VARCHAR(20) DEFAULT 'private',
  created_at  TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE project_members (
  project_id  UUID REFERENCES projects(id) ON DELETE CASCADE,
  user_id     UUID REFERENCES users(id) ON DELETE CASCADE,
  role        VARCHAR(20) NOT NULL DEFAULT 'viewer',
  joined_at   TIMESTAMPTZ DEFAULT now(),
  PRIMARY KEY (project_id, user_id)
);

CREATE TABLE diagrams (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id  UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  name        VARCHAR(255) NOT NULL,
  data        JSONB NOT NULL DEFAULT '{}'
);

This maps to:

GET    /projects                    → List projects
POST   /projects                    → Create project
GET    /projects/:id                → Get project
PATCH  /projects/:id                → Update project
DELETE /projects/:id                → Delete project

GET    /projects/:id/members        → List project members
POST   /projects/:id/members        → Add member
DELETE /projects/:id/members/:userId → Remove member

GET    /projects/:id/diagrams       → List diagrams
POST   /projects/:id/diagrams       → Create diagram
GET    /projects/:id/diagrams/:dId  → Get diagram

The nesting depth mirrors the dependency in your ERD: diagrams belong to projects, so they live under /projects/:id/diagrams. Avoid nesting more than two levels deep — beyond that, use query parameters instead.

Efficient Queries for Nested Resources

A naive implementation of GET /projects/:id/diagrams might issue a separate query per project attribute. Design the query to return everything needed in a single round-trip.

-- Single query for GET /projects/:id (with member count and diagram count)
SELECT
  p.id,
  p.name,
  p.visibility,
  p.created_at,
  u.display_name AS owner_name,
  u.avatar_url   AS owner_avatar,
  COUNT(DISTINCT m.user_id) AS member_count,
  COUNT(DISTINCT d.id)      AS diagram_count
FROM projects p
JOIN users u          ON u.id = p.owner_id
LEFT JOIN project_members m ON m.project_id = p.id
LEFT JOIN diagrams d        ON d.project_id = p.id
WHERE p.id = $1
GROUP BY p.id, u.display_name, u.avatar_url;
-- Paginated list: GET /projects?cursor=<id>&limit=20
SELECT
  p.id, p.name, p.visibility, p.created_at,
  pm.role AS my_role
FROM projects p
JOIN project_members pm ON pm.project_id = p.id AND pm.user_id = $current_user
WHERE p.id > $cursor_id   -- Keyset pagination
ORDER BY p.id
LIMIT 21;  -- Fetch one extra to determine if there is a next page

GraphQL Schema from ERD

GraphQL's type system maps almost one-to-one with a well-designed ERD. Each entity becomes a type, and each foreign key relationship becomes a field resolver.

-- ERD relationship:
-- projects.owner_id → users.id  (many-to-one)
-- diagrams.project_id → projects.id  (many-to-one)
# GraphQL schema derived from the ERD
type Project {
  id:          ID!
  name:        String!
  visibility:  String!
  createdAt:   String!
  owner:       User!       # Resolved via JOIN on users.id = projects.owner_id
  members:     [Member!]!  # Resolved via JOIN on project_members
  diagrams:    [Diagram!]! # Resolved via JOIN on diagrams.project_id
  memberCount: Int!
}

type Member {
  user:     User!
  role:     String!
  joinedAt: String!
}

type Query {
  project(id: ID!): Project
  projects(limit: Int, cursor: ID): ProjectConnection!
}

N+1 warning: Without DataLoader batching, resolving owner for a list of 20 projects fires 20 separate user queries. Use DataLoader to batch:

-- DataLoader batches: SELECT * FROM users WHERE id = ANY($ids)
-- Instead of 20 individual: SELECT * FROM users WHERE id = $1
SELECT id, email, display_name, avatar_url
FROM users
WHERE id = ANY($1::uuid[]);

Designing the API Contract from ERD

The clearest API contracts come from clean, well-normalized ERDs. When your schema is ambiguous, your API will be too. Erdmates lets you design your ERD collaboratively with frontend and backend engineers simultaneously, so the API contract and the database schema evolve together — reducing mismatches and rework during integration.

Conclusion

Your ERD is the foundation of your API design. Map primary entities to top-level resources, use nesting to express strong ownership relationships, and write efficient queries that return all necessary data in one round-trip. In GraphQL, leverage the ERD type system directly but add DataLoader batching to prevent N+1 queries. A clean schema leads to a clean API — invest in the ERD first.

Start with Erdmates Today

Start designing databases with our AI-powered ERD editor. Free to use.

Try Free DemoStart for Free

Related Articles

bigint, UUID v4, or UUID v7? How to Choose a Primary Key (with PostgreSQL 18's uuidv7())

Compare bigint, UUID v4, and UUID v7 primary keys, see what PostgreSQL 18's built-in uuidv7() changes, and learn which key to pick for each situation.

Databases Read by AI Agents: Schema Design Principles for the MCP Era

AI agents are increasingly connecting directly to databases through MCP. This post covers how to design names, comments, and constraints so agents don't misinterpret your schema, and how to split permissions safely.

PostgreSQL 19 Preview: Changes That Affect Schema Design and Operations (as of Beta 4)

Ahead of its official release, this post covers the PostgreSQL 19 changes that directly affect table design, migrations, and operations, based on the official release notes (Beta 4). It also looks at features reverted during beta and behavior changes to check before upgrading.

Erdmates Logo
Erdmates

Design databases with natural language and collaborate in real-time with your team using our AI-powered ERD editor.

Service

  • Features
  • Pricing
  • Demo
  • SQL to ERD converter

Resources

  • Blog
  • Help
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
  • Third-Party Provision
Company: Produx|Representative: 최정회
Address: 강원특별자치도 원주시 솔우물2길 4-1 (무실동)
Email: master@erdmates.com
Privacy Officer: 최정회|Hosting Provider: Amazon Web Services

© 2026 Produx. All rights reserved.