Free Tool

Database Schema Planner

Starting a new app and staring at a blank database? Pick your project type and features — get tables, fields, relationships, indexes and an ERD to start from.

Plan my schema

Features

Schema

8

tables

ERD (relationships)

users          1 ──< profiles
users          1 ──< posts
posts          1 ──< comments
users          1 ──< comments
users          1 ──< notifications
users          1 ──< followers
users          1 ──< reactions
users          1 ──< device_tokens

users

PKidBIGINT
nameVARCHAR(120)
UQemailVARCHAR(160)
passwordVARCHAR(255)
email_verified_atTIMESTAMP NULL
created_atTIMESTAMP
updated_atTIMESTAMP

index: email (unique)

profiles

PKidBIGINT
FKuser_idBIGINT
bioTEXT NULL
avatarVARCHAR(255) NULL
phoneVARCHAR(20) NULL
created_atTIMESTAMP
updated_atTIMESTAMP

↳ belongs to users

index: user_id

posts

PKidBIGINT
FKuser_idBIGINT
UQslugVARCHAR(220)
titleVARCHAR(200)
bodyTEXT
IDXstatusENUM(draft,published)
views_countINT DEFAULT 0
created_atTIMESTAMP
updated_atTIMESTAMP
deleted_atTIMESTAMP NULL

↳ belongs to users

index: user_id, status, slug (unique)

comments

PKidBIGINT
FKpost_idBIGINT
FKuser_idBIGINT
bodyTEXT
created_atTIMESTAMP
updated_atTIMESTAMP

↳ belongs to posts

↳ belongs to users

index: post_id, user_id

notifications

PKidBIGINT
FKuser_idBIGINT
typeVARCHAR(60)
dataJSON
IDXread_atTIMESTAMP NULL
created_atTIMESTAMP

↳ belongs to users

index: user_id, read_at

followers

PKidBIGINT
FKfollower_idBIGINT
FKfollowing_idBIGINT
created_atTIMESTAMP

↳ belongs to users

index: follower_id + following_id (unique), following_id

reactions

PKidBIGINT
FKuser_idBIGINT
IDXreactable_typeVARCHAR(60)
IDXreactable_idBIGINT
typeVARCHAR(20)
created_atTIMESTAMP
updated_atTIMESTAMP

↳ belongs to users

index: user_id + reactable_type + reactable_id (unique)

device_tokens

PKidBIGINT
FKuser_idBIGINT
UQtokenVARCHAR(255)
platformENUM(web,android,ios)
created_atTIMESTAMP
updated_atTIMESTAMP

↳ belongs to users

index: user_id, token (unique)

Indexing

Index every foreign key and any column you filter or sort by often.

Normalization

Don't duplicate data — link with foreign keys. Denormalize only for proven hot paths.

Performance

Use the smallest correct types, paginate big lists, and avoid N+1 queries with eager loading.

Production schema conventions

Soft deletes

Add deleted_at to user-facing content (posts, accounts) so a delete is recoverable — avoid hard deletes.

Slugs for public URLs

Give public/SEO content a unique slug (posts, products, categories) for clean, shareable URLs.

Pivot tables

Model many-to-many with a pivot (followers, taggables) + a composite UNIQUE so links can't duplicate.

Polymorphic relations

Reuse one table across many parents with a (type + id) pair — reactable, taggable. Index the pair together.

Index every FK

Index all foreign keys and any column you filter, sort or join on — it's the cheapest big win.

Consistent basics

BIGINT UNSIGNED primary keys, timestamps on every table, and status/enum columns with an index.

Get the schema right before you code

Your database schema is the skeleton of your app — get it wrong and every feature you build on top inherits the pain. A clean schema, on the other hand, makes the rest of the project feel easy. This database schema planner gives you a solid first draft: the tables your features imply, sensible fields and data types, primary and foreign keys, a relationship (ERD) map, and the indexes that keep queries fast. Treat it as a starting blueprint, then tune it to your exact needs.

Common questions

How do I design a database schema?

Start from your features. Each 'thing' your app tracks — users, posts, orders, payments — usually becomes a table. Give each a primary key, link related tables with foreign keys, pick the smallest correct data types, and index anything you'll search or join on. This planner does that first draft for you based on your project type and features.

What are PK, FK, UQ and IDX?

PK = Primary Key (the unique id of a row). FK = Foreign Key (a link to another table's id). UQ = Unique (no duplicates allowed, like email). IDX = Index (makes lookups on that column fast). Getting these right early saves painful migrations later.

What are pivot tables and polymorphic relations?

A pivot table links two tables in a many-to-many relationship — like 'followers' (follower_id, following_id) or 'taggables'. Always add a composite UNIQUE so the same link can't be inserted twice. A polymorphic relation lets one table attach to many parents using a (type + id) pair — e.g. a 'reactions' table whose reactable_type/reactable_id can point to a post OR a comment. Index that pair together. These two patterns keep real social and content apps clean and DRY.

Should I really delete rows, or use soft deletes?

For anything a user can create — posts, comments, accounts — prefer soft deletes: add a deleted_at column and hide rows instead of removing them. It saves you from accidental data loss, keeps references intact, and lets users (or admins) restore. Hard-delete only throwaway or sensitive data. This is standard in production Laravel apps.

MySQL or PostgreSQL?

Both are excellent and this schema works on either. MySQL is simple and everywhere; PostgreSQL shines for complex queries, JSON and data integrity. For ERP, marketplaces and analytics-heavy apps, lean PostgreSQL. For most other apps, either is a fine choice.

All free tools
Programmer HasanProgrammer Hasan

Full-Stack Software Engineer building scalable web, mobile & AI-powered applications. Alhamdulillah for everything!

Get in touch

© 2026 Programmer Hasan. All rights reserved.

Designed & developed by Programmer Hasan