Overview
This guide establishes coding standards for the Go React Scaffold project. Following these guidelines ensures consistency, maintainability, and code quality across the codebase.
Refer to AGENTS.md in the repository for the complete coding guidelines.
TypeScript / React (Frontend)
Imports
Organization:
// 1. React and core libraries
import React , { useState , useEffect } from 'react' ;
// 2. Third-party libraries
import axios from 'axios' ;
// 3. Local imports
import { UserProfile } from './components/UserProfile' ;
import { api } from './utils/api' ;
Group imports logically: React/core libraries first, then external packages, then local imports.
Syntax:
Use ES modules (import syntax)
No default path aliases configured (use relative paths)
Style Rules:
// 2-space indentation
function LoginForm () {
const [ email , setEmail ] = useState ( '' );
return (
< form >
< input type = 'email' value = { email } />
</ form >
);
}
// Single quotes for strings
const message = 'Hello, world!' ;
// Semicolons required
const count = 42 ;
Key rules:
2-space indentation (no tabs)
Single quotes for strings
Semicolons required
No Prettier config (maintain consistency manually)
TypeScript Types
Strict Mode Enabled:
The project uses strict TypeScript configuration:
{
"compilerOptions" : {
"strict" : true ,
"noUnusedLocals" : true ,
"noUnusedParameters" : true ,
"noFallthroughCasesInSwitch" : true
}
}
Type Best Practices:
// Explicit return types for non-obvious functions
function calculateTotal ( items : Item []) : number {
return items . reduce (( sum , item ) => sum + item . price , 0 );
}
// Interface for objects
interface User {
id : string ;
email : string ;
createdAt : Date ;
}
// Type for unions
type Status = 'pending' | 'active' | 'inactive' ;
Use explicit return types for functions when the return type is not immediately obvious from the implementation.
Naming Conventions
Element Convention Example Components PascalCase UserProfile.tsxFunctions camelCase getUserData()Variables camelCase userEmailConstants UPPER_SNAKE_CASE API_BASE_URLFiles (Components) PascalCase LoginForm.tsxFiles (Utilities) camelCase apiClient.ts
React Patterns
Functional Components:
// Use functional components with hooks
function UserProfile ({ userId } : { userId : string }) {
const [ user , setUser ] = useState < User | null >( null );
useEffect (() => {
fetchUser ( userId ). then ( setUser );
}, [ userId ]);
return < div >{user?. name } </ div > ;
}
Best practices:
Use functional components with hooks (no class components)
Destructure props in function parameters
Prefer const over let
Use useState, useEffect, and other React hooks
TailwindCSS Usage
Utility-First Approach:
// Use Tailwind classes directly
function Button ({ children } : { children : React . ReactNode }) {
return (
< button className = 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded' >
{ children }
</ button >
);
}
Guidelines:
Utility-first approach with Tailwind classes
Use App.css for component-specific styles when needed
Use @apply sparingly (prefer utility classes)
Error Handling
Safe Property Access:
// Always check for null/undefined
if ( user ) {
console . log ( user . email );
}
// Use optional chaining
const email = user ?. profile ?. email ;
// Handle fetch errors
try {
const response = await fetch ( '/api/users' );
if ( ! response . ok ) {
throw new Error ( 'Failed to fetch users' );
}
const data = await response . json ();
} catch ( error ) {
console . error ( 'Error fetching users:' , error );
}
Always check for null/undefined before accessing properties to prevent runtime errors.
Go (Backend)
Imports
Import Organization:
package main
import (
// 1. Standard library
" context "
" fmt "
" log "
" net/http "
// 2. External packages
" github.com/labstack/echo/v4 "
" go.mongodb.org/mongo-driver/mongo "
// 3. Local packages
" backend/auth "
" backend/configs "
)
Guidelines:
Group imports: standard library, external packages, local packages
Use blank imports only when necessary (e.g., _ "github.com/joho/godotenv/autoload")
Local import path: backend/<package>
Standard Go Formatting:
# Format code before committing
gofmt -w .
# Organize imports
goimports -w .
Run gofmt -w . before every commit to ensure consistent formatting.
Rules:
Standard Go formatting (gofmt)
Use goimports to organize imports
Tabs for indentation (Go standard)
Naming Conventions
Element Convention Example Packages lowercase, single word auth, configsExported functions/types PascalCase ConnectDB, UserUnexported functions camelCase processUserLoginAcronyms All caps ID, URI, APIFiles lowercase, underscores ok auth.go, user_model.go
Examples:
package auth
// Exported function - PascalCase
func GenerateToken ( userID string ) ( string , error ) {
// ...
}
// Unexported function - camelCase
func validatePassword ( password string ) error {
// ...
}
// Exported type - PascalCase
type User struct {
ID string `json:"id" bson:"_id"`
Email string `json:"email" bson:"email"`
}
// Acronyms - all caps
const APIURL = "https://api.example.com"
Types and Structs
Struct Tags:
type User struct {
ID primitive . ObjectID `json:"id" bson:"_id,omitempty"`
Email string `json:"email" bson:"email"`
Password string `json:"-" bson:"password"` // Never expose in JSON
CreatedAt time . Time `json:"created_at" bson:"created_at"`
}
Guidelines:
Use structs with tags for JSON/BSON/validation
Tag format: `json:"field" bson:"field"`
Prefer explicit types over interface{} or any
Error Handling
Always Check Errors:
// Check every error
user , err := getUserByEmail ( email )
if err != nil {
return err
}
// Wrap errors with context
if err := db . Insert ( user ); err != nil {
return fmt . Errorf ( "failed to insert user: %w " , err )
}
Error Handling Rules:
Always check errors: if err != nil { return err }
Wrap errors with context: fmt.Errorf("context: %w", err)
Use log.Fatalf() only in main or init, not in libraries
Return errors to caller - don’t log and swallow
Never ignore errors. Always check and handle them appropriately.
Project Structure
Package Organization:
backend/
├── main.go # Application entry point
├── auth/ # Auth package
│ ├── auth.go # JWT token handling
│ ├── controller.go # Business logic
│ ├── routes.go # HTTP handlers
│ └── configs.go # Configuration
├── users/
│ └── model.go # User data model
└── configs/
├── db.go # Database connection
└── env.go # Environment variables
Structure Rules:
One package per directory
Separate concerns:
Handlers in routes.go
Business logic in controller.go
Models in model.go
Main package in main.go at root
Database (MongoDB)
Context Usage:
// Use context with timeout (preferred)
ctx , cancel := context . WithTimeout ( context . Background (), 10 * time . Second )
defer cancel ()
result , err := collection . FindOne ( ctx , filter ). Decode ( & user )
if err != nil {
return fmt . Errorf ( "failed to find user: %w " , err )
}
// context.TODO() is used in the current codebase
// Update to context.WithTimeout() for production
MongoDB Best Practices:
Use context with timeouts (prefer context.WithTimeout() over context.TODO())
Handle connection errors at startup
Use bson.M{} for queries
Close connections properly with deferred disconnect
Query Safety:
// Safe: Use typed queries
filter := bson . M { "email" : email }
result := collection . FindOne ( ctx , filter )
// Unsafe: Don't interpolate user input directly
// filter := bson.M{"$where": userInput} // ❌ NoSQL injection risk
HTTP (Echo Framework)
Handler Pattern:
// Handler signature
func LoginHandler ( c echo . Context ) error {
// 1. Validate input
var loginReq LoginRequest
if err := c . Bind ( & loginReq ); err != nil {
return c . JSON ( http . StatusBadRequest , map [ string ] string {
"error" : "Invalid request" ,
})
}
// 2. Process request
token , err := authenticateUser ( loginReq . Email , loginReq . Password )
if err != nil {
return c . JSON ( http . StatusUnauthorized , map [ string ] string {
"error" : "Invalid credentials" ,
})
}
// 3. Return JSON response
return c . JSON ( http . StatusOK , map [ string ] string {
"token" : token ,
})
}
Echo Guidelines:
Use middleware for CORS, logging, recovery
Handler signature: func(c echo.Context) error
Return JSON responses: c.JSON(status, data)
Validate inputs before processing
Testing (Future Implementation)
When adding tests:
Go Tests:
// File: auth_test.go
package auth
import " testing "
func TestGenerateToken ( t * testing . T ) {
token , err := GenerateToken ( "user123" )
if err != nil {
t . Errorf ( "GenerateToken failed: %v " , err )
}
if token == "" {
t . Error ( "Expected non-empty token" )
}
}
Run tests:
# Run all tests
go test ./... -v
# Run specific test
go test ./auth -run TestGenerateToken -v
TypeScript Tests:
// Use Vitest or Jest
import { describe , it , expect } from 'vitest' ;
describe ( 'UserProfile' , () => {
it ( 'renders user name' , () => {
// Test implementation
});
});
Security Standards
Never commit or log sensitive data:
Never log or expose secrets, API keys, or passwords
Hash passwords with bcrypt (cost 14 is used)
Validate all user inputs
Use HTTPS in production
Set secure session cookies
Sanitize MongoDB queries to prevent injection
Pre-Commit Checklist
Before committing code:
Backend:
cd backend
gofmt -w . # Format code
make test # Run tests
Frontend:
cd frontend
npm run lint # Run ESLint
Both:
Quick Reference
Frontend
Indentation : 2 spaces
Quotes : Single quotes
Semicolons : Required
Components : PascalCase
Functions : camelCase
Backend
Formatting : gofmt -w .
Imports : Standard, external, local
Exported : PascalCase
Unexported : camelCase
Error handling : Always check
Next Steps
Contributing Learn how to contribute to the project
Project Structure Understand the codebase organization