Keep/docs/api-contracts-keep-notes.md
sepehr 640fcb26f7 fix: improve note interactions and markdown LaTeX support
## Bug Fixes

### Note Card Actions
- Fix broken size change functionality (missing state declaration)
- Implement React 19 useOptimistic for instant UI feedback
- Add startTransition for non-blocking updates
- Ensure smooth animations without page refresh
- All note actions now work: pin, archive, color, size, checklist

### Markdown LaTeX Rendering
- Add remark-math and rehype-katex plugins
- Support inline equations with dollar sign syntax
- Support block equations with double dollar sign syntax
- Import KaTeX CSS for proper styling
- Equations now render correctly instead of showing raw LaTeX

## Technical Details

- Replace undefined currentNote references with optimistic state
- Add optimistic updates before server actions for instant feedback
- Use router.refresh() in transitions for smart cache invalidation
- Install remark-math, rehype-katex, and katex packages

## Testing

- Build passes successfully with no TypeScript errors
- Dev server hot-reloads changes correctly
2026-01-09 22:13:49 +01:00

9.5 KiB

API Contracts - keep-notes (Memento Web App)

Overview

The keep-notes web application exposes REST API endpoints via Next.js App Router. All endpoints return JSON responses with a consistent format.

Base URL: /api Authentication: NextAuth session-based (required for most endpoints) Response Format:

{
  "success": true|false,
  "data": any,
  "error": string  // only present when success: false
}

Notes Endpoints

GET /api/notes

Get all notes with optional filtering.

Authentication: Not required (currently)

Query Parameters:

  • archived (boolean, optional): Include archived notes. Default: false
  • search (string, optional): Search in title and content (case-insensitive)

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "id": "clxxxxxxx",
      "title": "string or null",
      "content": "string",
      "color": "default|red|orange|yellow|green|teal|blue|purple|pink|gray",
      "isPinned": boolean,
      "isArchived": boolean,
      "type": "text|checklist",
      "checkItems": [
        {
          "id": "string",
          "text": "string",
          "checked": boolean
        }
      ] | null,
      "labels": ["string"] | null,
      "images": ["string"] | null,
      "reminder": "ISO8601 datetime" | null,
      "isReminderDone": boolean,
      "reminderRecurrence": "none|daily|weekly|monthly|custom" | null,
      "reminderLocation": "string" | null,
      "isMarkdown": boolean,
      "size": "small|medium|large",
      "embedding": "JSON string" | null,
      "order": number,
      "createdAt": "ISO8601 datetime",
      "updatedAt": "ISO8601 datetime"
    }
  ]
}

Error Response (500):

{
  "success": false,
  "error": "Failed to fetch notes"
}

POST /api/notes

Create a new note.

Authentication: Not required (currently)

Request Body:

{
  "title": "string (optional)",
  "content": "string (required unless type=checklist)",
  "color": "string (optional, default: 'default')",
  "type": "text|checklist (optional, default: 'text')",
  "checkItems": [
    {
      "id": "string",
      "text": "string",
      "checked": boolean
    }
  ] (optional),
  "labels": ["string"] (optional),
  "images": ["string"] (optional, base64 encoded)
}

Response (201 Created):

{
  "success": true,
  "data": { /* note object */ }
}

Error Responses:

  • 400 Bad Request: Content is required
  • 500 Internal Server Error: Failed to create note

PUT /api/notes

Update an existing note.

Authentication: Not required (currently)

Request Body:

{
  "id": "string (required)",
  "title": "string (optional)",
  "content": "string (optional)",
  "color": "string (optional)",
  "type": "text|checklist (optional)",
  "checkItems": [...] (optional),
  "labels": ["string"] (optional),
  "isPinned": boolean (optional),
  "isArchived": boolean (optional),
  "images": ["string"] (optional)
}

Response (200 OK):

{
  "success": true,
  "data": { /* updated note object */ }
}

Error Responses:

  • 400 Bad Request: Note ID is required
  • 500 Internal Server Error: Failed to update note

DELETE /api/notes?id=xxx

Delete a note by ID.

Authentication: Not required (currently)

Query Parameters:

  • id (string, required): Note ID

Response (200 OK):

{
  "success": true,
  "message": "Note deleted successfully"
}

Error Responses:

  • 400 Bad Request: Note ID is required
  • 500 Internal Server Error: Failed to delete note

Labels Endpoints

GET /api/labels

Get all labels for the authenticated user.

Authentication: Required (NextAuth session)

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "id": "clxxxxxxx",
      "name": "string",
      "color": "red|orange|yellow|green|teal|blue|purple|pink|gray",
      "userId": "string",
      "createdAt": "ISO8601 datetime",
      "updatedAt": "ISO8601 datetime"
    }
  ]
}

