Project

Instagram-Style Backend API

Social Media Backend API

No frontend ships with this project on purpose. It's a social media backend API, the part that stores posts, tracks who follows whom, and works out what actually lands in someone's feed. Built on NestJS, Prisma, and PostgreSQL, it exposes everything through GraphQL rather than REST. And the feed itself doesn't just show the newest post first. It ranks content with a hotScore that weighs popularity against age.

You can check the live demo or look through the source code directly. This sits alongside other backend work in the project portfolio, and it's a solid example of what a NestJS GraphQL API looks like once real social features get layered on top.

BackendNestJSGraphQLPrismaPostgreSQLJWT
Social media backend API built with NestJS and GraphQL, showing the GraphQL schema explorer
Social Media Backend API

Project Overview

This social media API includes the pieces you'd expect from an Instagram-style backend: posts, likes, follows, notifications, and authentication. Everything runs through a single GraphQL endpoint, so a client asks for exactly the fields it needs instead of getting stuck with whatever shape a REST endpoint happens to return.

The feed is where most of the actual thought went. Sorting purely by newest post is the obvious approach, and it's also the weakest one, since a post from sixty seconds ago would outrank something genuinely popular from earlier that day. hotScore blends engagement with how recently something was posted instead, so posts people actually reacted to stay visible for a while before naturally fading as newer content takes over.

Under the hood, this is a modular monolith: separate modules for separate features, shipped as one deployable app rather than a scattering of microservices.

My Role

  • Designed the GraphQL schema: types, queries, and mutations
  • Modeled the database in Prisma and PostgreSQL, including the follow relationships
  • Built the feature modules for posts, likes, follows, and notifications
  • Implemented JWT authentication and locked down the resolvers that need it
  • Wrote the hotScore ranking that orders the feed
  • Deployed the API with a public GraphQL endpoint

The Problem

A follow is just a link from one user to another, sitting inside the same table. Simple enough on paper. But a feed then has to gather posts from everyone a given user follows and put them into some sensible order, and likes and notifications stack even more relationships on top of that. So the actual engineering challenge in this social network backend wasn't any single feature. It was modeling those relationships correctly in PostgreSQL, keeping the feed query fast even as data grew, and keeping the codebase from turning into a tangle as features kept getting added.

How I Built It

NestJS made sense here because its module system tends to match how a social product actually grows over time. Posts, likes, follows, notifications, and auth each get their own module, complete with their own resolvers and services, so a change to how notifications work never accidentally touches post logic.

Prisma sits between those modules and PostgreSQL. Its schema defines users, posts, likes, follows, and notifications along with every relation connecting them, and it generates a fully typed client from that definition. Ask for a field that doesn't exist, and the build fails immediately rather than surfacing as a bug later in production.

GraphQL is the only way in. Each module contributes its own resolvers, and NestJS merges them into a single schema. Authentication runs on JWT: signing in hands back a token, and any resolver touching private data checks that token before doing anything else.

The feed query itself pulls posts from everyone a user follows, then sorts by hotScore rather than creation date. For more on how I approach this kind of relational, API-heavy backend work, take a look at my backend development skills and NestJS and Node.js skills.

Key Features

  • Posts and likes

    Users post and like content, and like counts feed straight into ranking. A like isn't just a number sitting on a post here; it actually shapes what other people see.

  • Follow relationships

    Accounts follow each other, and those links decide whose posts show up in a feed. In the database, this is a self-referencing relationship from the users table back to itself, which was one of the trickier parts to get right.

  • Ranked feed with hotScore

    Engagement and recency get weighed together, so a popular post stays visible for longer while a brand-new post with zero likes doesn't immediately vanish under everything else.

  • Notifications

    A like or a new follower writes one notification record for the affected user. The client reads that single list to show recent activity, no polling across a dozen other tables required.

  • JWT authentication

    Signing in returns a token, and it travels with every subsequent request. Resolvers touching private data check it first.

  • GraphQL schema

    One query pulls posts, their authors, and their like counts together. A REST API would typically need several round trips to assemble the same response.

Technical Architecture

  1. GraphQL client
  2. NestJS GraphQL layer
  3. Feature modules for posts, likes, follows, notifications, and auth
  4. Prisma
  5. PostgreSQL

A GraphQL query arrives at the NestJS app, gets routed to the resolver inside the right feature module, and that resolver calls its service for the real logic. Services reach PostgreSQL through Prisma. It all deploys as a single app. The boundaries between features are real and enforced, but they live inside the code rather than across separate servers, so there's no added infrastructure cost from running several services at once.

