Skip to content

Commit

Permalink
improving docs
Browse files Browse the repository at this point in the history
  • Loading branch information
nicobistolfi committed Sep 3, 2024
1 parent 89727e8 commit 21b7fae
Show file tree
Hide file tree
Showing 6 changed files with 359 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/docs/auth/_category_.yml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
label: Authentication
position: 4
138 changes: 138 additions & 0 deletions docs/docs/cors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
---
title: "CORS"
sidebar_position: 5
---

# CORS Middleware Configuration and Usage

This document explains how to configure and use the CORS (Cross-Origin Resource Sharing) middleware in the Go REST API Boilerplate project. The middleware is implemented using the `gin-contrib/cors` package and is designed to be flexible and configurable.

## Overview

The CORS middleware allows you to control which origins can access your API. It's crucial for securing your API while still allowing legitimate cross-origin requests.

## Configuration

The middleware is configured in the `CORSMiddleware()` function, which returns a `gin.HandlerFunc`. Here's how it works:

1. **Default Configuration**: It starts with the default CORS configuration.

2. **Allowed Origins**:
- The middleware checks for an environment variable `ALLOWED_ORIGINS`.
- If set, it uses these origins as the allowed origins.
- If not set, it allows any origin that starts with `http://localhost` or `https://localhost`.

3. **Allowed Methods**: The middleware allows the following HTTP methods:
- GET
- POST
- PUT
- PATCH
- DELETE
- OPTIONS

4. **Allowed Headers**: The middleware allows the following headers:
- Origin
- Content-Type
- Accept
- Authorization

## Usage

To use this middleware in your Gin application, follow these steps:

1. Import the middleware package:

```go
import "go-rest-api/internal/api/middleware"
```

2. Add the middleware to your Gin router:

```go
func main() {
router := gin.Default()

// Apply the CORS middleware
router.Use(middleware.CORSMiddleware())

// Your routes go here
// ...

router.Run()
}
```

## Configuration via Environment Variables

To configure allowed origins using environment variables:

1. Set the `ALLOWED_ORIGINS` environment variable before running your application. Multiple origins should be comma-separated.

Example:
```bash
export ALLOWED_ORIGINS="https://example.com,https://api.example.com"
```

2. If `ALLOWED_ORIGINS` is not set, the middleware will default to allowing any origin that starts with `http://localhost` or `https://localhost`.

## Customization

If you need to customize the CORS settings further:

1. Modify the `CORSMiddleware()` function in the middleware package.
2. You can adjust allowed methods, headers, or add more sophisticated origin checking logic.

Example of adding a custom header:

```go
config.AllowHeaders = append(config.AllowHeaders, "X-Custom-Header")
```

## Security Considerations

1. **Restrict Origins**: In production, always set `ALLOWED_ORIGINS` to a specific list of trusted domains.
2. **Least Privilege**: Only expose the methods and headers that your API actually needs.
3. **Credentials**: If your API requires credentials (cookies, HTTP authentication), you may need to set `config.AllowCredentials = true`. Use this with caution and ensure `AllowOrigins` is not set to `*`.

## Troubleshooting

If you're experiencing CORS issues:

1. Check that the `ALLOWED_ORIGINS` environment variable is set correctly.
2. Ensure that the origin making the request matches exactly with one of the allowed origins (including the protocol, `http://` or `https://`).
3. Verify that the request is using an allowed method and only includes allowed headers.

## Example

Here's a complete example of setting up a Gin router with the CORS middleware:

```go
package main

import (
"github.com/gin-gonic/gin"
"go-rest-api/internal/api/middleware"
)

func main() {
// Set up Gin
router := gin.Default()

// Apply CORS middleware
router.Use(middleware.CORSMiddleware())

// Define a route
router.GET("/api/data", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "This is CORS-enabled data",
})
})

// Run the server
router.Run(":8080")
}
```

In this example, the CORS middleware will be applied to all routes. The allowed origins will be determined by the `ALLOWED_ORIGINS` environment variable, or default to localhost if not set.

By following these guidelines, you can effectively implement and customize CORS in your Go REST API, ensuring that your API is accessible to the intended clients while maintaining security.
2 changes: 1 addition & 1 deletion docs/docs/deployments/_category_.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
label: Deployments
label: Deployments
2 changes: 1 addition & 1 deletion docs/docs/deployments/docker.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 1
---

# Docker Deployment
Expand Down
152 changes: 152 additions & 0 deletions docs/docs/logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---
title: "Logging"
sidebar_position: 3
---

# Logging

This document explains how to use the custom logger implemented in the Go REST API Boilerplate project. The logger is a singleton wrapper around the `zap` logging library, providing a simple and consistent way to log messages across your application.

## Overview

