A database designed for the product
Tables, constraints and indexes shaped around the queries the product actually runs, with migrations in the repository and backups from day one.
- PostgreSQL
- MySQL
- Migrations
Full-stack development · web apps and APIs
I'm Sanjeev Singh, a full-stack developer building software since 2016 and working remotely for a US company since 2020. I build the whole web app, from the PostgreSQL schema and the Node.js or Laravel API to the React front end and deployment. One person owns every layer, so nothing falls between handoffs. Fixed quote within 24 hours.

01The usual problem
Every feature needs three people to agree, and every bug belongs to someone else.
Sound familiar? Tick what applies to you.
02What you get
Tables, constraints and indexes shaped around the queries the product actually runs, with migrations in the repository and backups from day one.
Validation, permissions and business logic on the server, with clear errors, so every client gets the same answers.
Fast, accessible screens with real loading and error states, built against the API it was designed with.
Payments, email, WhatsApp, file storage and third-party APIs, wired in with retries and logging.
Tests and automated deploys, so shipping a fix takes minutes and can be rolled back.
Hashed passwords, permissions checked on the server, secrets out of the browser and inputs validated at every boundary.
03What changes
One developer who can answer any question about your app.
04Overview
A full-stack developer builds every layer of a web application: the database that stores the data, the API that applies the business rules, the front end people use, and the deployment that keeps it running. Sanjeev Singh is a senior full-stack developer who has been building software since 2016 and has worked remotely for a US company since 2020. Every project starts with a fixed written quote within 24 hours.
Hiring one full-stack developer instead of separate front-end and back-end developers removes the handoff in the middle. The person who designs the database also writes the screen that reads it, so a new feature doesn't wait for two people to agree on an API, and a bug doesn't bounce between teams. It also means one person can explain the whole system to you in plain words.
Sanjeev's main stack is PostgreSQL, Node.js with Express or Laravel for the API, and React or Next.js for the front end, deployed with automated tests and backups. He also integrates the services most products need: Stripe and Razorpay payments, transactional email, WhatsApp messaging and background job queues. When a product needs a mobile app, he can build it in Flutter against the same API.
His proof is CRUD Review, which he architected from an empty repository and then led as a team of four to five: one Node.js and PostgreSQL API behind a Next.js web app, an admin console and apps on both stores. He also built Dominique Bakers alone, an online ordering system with delivery, pickup and dine-in, Razorpay payments, WhatsApp notifications and one admin panel, in Laravel and React.
You can hire him for a new web app, for a feature on an existing one, or as the full-stack developer on your team for a few months. Taking over a codebase someone else started always begins with a written review of the code, the database and the hosting, so you know what's risky before anything changes.
He works with companies in the US, the UK, the UAE and Australia, with hours that overlap the US morning and the full UK working day. You get an NDA if you want one, a contract that assigns all code to your company, and milestone payments in US dollars.
05The blueprint
This is what a typical web app I build looks like. Click any part to see what I do there, and watch how a request travels through it.
Browser
Where your users are. Pages load fast, work with a keyboard and screen reader, and show clear loading and error states.
Mobile app
When the product needs one, a Flutter app for iOS and Android that uses the same API, so the rules live in one place.
Flutter
CDN & edge
Static files and cached pages served close to users, with HTTPS, compression and security headers set once.
Auth
Sign-up, login, sessions, password reset and roles. Permissions are checked on the server for every request, never only in the browser.
CI & deploys
Every change runs the tests and deploys automatically, with a way to roll back in one step.
GitHub Actions
Front end
The screens, built in React or Next.js against the API they were designed with, so the data shape never surprises either side.
ReactNext.js
API
Validation, permissions and business rules in one place. Every client, web or mobile, gets the same answers and the same errors.
Node.jsLaravel
Cache
Expensive reads cached with clear expiry rules, added only where measurements show a page needs it.
PostgreSQL
Tables, constraints and indexes designed around real queries, with migrations in the repository and tested backups.
Job queue
Emails, imports, exports and webhooks processed in the background with retries, so a slow task never slows a page.
Payments
Checkout, subscriptions and refunds, with signed webhooks that are safe to receive twice.
StripeRazorpay
Email & WhatsApp
Receipts, invites and alerts sent from the queue through your email provider or the WhatsApp Business API.
Browser
Where your users are. Pages load fast, work with a keyboard and screen reader, and show clear loading and error states.
Mobile app
When the product needs one, a Flutter app for iOS and Android that uses the same API, so the rules live in one place.
CDN & edge
Static files and cached pages served close to users, with HTTPS, compression and security headers set once.
Auth
Sign-up, login, sessions, password reset and roles. Permissions are checked on the server for every request, never only in the browser.
CI & deploys
Every change runs the tests and deploys automatically, with a way to roll back in one step.
Front end
The screens, built in React or Next.js against the API they were designed with, so the data shape never surprises either side.
API
Validation, permissions and business rules in one place. Every client, web or mobile, gets the same answers and the same errors.
Cache
Expensive reads cached with clear expiry rules, added only where measurements show a page needs it.
PostgreSQL
Tables, constraints and indexes designed around real queries, with migrations in the repository and tested backups.
Job queue
Emails, imports, exports and webhooks processed in the background with retries, so a slow task never slows a page.
Payments
Checkout, subscriptions and refunds, with signed webhooks that are safe to receive twice.
Email & WhatsApp
Receipts, invites and alerts sent from the queue through your email provider or the WhatsApp Business API.
06Every layer
"Let owners invite a teammate" sounds like one task. It touches four layers. Here is that feature as I would ship it, so you can see what full-stack actually means.
migrations/007_invites.sqlsql
create table invites (
id uuid primary key default gen_random_uuid(),
team_id uuid not null references teams (id) on delete cascade,
email text not null,
token_hash bytea not null unique, -- the token itself is only ever emailed
expires_at timestamptz not null default now() + interval '7 days',
accepted_at timestamptz
);
-- One open invite per person per team, whatever the email's capitalisation.
create unique index invites_one_open
on invites (team_id, lower(email))
where accepted_at is null;Why:The database enforces the rules too. Only a hash of the invite token is stored, invites expire on their own, and the same person can't have two open invites to one team.
src/routes/invites.tsts
import { randomBytes, createHash } from 'node:crypto';
import { z } from 'zod';
const Invite = z.object({ email: z.string().trim().email().max(254) });
// requireRole checks the signed-in user is an owner of this team.
router.post('/teams/:teamId/invites', requireRole('owner'), async (req, res) => {
const parsed = Invite.safeParse(req.body);
if (!parsed.success) return res.status(422).json({ error: 'Enter a valid email address.' });
const token = randomBytes(32).toString('base64url');
const tokenHash = createHash('sha256').update(token).digest();
try {
await db.query(
'insert into invites (team_id, email, token_hash) values ($1, $2, $3)',
[req.params.teamId, parsed.data.email, tokenHash],
);
} catch (err: any) {
if (err.code === '23505') return res.status(409).json({ error: 'That person already has an invite.' });
throw err;
}
await queue.add('send-invite', { email: parsed.data.email, token });
res.status(201).end();
});Why:Input is validated at the boundary, only owners can invite, and the database's uniqueness rule becomes a clear 409 instead of a 500. The email is sent from the queue, so the request returns at once.
app/team/InviteForm.tsxtsx
'use client';
import { useState } from 'react';
type State = { status: 'idle' | 'sending' | 'sent' | 'error'; message?: string };
export function InviteForm({ teamId }: { teamId: string }) {
const [state, setState] = useState<State>({ status: 'idle' });
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const email = new FormData(e.currentTarget).get('email');
setState({ status: 'sending' });
const res = await fetch(`/api/teams/${teamId}/invites`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});
if (res.ok) return setState({ status: 'sent', message: 'Invite sent.' });
const { error } = await res.json().catch(() => ({ error: 'Something went wrong. Try again.' }));
setState({ status: 'error', message: error });
}
return (
<form onSubmit={onSubmit}>
<label htmlFor="invite-email">Teammate's email</label>
<input id="invite-email" name="email" type="email" required autoComplete="off" />
<button disabled={state.status === 'sending'}>
{state.status === 'sending' ? 'Sending…' : 'Send invite'}
</button>
<p role="status">{state.message}</p>
</form>
);
}Why:The screen shows the API's own error messages, disables the button while sending and announces the result to screen readers, so the feature works for everyone, not just on the happy path.
test/invites.test.tsts
import { test } from 'node:test';
import assert from 'node:assert/strict';
import request from 'supertest';
import { app } from '../src/app';
import { loginAs } from './helpers';
test('owners can invite, and a duplicate gets a clear 409', async () => {
const owner = await loginAs('owner');
const invite = () =>
request(app)
.post(`/teams/${owner.teamId}/invites`)
.set('Cookie', owner.cookie)
.send({ email: '[email protected]' });
assert.equal((await invite()).status, 201);
assert.equal((await invite()).status, 409);
});
test('members cannot invite', async () => {
const member = await loginAs('member');
const res = await request(app)
.post(`/teams/${member.teamId}/invites`)
.set('Cookie', member.cookie)
.send({ email: '[email protected]' });
assert.equal(res.status, 403);
});Why:The tests hit the real API and database, so they prove the whole feature works, including who is allowed to use it, before it ships.
07Ways to work together
Database, API, front end and deployment for a new product or internal tool, with a fixed quote.
A new API, or your app connected to payments, CRMs, email, WhatsApp or another company's system.
A few months on your codebase, in your stand-ups and your Git workflow, shipping features end to end.
A written review of code someone else started, then the risky parts fixed first while the product keeps running.
Slow pages and timeouts traced to the query, index or N+1 that causes them, then fixed and measured.
Automated tests and one-click deploys added to a project that ships by hand today.
08Proof
09In their words
Rated on Fiverr
5.0
23 reviewsRead them
Sanjeev brought extraordinary technical knowledge to the project. He communicated professionally, shared clear updates, discussed every hurdle openly and always found a practical way forward. I was very happy working with him.
Sanjeev has been instrumental across several projects. He understands the vision from very little direction, then brings commitment, technical skill and original thinking to turn it into a finished product.
Great job.
Working with Sanjeev was a great experience.
01 / 04
10Your time, my time
Your time · London
--:--
My time · Lucknow, India
--:--
Your office hours (9 AM–6 PM)When I'm on calls and chat
11Your options
Who owns a feature
Working with meMe, from the table to the button
When a bug appears
Working with meOne person traces it end to end
Explaining the system
Working with meOne person who knows all of it
Who owns the code
Working with meYou, with every login, from day one
Starting
Working with meDays, after a fixed quote
| Working with me | Separate front & back-end devs | A large agency | |
|---|---|---|---|
| Who owns a feature | Me, from the table to the button | Two people and a handoff | A team, through a project manager |
| When a bug appears | One person traces it end to end | It bounces between front and back end | A ticket in a queue |
| Explaining the system | One person who knows all of it | Each knows half | Documentation, if it exists |
| Who owns the code | You, with every login, from day one | You | Sometimes the agency |
| Starting | Days, after a fixed quote | Two hires to make | Weeks of discovery |
12Free guide
A good full-stack developer can explain every layer of what they built, in plain words. These questions show you quickly whether they can.
| One full-stack developer | Separate front-end and back-end developers | |
|---|---|---|
| Features | Shipped end to end by one person | Split, with an API agreed in the middle |
| Bugs | Traced through every layer by one person | Often passed between the two |
| Best for | New products, internal tools, small teams | Large products with deep specialist needs |
| Watch out for | Make sure they’re strong in every layer | Handoff delays and unclear ownership |
Planning something new? Read choosing the right stack and 12 questions to ask before hiring a developer.
13How we work
A short call on goals, audience and what success looks like.
Scope, timeline and price agreed in writing before work starts.
Working pages shared early on a staging link, refined with your feedback.
Go-live, speed and SEO checks, then handover. You own the code, domain and accounts.
14Questions
A full-stack developer builds every layer of a web app, from the database and the API to the front end and the deployment. Because one person owns all of it, features ship without a handoff in the middle.
PostgreSQL and MySQL, Node.js with Express or Laravel for APIs, React and Next.js for front ends, and Flutter when a mobile app is needed. I also integrate Stripe, Razorpay, transactional email and the WhatsApp Business API.
It depends on the scope, or for ongoing work, on the hours each month. I send a fixed written quote within 24 hours of a short call, so the price is agreed before any work starts.
Yes. I can work on your codebase for a few months, in your stand-ups, your Git workflow and your review process, shipping features end to end alongside your developers.
Yes. I start with a written review of the code, the database and the hosting, fix the risky parts first and document as I go, without stopping the product for the people already using it.
I work Monday to Saturday, 10 AM to 11 PM India time. That covers the whole UK working day, the US East Coast morning, the UAE day and the Australian afternoon, and my page shows the live overlap for your city.
I sign an NDA before the first detailed call if you want one. The contract assigns all code to your company once paid, and the repository and hosting are in your name.
I invoice in US dollars, wherever you are, against milestones agreed in the quote, or monthly for ongoing work. You don't pay for a stage until you've seen it working.
Question not here? Ask me directly.
Ask on WhatsAppLast updated
Ready when you are
Chat with Sanjeev
Tap a question or send your own.