Your App LogoYOUR APP EXPERTYAE
    • Services
    • About
    • Portfolio
    • Blog
    • FAQ
    • Build Your App
    1. Home
    2. Blog
    3. Building an internal tool instead of buying Retool
    Tooling

    Building an internal tool instead of buying Retool

    When the build-your-own internal tool is the right call — the cost comparison most teams skip, and the surprisingly small Next.js + Postgres scaffold that replaces Retool for many use cases.

    YAEL Engineering·15 Nov 2025·8 min read·1,598 words
    On this page
    • The numbers
    • What an internal tool actually needs
    • The scaffold that replaces most Retool use
    • A list view in ~50 lines
    • Actions with audit
    • What Retool genuinely beats build-your-own at
    • The cost no one factors in: developer drag
    • What about Forest Admin / RetoolXY / Appsmith?
    • When the build-your-own approach hurts
    • What we ship as the default admin scaffold
    • FAQ
    • What if my ops team has zero engineers?
    • Can I start with build-your-own and switch to Retool later?
    • How do I do "give an ops person edit access to one specific column"?
    • What about Supabase Studio / Hasura Console?
    • How do I show charts and dashboards?
    • Should I use Forest Admin instead?
    • What about no-code tools like Bubble for internal tools?
    • Can I expose this admin to customers as a self-serve thing?

    For a single-user "admin dashboard" that touches your existing database and supports your existing ops team, building it yourself in Next.js is faster and cheaper than buying Retool. The point at which Retool genuinely beats build-your-own is when you have a non-engineering team building five or more internal tools — at that scale Retool's drag-and-drop and per-tool maintenance amortize beautifully. For most early-stage SaaS, that scale never arrives, and the Retool license becomes annual rent on a tool you could have shipped in a week. The decision isn't ideological — it's about how many internal tools you'll actually build.

    We've shipped internal tools both ways. This is the framework.

    The numbers

    Retool's published pricing (2026): ~$10/user/month for the cheapest plan that's actually production-usable. For a team of 5 ops + 2 engineers = $840/year. Plus a couple of hours of engineer time per month maintaining the Retool apps.

    Build-your-own: 2-5 days of senior engineering to ship the first version. Negligible ongoing cost. Lives in the same repo as your main app, shares the same auth, shares the same database connection.

    The crossover is roughly:

    • 1-3 internal tools needed → build your own
    • 4-10 internal tools needed → roughly even, depends on team skills
    • 10+ internal tools needed across multiple teams → Retool

    Most early-stage SaaS lands firmly in the first bucket.

    What an internal tool actually needs

    The 80/20 of internal-tool features:

    1. A list / search of records — "show me all users, filter by signup date"
    2. A detail view — "show me everything about this user"
    3. A handful of actions — "grant this user a free month," "refund this charge," "delete this account"
    4. An audit trail — who did what when
    5. Auth gating — only employees can access this

    Five things. None of them require a low-code platform.

    The scaffold that replaces most Retool use

    We build internal tools as a section of the main Next.js app under /admin. The full setup:

    src/app/admin/
    ├── layout.tsx              # auth gate + nav
    ├── page.tsx                # dashboard / quick stats
    ├── users/
    │   ├── page.tsx            # list + search
    │   ├── [id]/page.tsx       # detail view
    │   └── actions.ts          # server actions for ops
    ├── orgs/
    │   └── ...
    └── audit/
        └── page.tsx            # who did what

    Auth gate:

    ts
    // src/app/admin/layout.tsx
    import { auth } from "@/lib/auth";
    import { redirect } from "next/navigation";
    
    export default async function AdminLayout({ children }: { children: React.ReactNode }) {
      const session = await auth();
      if (!session?.user.isStaff) redirect("/");
      return <div className="admin">{children}</div>;
    }

    That's the entire access control. isStaff is a flag on the users table; only your team can flip it. If you want to be paranoid, gate it behind an additional IP allowlist or a hardware-key requirement.

    A list view in ~50 lines

    tsx
    // src/app/admin/users/page.tsx
    import Link from "next/link";
    import { db } from "@/lib/db";
    
    export default async function AdminUsersPage({
      searchParams,
    }: {
      searchParams: Promise<{ q?: string }>;
    }) {
      const { q } = await searchParams;
      const users = await db.users.findMany({
        where: q ? { email: { contains: q, mode: "insensitive" } } : undefined,
        orderBy: { createdAt: "desc" },
        take: 100,
      });
      return (
        <div className="p-6">
          <form>
            <input name="q" defaultValue={q} placeholder="Search by email" />
          </form>
          <table className="mt-4 w-full">
            <thead><tr><th>Email</th><th>Plan</th><th>Joined</th></tr></thead>
            <tbody>
              {users.map((u) => (
                <tr key={u.id}>
                  <td><Link href={`/admin/users/${u.id}`}>{u.email}</Link></td>
                  <td>{u.plan}</td>
                  <td>{u.createdAt.toISOString().slice(0, 10)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      );
    }

    A search-as-you-type version takes one more useDebounce hook. A pagination version takes 10 more lines. A CSV export takes an API route. None of this is hard.

    Actions with audit

    The pattern for an action — say, "grant this user a free month":

    ts
    // src/app/admin/users/actions.ts
    "use server";
    import { auth } from "@/lib/auth";
    import { db } from "@/lib/db";
    
    export async function grantFreeMonth(userId: string) {
      const session = await auth();
      if (!session?.user.isStaff) throw new Error("unauthorized");
    
      await db.$transaction(async (tx) => {
        await tx.subscriptions.update({
          where: { userId },
          data: { trialEnd: addDays(new Date(), 30) },
        });
        await tx.auditEvents.create({
          data: {
            actorId: session.user.id,
            kind: "grant_free_month",
            targetType: "user",
            targetId: userId,
            details: { days: 30 },
          },
        });
      });
    
      revalidatePath(`/admin/users/${userId}`);
    }

    Every action follows the same pattern: auth check, transactional change, audit log row. After 10 actions you have a clear pattern and the next one takes 10 minutes.

    What Retool genuinely beats build-your-own at

    Honest list:

    • Non-engineers building tools. A finance person who wants to build a dashboard without filing a ticket. Retool wins.
    • Many small one-off queries. "Show me this thing for this customer." Spinning up a Retool app for each is faster than coding routes for each.
    • Built-in connectors. Retool talks to Stripe, Salesforce, Snowflake, Segment, your warehouse, your task queue, all in their UI. Wiring those up in Next.js takes more code.
    • A unified UI across many tools. All tools have a similar look without you designing one.
    • Permissions across many tools. Retool's RBAC is more flexible than what you'd ship hand-rolled.

    If those bullets describe your team, Retool is worth it. Don't be ideological.

    The cost no one factors in: developer drag

    A subtle reason build-your-own often wins for engineering-led teams: the cost of context-switching out of your codebase to a low-code tool.

    When the admin dashboard lives in /admin of your main app, an engineer fixing a customer issue can:

    1. Read the bug report
    2. Open the admin tool to look at the customer's data
    3. Spot the bug
    4. Open the source code (same project, same IDE)
    5. Fix it
    6. Ship

    When the admin dashboard lives in Retool, step 4 requires opening a different tab and a different mental model. Multiply by every ops-driven bug and you've paid a real cost.

    What about Forest Admin / RetoolXY / Appsmith?

    Same trade-offs as Retool. Forest Admin is the closest to "free admin panel for your DB" but the customization story gets bumpy when your model is non-trivial. Appsmith is open-source self-host (no license fee) but you're now operating a separate service.

    The pattern: any tool that adds a "second source of truth" or "second runtime" for your app is incurring a cost. Sometimes the cost is worth it.

    When the build-your-own approach hurts

    I'll be fair to Retool. The cases where the home-grown version goes wrong:

    • One engineer leaves and no one knows the admin code. Internal tools rot fastest because no one is monitoring them. Fix: include them in tests, even shallow ones.
    • The schema changes and the admin breaks silently. Foreign key renames, column drops. Strong CI helps.
    • A non-engineer asks for a new admin feature and waits two weeks. Fix: pair an ops person with an engineer for a day, ship a batch of small features at once.

    These are real but manageable. Retool's marketing argues they prevent these by making non-engineers self-serve. In practice we've seen Retool apps rot just as fast — orphaned dashboards no one maintains, queries that broke after a schema change three months ago and no one noticed.

    What we ship as the default admin scaffold

    For most customer engagements:

    1. /admin/* routes in the same Next.js app, gated by isStaff
    2. Users / orgs / subscriptions list + detail views
    3. Common actions: impersonate user, refund charge, grant plan, force re-verify email
    4. Audit log table + viewer
    5. A few "ops queries" surfaced — "users who churned in the last 7 days," "subscriptions that failed payment"

    That's a one-week build at week 4 of a SaaS MVP — see how to build a SaaS MVP in 6 weeks. It saves quarters of "can you run this SQL for me" Slack messages.

    Need an admin panel that doesn't bleed your roadmap?

    We build internal tools and ops dashboards in the same Next.js stack as your main app — fast to ship, cheap to maintain.

    See automation service

    FAQ

    What if my ops team has zero engineers?

    Then Retool is genuinely the right call. The whole point of low-code is to lower the bar. Don't fight it.

    Can I start with build-your-own and switch to Retool later?

    Yes — and you might. The investment in /admin isn't wasted; you can keep the most-used routes and migrate the long-tail to Retool. We've seen this exact pattern.

    How do I do "give an ops person edit access to one specific column"?

    In Retool: their RBAC. In build-your-own: code the form to only show that column. Trade-off is real, but for the typical case (5-10 ops actions) coding it is faster than wiring Retool's RBAC.

    What about Supabase Studio / Hasura Console?

    Useful for direct database editing for engineers. Not a replacement for an admin tool aimed at ops. Different layer.

    How do I show charts and dashboards?

    Postgres queries + a chart library (recharts or tremor). About a day of work for a comprehensive metrics dashboard. Significantly less than the Retool-equivalent.

    Should I use Forest Admin instead?

    Forest is a fine middle ground if you want a generated admin from your schema with light customization. We default to hand-rolled because we usually want significant customization and the generated UI breaks once you stray.

    What about no-code tools like Bubble for internal tools?

    Bubble is aimed at building products, not admin. Wrong fit.

    Can I expose this admin to customers as a self-serve thing?

    Yes — and that's the natural next step. Many "internal admin" features become "customer self-serve" features over time. Building them in your main codebase makes that transition seamless.

    TagsInternal toolsRetoolAdminNext.jsProductivity
    ServiceSaaS DevelopmentAutomation Scripts
    PreviousChoosing a vector database: pgvector vs Pinecone vs Qdrant

    Keep reading

    ToolingTypeScript monorepo: pnpm + Turborepo tradeoffs in 2026When a monorepo helps, when it hurts, and the pnpm + Turborepo + Changesets stack we ship by default — including the parts that hurt at scale.7 min readArchitectureNext.js App Router: server actions vs API routes — when to pick eachWhen server actions are the right call, when API routes still win, and the production patterns we use on every Next.js build.8 min readSaaSHow to build a SaaS MVP in 6 weeks (without a rewrite later)A six-week SaaS MVP plan that doesn't trade speed for technical debt — auth, billing, multi-tenancy, and a real operator dashboard from day one.10 min read
    On this page
    • The numbers
    • What an internal tool actually needs
    • The scaffold that replaces most Retool use
    • A list view in ~50 lines
    • Actions with audit
    • What Retool genuinely beats build-your-own at
    • The cost no one factors in: developer drag
    • What about Forest Admin / RetoolXY / Appsmith?
    • When the build-your-own approach hurts
    • What we ship as the default admin scaffold
    • FAQ
    • What if my ops team has zero engineers?
    • Can I start with build-your-own and switch to Retool later?
    • How do I do "give an ops person edit access to one specific column"?
    • What about Supabase Studio / Hasura Console?
    • How do I show charts and dashboards?
    • Should I use Forest Admin instead?
    • What about no-code tools like Bubble for internal tools?
    • Can I expose this admin to customers as a self-serve thing?

    YOUR APP EXPERT LTD

    71-75 Shelton Street, LONDON WC2H 9JQ, UK

    +44 20 1234 5678

    [email protected]

    Quick Links

    • Services
    • About Us
    • Portfolio
    • Blog
    • Contact

    Stay Connected

    Newsletter

    Stay updated with our latest innovations and insights.

    © 2026 YOUR APP EXPERT LTD. All rights reserved.

    Engineering the Future of Technology