askill
backend-api

backend-apiSafety 88Repository

Build server-side APIs using Next.js Server Actions and API routes.

6 stars
1.2k downloads
Updated 1/29/2026

Package Files

Loading files...
SKILL.md

Backend API Development & Architecture

Core Philosophy

In the Modern Web (Next.js App Router), the line between frontend and backend is blurred but strict. Code co-location is key, but security boundaries are paramount. We prefer Server Actions for mutations and Server Components for data fetching.

Next.js App Router Architecture

1. Server Components vs Client Components

By default, everything in app/ is a Server Component.

FeatureServer Component (Default)Client Component ('use client')
Data Fetching✅ Direct DB / API calls (async/await)❌ Must use useEffect / SWR / React Query
Secrets✅ Can access env vars (API_KEY)❌ Exposure risk
Interactivity❌ No onClick, onChange, hooks✅ Full React interactivity
Bundle Size✅ Zero bundle size impact❌ Adds to client JS bundle

Best Practice: Keep the "Client Boundary" as low in the tree as possible. Pass Server Components as children to Client Components to prevent them from becoming Client Components themselves.

2. Data Fetching (The "Backend" in Frontend)

Fetch data directly in your async Page or Layout components.

// app/dashboard/page.jsx
import { db } from "@/lib/db";

export default async function DashboardPage() {
  // Direct DB call - safe on server
  const users = await db.user.findMany();

  return (
    <main>
      <h1>Users</h1>
      <UserList users={users} />
    </main>
  );
}

Server Actions (Mutations)

Server Actions are the standard way to handle form submissions and data mutations.

1. The Robust Pattern (Zod + Error Handling)

Always validate inputs on the server using Zod. Never trust the client.

// actions/create-post.ts
'use server'

import { z } from "zod";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";

const schema = z.object({
  title: z.string().min(5),
  content: z.string().min(10)
});

export async function createPost(prevState: any, formData: FormData) {
  const validatedFields = schema.safeParse({
    title: formData.get('title'),
    content: formData.get('content')
  });

  if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
      message: 'Missing Fields. Failed to Create Post.',
    };
  }

  try {
    await db.post.create({
      data: validatedFields.data,
    });
  } catch (error) {
    return { message: 'Database Error: Failed to Create Post.' };
  }

  revalidatePath('/dashboard/posts');
  redirect('/dashboard/posts');
}

2. Consuming Actions in Client Components

Use useFormState (or useActionState in React 19) to handle feedback.

// components/post-form.jsx
"use client";

import { useFormState } from "react-dom";
import { createPost } from "@/actions/create-post";

export function PostForm() {
  const [state, dispatch] = useFormState(createPost, {
    message: null,
    errors: {},
  });

  return (
    <form action={dispatch}>
      <input name="title" />
      {state.errors?.title && (
        <p className="text-red-500">{state.errors.title}</p>
      )}

      <button type="submit">Save</button>
      <p aria-live="polite">{state.message}</p>
    </form>
  );
}

API Routes (Route Handlers)

Use Route Handlers (app/api/.../route.js) when you need to expose endpoints to external services (webhooks, mobile apps, public API).

// app/api/webhooks/stripe/route.js
import { headers } from "next/headers";
import { NextResponse } from "next/server";

export async function POST(req) {
  const body = await req.text();
  const signature = headers().get("Stripe-Signature");

  try {
    // Verify webhook signature
    // Process event
    return NextResponse.json({ received: true });
  } catch (err) {
    return NextResponse.json({ error: "Webhook Error" }, { status: 400 });
  }
}

Database Best Practices

1. Connection Singleton (Prisma/Drizzle)

In development, Next.js hot-reloading can exhaust database connections. Use a singleton pattern.

// lib/db.ts
import { PrismaClient } from "@prisma/client";

const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };

export const db = globalForPrisma.prisma || new PrismaClient();

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db;

2. Caching Strategies

  • unstable_cache: Cache expensive DB queries by key/tag.
  • revalidateTag: Purge cache on demand (e.g., after an admin update).

Security Checklist

  1. Authentication: Use libraries like NextAuth.js (Auth.js) or Clerk.
  2. Authorization: Check permissions inside EVERY Server Action and Server Component.
    const session = await auth();
    if (!session || session.user.role !== "ADMIN") {
      throw new Error("Unauthorized");
    }
    
  3. Input Validation: Validate everything with Zod.
  4. Environment Variables: Never prefix secrets with NEXT_PUBLIC_ unless they are truly public.

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

78/100Analyzed 2/24/2026

High-quality technical reference skill covering Next.js backend development with excellent code examples and patterns. Well-structured with Server Components, Server Actions, Route Handlers, and security best practices. However, lacks an explicit "when to use" trigger section and the description is brief. Located in .agent folder suggesting project-specific internal use, which moderately reduces reusability. Despite this, the content is comprehensive and actionable for developers working with Next.js App Router.

88
88
72
80
92

Metadata

Licenseunknown
Version1.0.0
Updated1/29/2026
PublisherJaivishChauhan

Tags

apidatabasesecurity