Engineering Decisions

  • Modular monolith instead of microservices

    Why
    Going with a modular monolith instead of microservices came down to how tightly these features actually depend on each other. A single like touches posts, users, and notifications all at once, and splitting those into separate services would mean adding network calls between things that genuinely belong together. NestJS modules give clear internal boundaries without that overhead,
    Trade-off
    though the cost is that everything deploys together; even a small change means redeploying the whole API. At this size, that's a far smaller price than coordinating several separate services would be.
  • GraphQL over REST

    Why
    GraphQL won out over REST largely because social data is so interconnected. A feed needs posts, authors, and like counts all at once, and REST usually forces a choice between several thin endpoints or one bloated one returning more than a client asked for. GraphQL lets the client describe exactly what it wants.
    Trade-off
    The trade-off shows up in query cost, which becomes harder to predict, and in nested queries, which can trigger repeated database lookups if resolvers aren't careful about how they fetch related data.
  • hotScore over reverse-chronological feed

    Why
    Choosing hotScore over a plain reverse-chronological feed came from a simple observation: a timeline is the easiest feed to build and the least interesting one to actually scroll through. Blending engagement with recency keeps good content visible longer while still giving new posts a fair shot at surfacing.
    Trade-off
    It does make the feed harder to reason about, though. When a post lands somewhere unexpected, you have to actually calculate its score rather than glance at a timestamp.
  • Prisma as the database layer

    Why
    Prisma earned its spot as the database layer because the schema acts as a single source of truth, and the generated client is fully typed. With relationships as tangled as follows and likes, catching a typo'd field name at compile time saves real debugging time later.
    Trade-off
    The downside is that genuinely complex queries occasionally still need raw SQL, which means giving up some fine control over exactly what runs against the database.

Challenges and Trade-offs

  • Modeling the follow relationship as a self-reference back to the users table took real care early on, since the entire feed query depends on getting that right. A structural fix later would have rippled through nearly everything downstream.
  • Nested GraphQL queries can fire off repeated database calls for related records, and that problem only becomes visible once the dataset actually grows. The fix belongs in the query layer itself, not scattered across individual resolvers trying to patch around it.
  • The hotScore formula, to be honest, is a judgment call more than a settled fact. It behaves reasonably in testing, but any ranking system like this needs ongoing tuning once real content starts moving through it and real usage patterns emerge.

Technology Stack

API

  • NestJS
  • GraphQL
  • TypeScript

Data

  • Prisma
  • PostgreSQL

Auth

  • JWT

Deployment

  • Render

Outcome

  • A working GraphQL API covering posts, likes, follows, notifications, and authentication.
  • A feed ordered by hotScore instead of a plain chronological list.
  • A relational PostgreSQL schema that actually handles follow relationships and engagement data cleanly.
  • And a public GraphQL endpoint where you can explore the schema yourself.

What I Learned

  • Database modeling has to come before API design, not run alongside it as an afterthought. The follow relationship shaped nearly every query I wrote after it, which is worth knowing before you start planning a social media database from scratch.
  • Setting up module boundaries early paid off repeatedly. Once posts, likes, and notifications each had their own space, new features stopped bleeding into code they had no business touching.
  • GraphQL trades over-fetching for a query-cost problem. That's not a one-time fix; it needs continuous attention as the schema and usage patterns evolve.

Frequently Asked Questions

  • What's this social media backend API actually built with?

    NestJS, GraphQL, Prisma, and PostgreSQL, with JWT handling authentication. It exposes a single GraphQL endpoint covering posts, likes, follows, and notifications, rather than separate REST routes for each.

  • How does the ranking algorithm decide what shows up in the feed?

    A hotScore blends engagement with how recently something was posted, so a popular post stays visible longer while brand-new content still gets a real chance to surface, instead of everything sorting purely by timestamp.

  • Why GraphQL instead of REST for a social platform?

    GraphQL lets a client ask for exactly the fields it needs in one request. For a feed, that means posts, authors, and like counts can arrive together in a single query, where REST would typically need several separate calls to build the same response.

  • Is this built as microservices or a monolith?

    It's a modular monolith. Posts, likes, follows, notifications, and auth each live in their own NestJS module, but the whole thing ships and deploys as one application rather than as separate independently-deployed services.

API work like this, spanning NestJS, GraphQL, Prisma, and PostgreSQL, makes up a large share of what I build. Look through my backend development skills.

There's more backend and full-stack work in the wider portfolio. See every project I've shipped.

Or check the backend API development services.

Open to freelance & collaborationIndia

Start a project

Let's Build Something Great Together

Have a project, product idea or development challenge? Let's discuss how I can help turn it into a reliable, scalable web application.

~/hire-jay

$

Usually replies within 24 hours

What brings you here?