Skip to main content

Development Server

Start the Next.js development server on localhost:3000:
npm run dev
The server will:
  • Hot-reload on file changes
  • Display compilation errors in the browser
  • Show API route logs in the terminal
  • Load environment variables from .env.local
The dev server runs on port 3000 by default. To use a different port: npm run dev -- -p 3001

Available Scripts

Web Development

npm run dev
# Start Next.js dev server on localhost:3000

Mobile Development

eStory includes an Expo-based mobile app in the mobile/ directory.
npm run mobile
# Start Expo dev server
The main web app scripts (dev, build, start) must always run Next.js commands, not Expo. Mobile scripts are namespaced under mobile:*.

Testing Commands

Unit Tests (Vitest)

eStory uses Vitest for fast unit testing with React Testing Library.
npx vitest run
# Run all unit tests once (CI mode)
Test files are located in __tests__/. The test setup is in __tests__/setup.ts.

E2E Tests (Playwright)

Playwright runs end-to-end tests across Chrome, Firefox, and Safari.
npx playwright test
# Runs on port 3001 to avoid conflicts
E2E tests are in the e2e/ directory. Configure browsers and settings in playwright.config.ts.

Smart Contract Commands

Compilation

npx hardhat compile
Compiles Solidity contracts in the contracts/ directory. Generates:
  • TypeScript types
  • ABI JSON files in lib/abis/

Deployment

1

Deploy to Base Sepolia

npx hardhat run scripts/deploy.ts --network baseSepolia
This deploys all three core contracts:
  • eStoryToken.sol - ERC20 $STORY token
  • StoryProtocol.sol - Tips & paywall payments
  • StoryNFT.sol - ERC721 story book NFTs
2

Verify on Basescan

npx hardhat run scripts/verify.ts --network baseSepolia
Makes your contract source code publicly viewable on Basescan.
3

Verify ABI Matches

npx hardhat run scripts/verify-deployment.ts --network baseSepolia
Validates that deployed contracts match expected interfaces.
Contract deployment requires ADMIN_WALLET_PRIVATE_KEY and BASESCAN_API_KEY in .env.local.

Separate Contract Deployments

You can also deploy contracts individually:
npx hardhat run scripts/deployVerifiedMetrics.ts --network baseSepolia
# Old CRE metrics contract (full data on-chain)

Utility Scripts

npx ts-node scripts/backfill-metadata.ts
# Backfill AI metadata for existing stories

Development Workflow

Typical Development Session

1

Start Dev Server

npm run dev
Open http://localhost:3000
2

Make Changes

  • Edit files in app/, components/, or lib/
  • Changes hot-reload automatically
  • Check terminal for build errors
3

Test Changes

npx vitest run
Run tests to ensure nothing broke.
4

Verify Build

npm run build
Ensure production build passes before committing.

Adding a New Feature

Create a new API route in app/api/[route-name]/:
app/api/example/route.ts
import { NextRequest, NextResponse } from "next/server";
import { validateAuthOrReject, isAuthError } from "@/lib/auth";

export async function POST(req: NextRequest) {
  // Authenticate user
  const authResult = await validateAuthOrReject(req);
  if (isAuthError(authResult)) return authResult;
  const userId = authResult;

  // Your logic here
  const { data } = await req.json();

  return NextResponse.json({ success: true, data });
}
All API routes MUST use validateAuthOrReject for security.
Pages follow a two-file pattern:Server Component (app/[page]/page.tsx):
import { Metadata } from "next";
import ExamplePageClient from "./ExamplePageClient";

export const metadata: Metadata = {
  title: "Example Page",
  description: "Page description",
};

export default function ExamplePage() {
  return <ExamplePageClient />;
}
Client Component (app/[page]/ExamplePageClient.tsx):
"use client";

import { useState } from "react";

export default function ExamplePageClient() {
  const [data, setData] = useState(null);

  return (
    <div className="container mx-auto">
      {/* Your UI here */}
    </div>
  );
}
Create test file in __tests__/:
__tests__/ExamplePage.test.tsx
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import ExamplePageClient from "@/app/example/ExamplePageClient";

describe("ExamplePage", () => {
  it("renders correctly", () => {
    render(<ExamplePageClient />);
    expect(screen.getByText("Example")).toBeInTheDocument();
  });
});

Architecture Patterns

Authentication Flow