Error Response (401 Unauthorized):

{
  "error": "Unauthorized"
}

POST /api/labels

Create a new label.

Authentication: Required (NextAuth session)

Request Body:

{
  "name": "string (required)",
  "color": "string (optional, random color if not provided)"
}

Response (200 OK):

{
  "success": true,
  "data": { /* label object */ }
}

Error Responses:

  • 400 Bad Request: Label name is required
  • 401 Unauthorized: Not authenticated
  • 409 Conflict: Label already exists for this user
  • 500 Internal Server Error: Failed to create label

DELETE /api/labels/{id}

Delete a label by ID.

Authentication: Required (NextAuth session)

URL Parameters:

  • id (string): Label ID

Response (200 OK):

{
  "success": true,
  "message": "Label deleted successfully"
}

Error Responses:

  • 401 Unauthorized: Not authenticated
  • 500 Internal Server Error: Failed to delete label

Authentication Endpoints

GET/POST /api/auth/[...nextauth]

NextAuth.js authentication handler.

Authentication: Not required (this is the auth endpoint)

All NextAuth operations are handled by this route:

  • Sign in
  • Sign out
  • Session management
  • OAuth callbacks
  • Email verification

Implementation: Delegates to @/auth configuration


AI Endpoints

POST /api/ai/tags

Generate intelligent tags for a note using AI.

Authentication: Not specified (likely required)

Request Body: (to be determined from implementation)

Response: (to be determined from implementation)


POST /api/ai/test

Test AI integration.

Authentication: Not specified (likely required)

Request Body: (to be determined from implementation)

Response: (to be determined from implementation)


Admin Endpoints

POST /api/admin/randomize-labels

Randomize label colors (admin functionality).

Authentication: Required (admin role)

Request Body: (to be determined from implementation)

Response: (to be determined from implementation)


POST /api/admin/sync-labels

Synchronize labels across the system (admin functionality).

Authentication: Required (admin role)

Request Body: (to be determined from implementation)

Response: (to be determined from implementation)


Upload Endpoint

POST /api/upload

Upload files (e.g., images for notes).

Authentication: Not specified

Request Body: multipart/form-data

Response: (to be determined from implementation)


Cron/Reminder Endpoint

POST /api/cron/reminders

Scheduled job for handling reminders.

Authentication: Not required (cron job)

Request Body: (to be determined from implementation)

Response: (to be determined from implementation)


Debug Endpoints

POST /api/debug/search

Debug search functionality.

Authentication: Not specified (admin/debug only)

Request Body: (to be determined from implementation)

Response: (to be determined from implementation)


Data Models

Note Model (Prisma)

model Note {
  id                  String    @id @default(cuid())
  title               String?
  content             String
  color               String    @default("default")
  isPinned            Boolean   @default(false)
  isArchived          Boolean   @default(false)
  type                String    @default("text")
  checkItems          String?   // JSON array
  labels              String?   // JSON array
  images              String?   // JSON array
  links               String?   // JSON array
  reminder            DateTime?
  isReminderDone      Boolean   @default(false)
  reminderRecurrence  String?
  reminderLocation    String?
  isMarkdown          Boolean   @default(false)
  size                String    @default("small")
  embedding           String?   // JSON vector
  userId              String?
  user                User?     @relation(fields: [userId], references: [id])
  order               Int       @default(0)
  createdAt           DateTime  @default(now())
  updatedAt           DateTime  @updatedAt

  @@index([isPinned])
  @@index([isArchived])
  @@index([order])
  @@index([reminder])
  @@index([userId])
}

Label Model (Prisma)

model Label {
  id        String   @id @default(cuid())
  name      String
  color     String   @default("gray")
  userId    String?
  user      User?    @relation(fields: [userId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@unique([name, userId])
  @@index([userId])
}

User Model (Prisma)

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String    @unique
  emailVerified DateTime?
  password      String?
  role          String    @default("USER")
  image         String?
  theme         String    @default("light")
  resetToken    String?   @unique
  resetTokenExpiry DateTime?
  accounts      Account[]
  sessions      Session[]
  notes         Note[]
  labels        Label[]
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
}

Notes

  • State Management: Server actions and API routes (no dedicated state management library detected)
  • Database: SQLite via Prisma ORM with better-sqlite3 adapter
  • Authentication: NextAuth.js v5 with Prisma adapter
  • Error Handling: All endpoints return consistent error format
  • JSON Parsing: Arrays (checkItems, labels, images) stored as JSON strings in DB, parsed in API layer
  • Ordering: Notes sorted by isPinned DESC, then order ASC, then updatedAt DESC
  • Search: Case-insensitive search across title and content fields