DocsContributingCoding Conventions

Coding Conventions

Code style, naming patterns, and best practices for Reqcore development. Covers Vue components, API routes, Drizzle queries, and more.

Coding Conventions

This guide covers the coding standards and patterns used throughout Reqcore. Following these conventions ensures consistency and prevents common mistakes.

TypeScript

Reqcore is fully typed. Nuxt 4 provides separate TypeScript contexts for app/ and server/:

  • Server utilities are not available in app/ code (and vice versa)
  • Use shared/ for types and functions needed in both contexts
  • API response types are inferred automatically by useFetch

Vue Components

File Organization

<script setup lang="ts">
// 1. Imports (framework, libraries, types)
import { Briefcase, ArrowRight } from 'lucide-vue-next'

// 2. Page meta & SEO
definePageMeta({ layout: 'dashboard', middleware: ['auth'] })
useSeoMeta({ title: 'Jobs', description: '...' })

// 3. Reactive state & composables
const { data: jobs } = await useFetch('/api/jobs', {
  headers: useRequestHeaders(['cookie']),
})

// 4. Computed properties
const openJobs = computed(() =>
  jobs.value?.filter(j => j.status === 'open') ?? []
)

// 5. Methods
function handleCreate() { /* ... */ }
</script>

<template>
  <!-- Component template -->
</template>

Naming

ItemConventionExample
ComponentsPascalCasePipelineCard.vue, OrgSwitcher.vue
ComposablescamelCase with use prefixuseJobs(), useCandidate()
Pageskebab-caseapplication-form.vue
Layoutskebab-casedashboard.vue

API Routes

File Naming

server/api/jobs/index.get.ts     → GET /api/jobs
server/api/jobs/index.post.ts    → POST /api/jobs
server/api/jobs/[id].get.ts      → GET /api/jobs/:id
server/api/jobs/[id].patch.ts    → PATCH /api/jobs/:id
server/api/jobs/[id].delete.ts   → DELETE /api/jobs/:id

Handler Pattern

export default defineEventHandler(async (event) => {
  // 1. Authenticate
  const { session } = await requireAuth(event)
  const orgId = session.activeOrganizationId

  // 2. Validate input
  const body = await readValidatedBody(event, schema.parse)

  // 3. Query (always scoped by orgId)
  const result = await db
    .select()
    .from(table)
    .where(eq(table.organizationId, orgId))

  // 4. Return data
  return result
})

Error Handling

// ✅ Correct — throws proper HTTP error
throw createError({ statusCode: 404, message: 'Job not found' })

// ❌ Wrong — returns 200 with error body
return { error: 'Job not found' }

Database Queries

Always Scope by Organization

// ✅ Every query MUST include organizationId
const jobs = await db
  .select()
  .from(job)
  .where(eq(job.organizationId, orgId))

// ❌ Never query without org scope
const jobs = await db.select().from(job)

Use Drizzle Operators

import { eq, and, desc } from 'drizzle-orm'

const results = await db
  .select()
  .from(application)
  .where(
    and(
      eq(application.organizationId, orgId),
      eq(application.jobId, jobId),
    )
  )
  .orderBy(desc(application.createdAt))

CSS & Styling

Tailwind CSS v4

  • Use utility classes — avoid custom CSS
  • Theme tokens are defined in app/assets/css/main.css via @theme
  • The brand-* color scale maps to the primary color

Dark Theme Pages

Public pages (landing, blog, roadmap, docs) use a dark theme:

useHead({
  bodyAttrs: {
    style: 'background-color: #09090b;',
  },
})

Common dark theme patterns:

  • Background: bg-[#09090b]
  • Text: text-white, text-white/50, text-white/40
  • Borders: border-white/[0.06]
  • Cards: bg-white/[0.02], hover: bg-white/[0.04]
  • Glow: blur-[120px] with brand colors

Environment Variables

// ✅ Use the validated env utility
import { env } from '~/server/utils/env'
const dbUrl = env.DATABASE_URL

// ❌ Never access process.env directly in server code
const dbUrl = process.env.DATABASE_URL

Next Steps