Naystack: Next.js Full-Stack Toolkit

1.7.26 · active · verified Wed Apr 22

Naystack is a full-stack utility library for Next.js applications, currently at version 1.7.26. It provides pre-built solutions for common web development challenges, including robust email-based authentication (with optional Google/Instagram OAuth), GraphQL API scaffolding, and file upload functionalities. The library is designed to be modular, allowing developers to integrate specific features as needed, and follows a 'bring-your-own database' philosophy, demonstrating integration with ORMs like Drizzle ORM in its examples. Naystack is actively maintained and focuses on providing a cohesive, opinionated yet flexible framework for server-side API routes and client-side React components within the Next.js ecosystem. Its key differentiator is providing integrated, end-to-end solutions for authentication, GraphQL, and file management without dictating the database layer.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates the core Naystack setup for authentication, combining server-side route handlers with client-side context providers and hooks. It simulates a basic Drizzle ORM integration for the server logic and shows how to wrap a Next.js application with `AuthWrapper` and `ApolloWrapper`, then conditionally render UI based on authentication state using `useToken()`.

import { setupEmailAuth } from 'naystack/auth';
import { AuthWrapper, useToken } from 'naystack/auth/client';
import { ApolloWrapper } from 'naystack/graphql/client';
import React from 'react';

// --- Simulate a Drizzle ORM setup for the server-side --- 
// In a real app, this would be your database connection and schema
const db = { 
  select: () => ({ from: () => ({ where: () => ([{ id: 'user-id-123', password: 'hashed-password' }]) }) }),
  insert: () => ({ values: () => ({ returning: () => ([{ id: 'new-user-id', password: 'new-hashed-password' }]) }) })
};
const UserTable = { id: 'id', email: 'email', password: 'password' };
const eq = (a: any, b: any) => ({}); // Dummy eq function

// --- Server-side (app/api/(auth)/email/route.ts) ---
// Ensure process.env.SIGNING_KEY and process.env.REFRESH_KEY are set
export const { GET, POST, PUT, DELETE } = setupEmailAuth({
  getUser: async ({ email }) => {
    // Replace with your actual database query
    const [user] = await db.select({ id: UserTable.id, password: UserTable.password }).from(UserTable).where(eq(UserTable.email, email));
    return user;
  },
  createUser: async (data) => {
    // Replace with your actual database insertion
    const [user] = await db.insert(UserTable).values(data).returning({ id: UserTable.id, password: UserTable.password });
    return user;
  },
  onSignUp: async (userId, body) => {
    console.log(`User ${userId} signed up.`);
  }
});

// --- Client-side (app/layout.tsx) ---
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AuthWrapper>
          <ApolloWrapper>
            {children}
          </ApolloWrapper>
        </AuthWrapper>
      </body>
    </html>
  );
}

// --- Client-side (app/page.tsx or any client component) ---
'use client';

function DashboardButton() {
  const token = useToken();

  return (
    <div>
      {token ? (
        <button>Go to Dashboard (Logged In)</button>
      ) : (
        <button>Sign Up / Login (Logged Out)</button>
      )}
      <p>Current Token: {token ? 'Present' : 'Not Present'}</p>
    </div>
  );
}

// To make the client component render within the quickstart context
export { DashboardButton };

view raw JSON →