Development Guides
Development Workflow
Daily development workflow, commands, and best practices for BISO Sites.
Development Workflow
This guide covers the day-to-day development workflow for BISO Sites, from starting the dev server to committing your changes.
Daily Workflow
Start development server
# All apps
bun run dev
# Or specific app
bun run dev --filter=webMake your changes
Edit files in your IDE. Hot reload will update automatically.
Test your changes
# Type check
bun run check-types
# Lint
bun run lint
# Build (ensure it compiles)
bun run build --filter=webCommit your changes
git add .
git commit -m "feat(web): add new feature"
git pushCommon Commands
Development
# Start all apps in development mode
bun run dev
# Start specific app
bun run dev --filter=web # Web app only
bun run dev --filter=admin # Admin app only
bun run dev --filter=docs # Docs onlyBuilding
# Build all apps
bun run build
# Build specific app
bun run build --filter=web
# Build for Appwrite deployment
cd apps/web
bun run build:appwriteQuality Checks
# Type checking
bun run check-types # All packages
cd apps/web && bun run check-types # Specific app
# Linting
bun run lint # All packages
cd apps/web && bunx eslint . # Specific app
# Formatting
bun run format # Format all filesPackage Management
# Install dependencies
bun install
# Add dependency to specific app
cd apps/web
bun add package-name
# Add dependency to package
cd packages/ui
bun add package-name
# Add dev dependency
bun add -D package-name
# Add to root (affects all)
bun add -w package-nameApplication Ports
| App | Port | URL |
|---|---|---|
| Web | 3000 | http://localhost:3000 |
| Admin | 3001 | http://localhost:3001 |
| Docs | 3002 | http://localhost:3002 |
File Structure
Creating a New Page
Web App
# Create route
apps/web/src/app/[locale]/about/page.tsx
# Create server action
apps/web/src/app/actions/about.ts
# Create components
apps/web/src/components/about/hero.tsx
apps/web/src/components/about/features.tsxAdmin App
# Protected route
apps/admin/src/app/(admin)/admin/settings/page.tsx
# Server action
apps/admin/src/app/actions/settings.tsCreating a Component
# In app
apps/web/src/components/feature/my-component.tsx
# In UI package (shared)
packages/ui/components/ui/my-component.tsxCreating a Server Action
// apps/web/src/app/actions/posts.ts
'use server';
import { createSessionClient } from '@repo/api/server';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const { db, account } = await createSessionClient();
// Verify auth
const user = await account.get();
// Create post
const post = await db.createDocument(
'database_id',
'posts',
ID.unique(),
{
title: formData.get('title'),
content: formData.get('content'),
authorId: user.$id,
}
);
revalidatePath('/posts');
return { success: true, postId: post.$id };
}Code Organization
Server Components
// app/posts/page.tsx - Server Component
import { createSessionClient } from '@repo/api/server';
export default async function PostsPage() {
// Fetch data directly
const { db } = await createSessionClient();
const posts = await db.listDocuments('database_id', 'posts');
return (
<div>
{posts.documents.map(post => (
<PostCard key={post.$id} post={post} />
))}
</div>
);
}Client Components
// components/post-form.tsx - Client Component
'use client';
import { useState } from 'react';
import { createPost } from '@/app/actions/posts';
import { Button } from '@repo/ui/components/ui/button';
export function PostForm() {
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
const formData = new FormData(e.target as HTMLFormElement);
await createPost(formData);
setLoading(false);
};
return (
<form onSubmit={handleSubmit}>
<input name="title" required />
<textarea name="content" required />
<Button type="submit" disabled={loading}>
{loading ? 'Creating...' : 'Create Post'}
</Button>
</form>
);
}Debugging
Server-Side Debugging
// Add console.log in Server Components/Actions
export default async function Page() {
console.log('Server: Rendering page');
const data = await fetchData();
console.log('Server: Data fetched', data);
return <div>...</div>;
}Output appears in terminal where bun run dev is running.
Client-Side Debugging
'use client';
export function MyComponent() {
console.log('Client: Component mounted');
return <div>...</div>;
}Output appears in browser console (F12).
React DevTools
Install browser extension:
Network Debugging
Check browser Network tab (F12) to see:
- API requests
- Response data
- Request timing
- Error responses
Environment Variables
Local Development
Create .env.local in each app:
# apps/web/.env.local
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://your-appwrite.com/v1
NEXT_PUBLIC_APPWRITE_PROJECT=your-project-id
APPWRITE_API_KEY=your-api-key
# Payment
VIPPS_CLIENT_ID=your-vipps-client-id
VIPPS_CLIENT_SECRET=your-secret
VIPPS_TEST_MODE=trueAccessing Environment Variables
// Public (client-side accessible)
const endpoint = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT;
// Server-only (never exposed to client)
const apiKey = process.env.APPWRITE_API_KEY;Common Tasks
Adding a New Feature
Plan the feature
- Identify required components
- Design data model
- Plan API calls
Create database structure (if needed)
- Add collection in Appwrite
- Update type definitions in
packages/api/types/appwrite.ts
Create components
# UI components
apps/web/src/components/feature/component.tsx
# Server actions
apps/web/src/app/actions/feature.tsCreate route
apps/web/src/app/[locale]/feature/page.tsxTest and refine
bun run dev --filter=web
# Test in browserUpdating Shared Packages
# Make changes in package
cd packages/ui
# Edit components/ui/button.tsx
# Changes are immediately available in apps (hot reload)
# No need to rebuild packageAdding Dependencies
# To specific app
cd apps/web
bun add zod
# To package
cd packages/payment
bun add @vippsmobilepay/sdk
# Workspace-wide (use sparingly)
bun add -w turboDatabase Migrations
# Update collection via Appwrite Console
# Or use Appwrite CLI
cd apps/web
appwrite deploy collectionTranslations
# Add new translation key
# apps/web/messages/en/common.json
{
"welcome": "Welcome to BISO Sites"
}
# apps/web/messages/no/common.json
{
"welcome": "Velkommen til BISO Sites"
}
# Use in components
import { useTranslations } from 'next-intl';
const t = useTranslations('common');
<h1>{t('welcome')}</h1>Git Workflow
Commit Message Format
Follow Conventional Commits:
# Format: type(scope): description
feat(web): add user profile page
fix(admin): correct form validation
docs(api): update API documentation
style(ui): improve button styling
refactor(payment): simplify checkout flow
test(web): add unit tests for utils
chore(repo): update dependenciesCommon Types
feat- New featurefix- Bug fixdocs- Documentationstyle- Formatting, missing semi-colons, etc.refactor- Code restructuringtest- Adding testschore- Maintenance tasks
Branch Strategy
# Create feature branch
git checkout -b feat/user-profile
# Make changes and commit
git add .
git commit -m "feat(web): add user profile page"
# Push branch
git push origin feat/user-profile
# Create Pull Request on GitHubTroubleshooting
Port Already in Use
# Find process on port
lsof -ti:3000
# Kill process
lsof -ti:3000 | xargs kill -9
# Or change port in package.json
{
"scripts": {
"dev": "next dev -p 3050"
}
}Module Not Found
# Reinstall dependencies
rm -rf node_modules apps/*/node_modules packages/*/node_modules
bun install
# Clear Next.js cache
rm -rf apps/web/.nextType Errors
# Generate Next.js types
cd apps/web
bunx next build --typegen-only
# Check types
bun run check-typesHot Reload Not Working
- Restart dev server
- Clear browser cache
- Check file is saved
- Ensure file is in correct directory
Performance Tips
Optimize Build Times
# Use Turbo cache
bun run dev # Automatically caches
# Build only changed apps
bun run build --filter=web --forceReduce Bundle Size
- Use dynamic imports for large components
- Optimize images with next/image
- Tree-shake unused code
- Check bundle analyzer:
cd apps/web
ANALYZE=true bun run buildBest Practices
- TypeScript Strict Mode - Fix type errors immediately
- ESLint - Run before committing
- Prettier - Format code with
bun run format - Server Components First - Default to Server Components
- Revalidate After Mutations - Use
revalidatePath() - Error Handling - Always handle errors gracefully
- Loading States - Show feedback during async operations
- Accessibility - Use semantic HTML and ARIA labels
- Mobile First - Design for mobile, enhance for desktop
- Test Locally - Test before pushing
Next Steps
ℹ️
- Creating Features - Step-by-step feature development
- Styling Guide - CSS and Tailwind patterns
- Forms Guide - Building and handling forms
- Database Guide - Working with Appwrite