The logger package provides a global `Log` variable of type `*Logger`, which is a wrapper around `zap.Logger`. It's designed as a singleton to ensure that only one instance of the logger is created and used throughout the application.

## Initialization

Before using the logger, you need to initialize it. This is typically done at the start of your application:

```go
package main

import (
logger "go-rest-api/pkg"
)

func main() {
logger.Init()
// Rest of your application code
}
```

The `Init()` function uses a `sync.Once` to ensure that the logger is only initialized once, even if `Init()` is called multiple times.

## Basic Usage

After initialization, you can use the logger throughout your application. The package provides several logging methods:

### Logging at Different Levels

```go
logger.Info("This is an info message")
logger.Error("This is an error message")
logger.Fatal("This is a fatal message") // This will also call os.Exit(1)
```

### Logging with Additional Fields

You can add structured fields to your log messages:

```go
logger.Info("User logged in", zap.String("username", "john_doe"), zap.Int("user_id", 12345))
```


### Creating a Logger with Preset Fields

If you want to create a logger with some fields already set (useful for adding context to all logs from a particular component):

```go
componentLogger := logger.With(zap.String("component", "auth_service"))
componentLogger.Info("Starting authentication")
```

## Advanced Usage

### Direct Access to Underlying Zap Logger

If you need access to the underlying `zap.Logger` for advanced use cases:

```go
zapLogger := logger.Log.Logger
// Use zapLogger for zap-specific functionality
```

### Custom Log Fields

You can create custom log fields using `zap.Field` constructors:

```go
customField := zap.Any("custom_data", someComplexStruct)
logger.Info("Message with custom data", customField)
```

## Best Practices

1. **Initialization**: Always call `logger.Init()` at the start of your application.

2. **Log Levels**: Use appropriate log levels:
- `Info` for general information
- `Error` for error conditions
- `Fatal` for unrecoverable errors (use sparingly as it terminates the program)

3. **Structured Logging**: Prefer using structured fields over string interpolation for better searchability and analysis of logs.

4. **Context**: Use `logger.With()` to add context to logs from specific components or request handlers.

5. **Performance**: The logger is designed to be performant, but avoid excessive logging in hot paths.

## Customization

The logger is initialized with a production configuration. If you need to customize the logger (e.g., for development environments), you can modify the `Init()` function in `logger.go`.

## Examples

### In HTTP Handlers

```go
func UserHandler(w http.ResponseWriter, r *http.Request) {
userID := getUserIDFromRequest(r)
requestLogger := logger.With(zap.String("handler", "UserHandler"), zap.Int("user_id", userID))

requestLogger.Info("Processing user request")

// Handler logic...

if err != nil {
requestLogger.Error("Failed to process user request", zap.Error(err))
// Handle error...
}

requestLogger.Info("User request processed successfully")
}
```

### In Services

```go
type AuthService struct {
logger *logger.Logger
}

func NewAuthService() *AuthService {
return &AuthService{
logger: logger.With(zap.String("service", "auth")),
}
}

func (s *AuthService) Authenticate(username, password string) error {
s.logger.Info("Attempting authentication", zap.String("username", username))

// Authentication logic...

if authFailed {
s.logger.Error("Authentication failed", zap.String("username", username))
return ErrAuthFailed
}

s.logger.Info("Authentication successful", zap.String("username", username))
return nil
}
```

By following these guidelines and examples, you can effectively use the logger throughout your application to produce consistent, structured logs that will aid in monitoring, debugging, and maintaining your Go REST API.
66 changes: 66 additions & 0 deletions pkg/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package logger

import (
"os"
"sync"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

var (
Log *Logger
once sync.Once
)

type Logger struct {
*zap.Logger
}

func Init() {
once.Do(func() {
config := zap.NewProductionConfig()
config.EncoderConfig.TimeKey = "timestamp"
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder

logger, err := config.Build()
if err != nil {
panic(err)
}

Log = &Logger{logger}
})
}

func (l *Logger) Info(msg string, fields ...zap.Field) {
l.Logger.Info(msg, fields...)
}

func (l *Logger) Error(msg string, fields ...zap.Field) {
l.Logger.Error(msg, fields...)
}

func (l *Logger) Fatal(msg string, fields ...zap.Field) {
l.Logger.Fatal(msg, fields...)
os.Exit(1)
}

func (l *Logger) With(fields ...zap.Field) *Logger {
return &Logger{l.Logger.With(fields...)}
}

func Info(msg string, fields ...zap.Field) {
Log.Info(msg, fields...)
}

func Error(msg string, fields ...zap.Field) {
Log.Error(msg, fields...)
}

func Fatal(msg string, fields ...zap.Field) {
Log.Fatal(msg, fields...)
}

func With(fields ...zap.Field) *Logger {
return Log.With(fields...)
}

0 comments on commit 21b7fae

Please sign in to comment.