Skip to content

What APIs hide ​

ORMs, N+1, and "why is this endpoint slow" - the stuff that leaks through into your Network tab.

The polite translator ​

Your FE rarely speaks SQL. It speaks HTTP + JSON. Behind the controller there's often:

  1. an ORM (Prisma, Eloquent, Django ORM, SQLAlchemy, …)
  2. or a query builder
  3. or hand-written SQL (rare in startups, common in hot paths)

Same destination. Different accents.

ts
// Prisma-ish vibe
const contacts = await prisma.contact.findMany({
  where: { deletedAt: null, companyId: 1 },
  include: { company: true },
  take: 20,
})

Roughly becomes a SELECT + JOIN (or two queries). Knowing SQL means you can guess which.

N+1 - the classic FE-visible bug ​

Bad pattern on the server:

text
SELECT * FROM contacts WHERE company_id = 1;   -- 1 query
for each contact:
  SELECT * FROM companies WHERE id = ?;        -- N queries

Your contact list of 50 people → 51 round-trips. The endpoint feels mysteriously slow. The waterfall in APM tools looks like a picket fence.

Fixes you've heard in standup:

  • JOIN / include / eager load in one (or two) queries
  • dataloader-style batching
  • denormalize a display field if you must (careful)

When a list page crawls after "we added the company name to each row", ask about N+1. You'll sound dangerous in a good way.

Over-fetching and under-fetching ​

ProblemFE symptomSQL angle
Over-fetchhuge JSON, unused fieldsSELECT * / fat includes
Under-fetchmany tiny requestsmissing JOIN / include; chatty API

GraphQL and careful REST both try to land in the middle. SQL skills help you describe what you actually need.

When to peek at SQL ​

I don't think every FE should live in psql. I do think you should peek when:

  • a screen is slow and the payload isn't huge
  • a filter/sort/pagination bug smells like OFF-by-one in LIMIT/OFFSET
  • you're designing a feature and need to sketch tables with a backend person
  • an ORM error mentions a constraint you want to understand

Migrations are schema PRs ​

When someone adds deleted_at or a new deals.stage value, that's a migration - a versioned ALTER / CREATE. Reading those PRs is easier once you've written CREATE TABLE yourself.

Where to go next ​

  • Practice: break the CRM schema, add tags, add closed_at
  • Read one real migration in a work project end-to-end
  • Try the same queries on Postgres if you have it - notice what stays the same
  • Cheatsheet lives next door for the rainy day

You've got enough SQL to stop treating the database as pure mysticism. The rest is reps.

Enjoy!

Best,

-- ł.

Notes-style SQL tutorial for front-end folks