All protected API routes use the same pattern:
import { validateAuthOrReject, isAuthError } from "@/lib/auth";

export async function POST(req: NextRequest) {
  const authResult = await validateAuthOrReject(req);
  if (isAuthError(authResult)) return authResult;
  const userId = authResult;

  // Verify user owns the resource
  const resource = await getResource(resourceId);
  if (resource.user_id !== userId) {
    return NextResponse.json(
      { error: "Unauthorized" },
      { status: 403 }
    );
  }

  // Proceed with operation
}

Supabase Client Usage

eStory uses three Supabase client variants:
import { createSupabaseBrowserClient } from "@/app/utils/supabase/supabaseClient";

const supabase = createSupabaseBrowserClient();
const { data } = await supabase.from("stories").select("*");
Use in client components and browser hooks.

Web3 Hooks

eStory provides custom hooks for contract interactions:
import { useEStoryToken } from "@/app/hooks/useEStoryToken";
import { useStoryProtocol } from "@/app/hooks/useStoryProtocol";
import { useStoryNFT } from "@/app/hooks/useStoryNFT";

function Component() {
  const { balance, approve } = useEStoryToken();
  const { tipCreator, payPaywall } = useStoryProtocol();
  const { mintBook } = useStoryNFT();

  // Use hooks
}

Debugging

Browser DevTools

1

Check Console

Look for errors in the browser console (F12)
2

Network Tab

Monitor API requests and responses
3

React DevTools

Install React DevTools extension to inspect component state

Server Logs

API route logs appear in the terminal running npm run dev:
[API] POST /api/journal/save
[API] Response: 200 OK

Database Debugging

Query Supabase directly through the dashboard:
  1. Go to Table Editor to view data
  2. Use SQL Editor for custom queries
  3. Check Database > Roles for RLS policies

Performance Optimization

Bundle Analysis

Check bundle sizes after build:
npm run build
Output shows per-route sizes:
┌ ○ /                    426 kB         104 kB
├ ○ /record              467 kB         104 kB
├ ○ /library             462 kB         104 kB
└ ○ /profile             498 kB         104 kB
Keep pages under 500 kB First Load JS. The shared bundle (Web3 stack) is ~104 kB.

Bundle Budget

Current first load targets:
  • Shared (all pages): 104 kB (wagmi + viem + RainbowKit)
  • Landing: 426 kB
  • Record: 467 kB
  • Library: 462 kB
  • Profile: 498 kB
  • Social: 462 kB

Optimization Techniques

Dynamic Imports

const Heavy = dynamic(() => import("./Heavy"), {
  ssr: false,
});

Optimize Packages

Configured in next.config.mjs:
  • lucide-react
  • framer-motion
  • @radix-ui/*

Image Optimization

Use Next.js <Image> component:
<Image
  src="/hero.jpg"
  width={800}
  height={600}
  alt="Hero"
/>

Code Splitting

App Router automatically splits by route

Common Issues

  • Check import paths use @/ alias correctly
  • Verify the file exists at the specified path
  • Run npm install to ensure dependencies are installed
  • Check Row Level Security (RLS) policies
  • Verify you’re using the correct Supabase client variant
  • Ensure user is authenticated if required
  • Verify NEXT_PUBLIC_PROJECT_ID is set
  • Check MetaMask is installed and unlocked
  • Ensure you’re on the correct network (Base Sepolia)
  • Check Authorization: Bearer <token> header is present
  • Verify Supabase session is valid
  • Ensure route uses validateAuthOrReject
  • Wrap client-only code with typeof window !== 'undefined'
  • Mock browser APIs in test setup (__tests__/setup.ts)
  • Use dynamic imports with ssr: false for client-only components

Pre-Push Checklist

Before pushing code, always run:
1

Check for merge conflicts

grep -r "^<<<<<<< " --include="*.ts" --include="*.tsx" --include="*.mjs" .
Should return no results.
2

Verify package.json scripts

node -e "const p=require('./package.json'); console.log(p.scripts.build, p.scripts.dev)"
Should output:
next build next dev -p 3000
3

Run tests

npx vitest run
4

Production build

npm run build
Must pass without errors.

Next Steps

API Reference

Explore all available API endpoints

Database Schema

Learn about data models and relationships

Smart Contracts

Understand the blockchain layer

Testing Guide

Write and run tests effectively

Build docs developers (and LLMs) love