Development Setup
Set up your local environment to contribute to Discord Forum API.
Prerequisites
| Tool | Version | Install |
|---|---|---|
| Node.js | 20.x+ | nodejs.org |
| pnpm | 9.x+ | npm install -g pnpm |
| Git | Any | git-scm.com |
| Discord Bot | - | Discord Setup |
Quick Setup
-
Fork the repository
Click “Fork” on GitHub
-
Clone your fork
Terminal window git clone https://github.com/YOUR_USERNAME/discord-forum-api.gitcd discord-forum-api -
Install dependencies
Terminal window pnpm install -
Configure environment
Terminal window cp .env.example .env# Edit .env with your Discord credentials -
Initialize database
Terminal window pnpm db:push -
Start development
Terminal window pnpm dev
Project Structure
discord-forum-api/├── packages/│ ├── api/ # REST API (Hono.js)│ │ ├── src/│ │ │ ├── routes/ # API endpoints│ │ │ ├── middleware/│ │ │ └── lib/│ │ └── package.json│ ││ ├── bot/ # Discord bot (discord.js)│ │ ├── src/│ │ │ ├── events/ # Discord event handlers│ │ │ ├── commands/ # Slash commands│ │ │ ├── sync/ # Sync logic│ │ │ └── lib/│ │ └── package.json│ ││ ├── db/ # Database (Drizzle ORM)│ │ ├── src/│ │ │ ├── schema.ts # Database schema│ │ │ ├── client.ts # DB client│ │ │ └── helpers.ts│ │ └── package.json│ ││ └── docs/ # Documentation (Starlight)│ ├── src/│ │ └── content/docs/│ └── package.json│├── docs/ # Legacy markdown docs├── turbo.json # Turborepo config├── tsconfig.json # Base TypeScript config└── package.json # Root workspaceDevelopment Commands
All Packages
| Command | Description |
|---|---|
pnpm dev | Start all packages in watch mode |
pnpm build | Build all packages |
pnpm lint | Run ESLint |
pnpm lint:fix | Fix auto-fixable lint issues |
pnpm typecheck | Run TypeScript type checking |
pnpm test | Run all tests |
Database
| Command | Description |
|---|---|
pnpm db:push | Push schema to database |
pnpm db:generate | Generate migrations |
pnpm db:migrate | Run migrations |
pnpm db:studio | Open Drizzle Studio GUI |
Documentation
| Command | Description |
|---|---|
pnpm docs:dev | Start docs dev server |
pnpm docs:build | Build docs site |
pnpm docs:preview | Preview built docs |
Single Package
# Run command in specific packagepnpm --filter @discolink/api devpnpm --filter @discolink/bot buildpnpm --filter @discolink/db testIDE Setup
VS Code
Recommended extensions:
{ "recommendations": [ "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "astro-build.astro-vscode", "bradlc.vscode-tailwindcss" ]}Settings:
{ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "typescript.tsdk": "node_modules/typescript/lib"}Other Editors
- WebStorm: Built-in TypeScript support
- Neovim: Use
typescript-language-server - Sublime Text: LSP + TypeScript plugin
Testing
Run All Tests
pnpm testRun Specific Tests
# By packagepnpm --filter @discolink/api test
# By patternpnpm test -- --grep "threads"
# Watch modepnpm test -- --watchTest Coverage
pnpm test:covWriting Tests
Tests use Vitest. Example:
import { describe, it, expect, beforeEach } from 'vitest';import { app } from '../index';
describe('GET /api/threads', () => { it('returns threads list', async () => { const res = await app.request('/api/threads?serverId=123'); expect(res.status).toBe(200);
const data = await res.json(); expect(data).toHaveProperty('threads'); expect(Array.isArray(data.threads)).toBe(true); });
it('requires serverId parameter', async () => { const res = await app.request('/api/threads'); expect(res.status).toBe(400); });});Database Development
Drizzle Studio
Explore your database visually:
pnpm db:studioOpens at http://localhost:4983
Schema Changes
-
Modify
packages/db/src/schema.ts -
Generate migration:
Terminal window pnpm db:generate -
Review generated SQL in
packages/db/drizzle/ -
Apply migration:
Terminal window pnpm db:migrateOr for development, push directly:
Terminal window pnpm db:push
Seeding Data
Create test data for development:
import { db } from './client';import { servers, threads, messages } from './schema';
async function seed() { // Insert test server await db.insert(servers).values({ id: '123456789', name: 'Test Server', icon: null, });
// Insert test thread await db.insert(threads).values({ id: '111222333', serverId: '123456789', title: 'Test Thread', // ... });
console.log('Seed complete!');}
seed();Run with:
pnpm --filter @discolink/db tsx src/seed.tsEnvironment Variables
Required
DISCORD_TOKEN=your_bot_tokenDISCORD_CLIENT_ID=your_client_idOptional
# Database (default: sqlite)DATABASE_TYPE=sqliteDATABASE_PATH=./data/discord-forum.db
# For TursoDATABASE_TYPE=tursoTURSO_DATABASE_URL=libsql://...TURSO_AUTH_TOKEN=...
# APIAPI_PORT=3000CORS_ORIGIN=http://localhost:3000
# DevelopmentLOG_LEVEL=debugSYNC_BOT_MESSAGES=trueDebugging
VS Code Debugger
Create .vscode/launch.json:
{ "version": "0.2.0", "configurations": [ { "name": "Debug API", "type": "node", "request": "launch", "runtimeExecutable": "pnpm", "runtimeArgs": ["--filter", "@discolink/api", "dev"], "cwd": "${workspaceFolder}", "console": "integratedTerminal" }, { "name": "Debug Bot", "type": "node", "request": "launch", "runtimeExecutable": "pnpm", "runtimeArgs": ["--filter", "@discolink/bot", "dev"], "cwd": "${workspaceFolder}", "console": "integratedTerminal" } ]}Console Logging
// Use structured loggingconsole.log('[DEBUG]', { action: 'sync', threadId: '123' });Inspecting Requests
# Use curl to test APIcurl -v http://localhost:3000/api/threads?serverId=123
# Use httpie for nicer outputhttp localhost:3000/api/threads serverId==123Common Tasks
Adding a New Endpoint
-
Create route file:
packages/api/src/routes/my-route.ts import { Hono } from 'hono';export const myRoute = new Hono();myRoute.get('/', async (c) => {return c.json({ hello: 'world' });}); -
Register in main app:
packages/api/src/index.ts import { myRoute } from './routes/my-route';app.route('/api/my-route', myRoute); -
Add tests:
packages/api/src/routes/my-route.test.ts
Adding a Discord Event Handler
-
Create event file:
packages/bot/src/events/myEvent.ts import { Events } from 'discord.js';import type { Client } from 'discord.js';export function register(client: Client) {client.on(Events.MessageCreate, async (message) => {// Handle event});} -
Register in bot:
packages/bot/src/index.ts import * as myEvent from './events/myEvent';myEvent.register(client);
Troubleshooting
”Module not found” Errors
# Rebuild packagespnpm build
# Or clean and reinstallrm -rf node_modules packages/*/node_modulespnpm installTypeScript Errors
# Check types across all packagespnpm typecheckDatabase Issues
# Reset database (development only!)rm -f data/discord-forum.dbpnpm db:pushBot Not Connecting
- Verify token in
.env - Check intents in Discord Developer Portal
- Review console output for errors