agentsclimarketplace

Nextjs module

Skill Deadlymind/nanolama/skills/nextjs-module

A portable Claude Code skills library (plugin) for a Django 5.2 + Next.js 16 house stack: 30 model-agnostic, tenant-isolation-and-security-first skills.

Install
npx -y skills add Deadlymind/nanolama --skill nextjs-module

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 24 days oldThe repository was created 24 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Structures a Next.js 16 App Router frontend feature as layered modules (lib/api typed fetchers, query-keys, TanStack Query hooks, React 19 pages) fed by one shared fetch client that sends the auth cookie with credentials include, attaches the tenant header, and normalizes errors. Use when scaffolding a new frontend feature or module, building a fetch/apiClient wrapper, wiring cookie-JWT calls to the DRF backend, deciding where fetchers vs hooks vs pages live, or fixing a feature whose data layer is tangled. Not for the cookie-JWT plus CSRF auth seam itself — cookie flags, credentialed CORS, refresh rotation and logout revocation (see cookie-auth-csrf) — query cache config and useMutation invalidation depth (see react-query), form state and validation (see zod-forms), or designing the workflow and screens a module implements (see product-ux-design and visual-ui-design).

SKILL.md

7.5 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it

Next.js module (layered feature + shared fetch client)

When to use

Starting or reorganizing a frontend feature that talks to the DRF backend, or building the fetch client every feature shares. Keep each feature a thin stack of layers so pages stay declarative and the auth/tenant/error concerns live in one place.

Pattern

One feature = four layers, each depending only on the one below it:

app/(pages)  ->  hooks (useQuery/useMutation)  ->  query-keys  ->  lib/api (typed fetchers)
                                                                        |
                                                        shared fetch client (cookie + tenant + errors)
  • lib/api/<feature>.ts — typed async fetchers, the only place a URL/verb appears.
  • query-keys — one factory per feature so hooks and invalidation agree on keys.
  • hooksuseQuery/useMutation wrapping the fetchers; components import these.
  • pages — App Router Server/Client Components render hook state, no fetch inline.

Every fetcher goes through one shared client that sends the HttpOnly auth cookie (credentials: "include"), attaches the tenant header, and throws a normalized error. One vertical slice, top of stack unchanged from the client up:

// lib/api/client.ts — one shared client: cookie-JWT, tenant header, normalized errors
export class ApiError extends Error {
  constructor(public status: number, public detail: unknown) { super(`HTTP ${status}`); }
}
const UNSAFE = /^(POST|PUT|PATCH|DELETE)$/i;
const csrfToken = () =>
  decodeURIComponent(document.cookie.match(/(?:^|;\s*)csrftoken=([^;]*)/)?.[1] ?? "");

export async function apiClient<T>(path: string, init: RequestInit = {}): Promise<T> {
  const method = init.method ?? "GET";
  const isForm = init.body instanceof FormData;
  const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${path}`, {
    ...init,
    credentials: "include",              // HttpOnly cookie rides along; never read the token in JS
    headers: {
      ...(isForm ? {} : { "Content-Type": "application/json" }), // let the browser set the multipart boundary
      ...(UNSAFE.test(method) ? { "X-CSRFToken": csrfToken() } : {}), // cookie auth => CSRF-exposed
      "X-Entreprise": getTenantId(),
      ...init.headers,
    },
  });
  if (!res.ok) throw new ApiError(res.status, await res.json().catch(() => null));
  return res.status === 204 ? (undefined as T) : res.json();   // empty body on 204/DELETE
}

// lib/api/invoices.ts — typed fetchers are the only place a URL/verb appears
export const getInvoices = () => apiClient<Invoice[]>("/api/invoices/");

// query-keys — one factory so hooks and invalidation agree on keys; tenant is IN the key
export const invoiceKeys = {
  all: (t: string) => ["tenant", t, "invoices"] as const,
  list: (t: string) => [...invoiceKeys.all(t), "list"] as const,
};

// hooks — components import these, never the fetchers
export const useInvoices = () => {
  const tenantId = useUI((s) => s.activeEntrepriseId);
  return useQuery({
    queryKey: invoiceKeys.list(tenantId),   // never share a cache entry across tenants
    queryFn: getInvoices,
    enabled: !!tenantId,
  });
};

// app/invoices/page.tsx — declarative page reads hook state
"use client";
export default function InvoicesPage() {
  const { data, isPending, error } = useInvoices();   // TanStack v5: isPending, not isLoading
  if (isPending) return <Spinner />;
  if (error) return <ErrorState error={error} />;      // ApiError carries .status
  return <InvoiceTable rows={data} />;
}

Adapt to your repo

Rename the tenant header (X-Entreprise) and getTenantId() to match your backend and where the id lives (often a Zustand store — see the state pattern). Set NEXT_PUBLIC_API_URL per environment. Pick your folder convention (app/ vs a features/ directory) and hold it repo-wide. If you server-fetch in a Server Component, forward the incoming cookie header explicitly — credentials: "include" only applies to browser fetches.

Gotchas

  • The client's auth contract, in one rule: credentials: "include" on every request, plus X-CSRFToken on every unsafe method (POST/PUT/PATCH/DELETE). A 403 on a write means that header is missing — never @csrf_exempt the endpoint to silence it. See cookie-auth-csrf for why cookie auth is CSRF-exposed, the cookie flags, credentialed CORS, and refresh/logout.
  • Never hard-code Content-Type: application/json unconditionally. For a FormData body you must omit the header entirely so the browser can set multipart/form-data with its generated boundary — setting it by hand produces a body the server cannot parse.
  • Security-critical: the tenant belongs IN the query key, not only in the request header. Otherwise two tenants share one cache entry and a tenant switch serves the previous tenant's rows. Scope keys under ["tenant", tenantId, ...] and follow the switch procedure in react-query. Client-side isolation is defence-in-depth, not the boundary — the server still enforces it.
  • A silent 401 that only happens in production is almost never your fetcher — it is the cookie/CORS seam (cookie-auth-csrf). Check there before editing this layer.
  • Don't call fetch inside components or pages; that scatters auth/tenant/error logic the client exists to centralize.
  • Return undefined for 204 No Content before calling res.json(), or a DELETE mutation throws on an empty body.
  • Render loading, empty, AND error as three distinct states, not just success. A successfully-fetched empty list (data.length === 0) is not an error and not still loading — it needs its own empty-state UI (a message plus a call to action, e.g. a "Create your first invoice" button), never an infinite spinner or a blank table. So the page reads: isPending -> spinner, error -> error state, empty -> empty state, else -> the list.
  • Validate response shapes at the fetcher boundary with Zod when the payload is untrusted or drifts (see drf-zod-contract).
  • Security-critical: do authentication and tenant/authorization checks on the server — in a Server Component, route handler, or middleware — never in a Client Component. Anything a "use client" component can read is attacker-visible, so client-side gating is UX only, not a boundary (ties rbac-permissions). Keep "use client" at interactive leaves; never put secrets or tenant-authorization logic in client code. For a privileged operation, prefer a Server Action or a server-side fetch over exposing the call (and any check guarding it) to the client.

See also

  • cookie-auth-csrf
  • react-query
  • rbac-permissions
  • zod-forms
  • drf-zod-contract
  • i18n-rtl

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,970. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.