Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/platforma-dev/platforma/llms.txt

Use this file to discover all available pages before exploring further.

Platforma’s session domain provides database-backed session management with automatic expiration tracking and user association.

Overview

The session package provides:
  • Session creation and retrieval
  • User association
  • Expiration tracking
  • Database persistence
  • Integration with the auth domain

Session Model

Sessions are represented by the Session struct:
type Session struct {
	ID      string    `db:"id"      json:"id"`
	User    string    `db:"user"    json:"user"`
	Created time.Time `db:"created" json:"created"`
	Expires time.Time `db:"expires" json:"expires"`
}
Each session:
  • Has a unique ID (UUID)
  • Is associated with a user ID
  • Tracks creation time
  • Has an expiration timestamp

Checking Expiration

The Session model includes a helper method:
if session.IsExpired() {
	// Session has expired
	return errors.New("session expired")
}

Setting Up Sessions

1

Create database connection

import (
  "github.com/platforma-dev/platforma/database"
  "github.com/platforma-dev/platforma/session"
)

db, err := database.New(connectionString)
if err != nil {
  return err
}
2

Create session domain

sessionDomain := session.New(db.Connection())
The New function creates both the repository and service.
3

Register repository

db.RegisterRepository("session", sessionDomain.Repository)
This enables automatic migration of the sessions table.
4

Run migrations

if err := db.Migrate(ctx); err != nil {
  return err
}

Service Methods

The session.Service provides methods for session management:

Create Session

Create a new session manually:
import "github.com/google/uuid"

session := &session.Session{
	ID:      uuid.NewString(),
	User:    userID,
	Created: time.Now(),
	Expires: time.Now().Add(24 * time.Hour),
}

err := sessionService.Create(ctx, session)
if err != nil {
	return err
}

Create Session for User

Create a session for a user (automatically sets ID, timestamps):
sessionID, err := sessionService.CreateSessionForUser(ctx, userID)
if err != nil {
	return fmt.Errorf("failed to create session: %w", err)
}

// Use sessionID as cookie value
Default expiration is 100 days from creation:
Expires: time.Now().Add(100 * 24 * time.Hour)

Get Session

Retrieve a session by ID:
session, err := sessionService.Get(ctx, sessionID)
if err != nil {
	return err
}

fmt.Printf("Session for user: %s\n", session.User)

Get Session by User ID

Retrieve a session by user ID:
session, err := sessionService.GetByUserId(ctx, userID)
if err != nil {
	return err
}

Get User ID from Session

Extract the user ID from a session:
userID, err := sessionService.GetUserIdFromSessionId(ctx, sessionID)
if err != nil {
	return err
}
This is commonly used by the auth middleware to identify the current user.

Delete Session

Delete a specific session:
err := sessionService.DeleteSession(ctx, sessionID)
if err != nil {
	return err
}

Delete All User Sessions

Delete all sessions for a user (useful for logout from all devices):
err := sessionService.DeleteSessionsByUserId(ctx, userID)
if err != nil {
	return err
}

Integration with Auth

The session service is designed to work with the auth domain. The auth service uses it to manage authentication sessions:
// Auth service depends on session service
authService := auth.NewService(
	authRepo,
	sessionDomain.Service,  // Pass session service
	"session_id",
	nil, nil, nil,
)
The auth service uses these session methods:
  • CreateSessionForUser - When logging in
  • GetUserIdFromSessionId - In authentication middleware
  • DeleteSession - When logging out
  • DeleteSessionsByUserId - When deleting a user account

Domain Structure

The session.Domain contains both repository and service:
type Domain struct {
	Repository *Repository
	Service    *Service
}
Access them via:
sessionDomain := session.New(db.Connection())

// Access repository (for migrations)
db.RegisterRepository("session", sessionDomain.Repository)

// Access service (for session operations)
sessionID, err := sessionDomain.Service.CreateSessionForUser(ctx, userID)

Repository Implementation

The repository handles database operations:
type Repository struct {
	db db
}

func NewRepository(db db) *Repository {
	return &Repository{db: db}
}
It implements the migrator interface:
func (r *Repository) Migrations() fs.FS {
	m, _ := fs.Sub(migrations, "migrations")
	return m
}
Migrations are embedded from session/migrations/*.sql.

Database Schema

The sessions table structure:
CREATE TABLE sessions (
  id VARCHAR(255) PRIMARY KEY,
  "user" VARCHAR(255) NOT NULL,
  created TIMESTAMP NOT NULL,
  expires TIMESTAMP NOT NULL
);
Note: The user column is quoted because user is a reserved word in PostgreSQL.

Custom Expiration

To customize session expiration, create sessions manually:
// Session expires in 1 hour
session := &session.Session{
	ID:      uuid.NewString(),
	User:    userID,
	Created: time.Now(),
	Expires: time.Now().Add(1 * time.Hour),
}

err := sessionService.Create(ctx, session)
Or modify the service method (requires forking or wrapping).

Session Cleanup

Expired sessions are not automatically deleted from the database. You should periodically clean them up:
func cleanupExpiredSessions(ctx context.Context, db *sqlx.DB) error {
	query := `DELETE FROM sessions WHERE expires < NOW()`
	result, err := db.ExecContext(ctx, query)
	if err != nil {
		return err
	}
	
	rows, _ := result.RowsAffected()
	log.InfoContext(ctx, "cleaned up sessions", "deleted", rows)
	return nil
}

Session Validation

Always check session expiration when validating:
session, err := sessionService.Get(ctx, sessionID)
if err != nil {
	return nil, err
}

if session.IsExpired() {
	// Clean up expired session
	sessionService.DeleteSession(ctx, sessionID)
	return nil, errors.New("session expired")
}

// Session is valid
return session, nil

Best Practices

Set reasonable expiration

Balance security and UX: short expiration (hours/days) for sensitive apps, longer (weeks/months) for convenience.

Clean up expired sessions

Schedule periodic cleanup to prevent the sessions table from growing indefinitely.

Invalidate on logout

Always delete sessions when users explicitly log out.

Rotate session IDs

Consider rotating session IDs after privileged operations to prevent session fixation attacks.

Complete Example

package main

import (
	"context"
	"fmt"
	"github.com/platforma-dev/platforma/database"
	"github.com/platforma-dev/platforma/session"
)

func main() {
	ctx := context.Background()

	// Setup
	db, _ := database.New("postgres://localhost/myapp")
	sessionDomain := session.New(db.Connection())
	db.RegisterRepository("session", sessionDomain.Repository)
	db.Migrate(ctx)

	sessionService := sessionDomain.Service

	// Create session
	userID := "user123"
	sessionID, err := sessionService.CreateSessionForUser(ctx, userID)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Created session: %s\n", sessionID)

	// Retrieve session
	session, err := sessionService.Get(ctx, sessionID)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Session for user: %s\n", session.User)

	// Check expiration
	if session.IsExpired() {
		fmt.Println("Session expired")
	} else {
		fmt.Printf("Session valid until: %s\n", session.Expires)
	}

	// Delete session
	err = sessionService.DeleteSession(ctx, sessionID)
	if err != nil {
		panic(err)
	}
	fmt.Println("Session deleted")
}

Build docs developers (and LLMs) love