Skip to content

Storage ​

Overview ​

storage.Store is the persistence boundary for application-owned state. It is intentionally independent of Discord resources and can hold settings, moderation records, statistics, tickets, or jobs. MemoryStore provides a concurrency-safe JSON-backed implementation for tests and small processes; production adapters can implement the same four methods over SQL, Redis, or a document database.

Architecture ​

The interface is Get(ctx, key, target) error, Set(ctx, key, value) error, Delete(ctx, key) error, and Keys(ctx, prefix) ([]string, error). Values are serialized to JSON on Set and decoded from JSON on Get, which means the store keeps a snapshot rather than a pointer to mutable caller memory. ErrNotFound is returned for missing or expired records.

NewMemoryStore(ttl time.Duration) initializes an empty store. A positive TTL is applied to every record at write time. Zero or negative TTL means no expiration. Keys returns live keys whose names have the requested prefix in sorted order; it does not delete expired records as a side effect.

Quick Start ​

go
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/discord-go/discord.go/storage"
)

type Settings struct {
	Prefix string `json:"prefix"`
	Enabled bool `json:"enabled"`
}

func main() {
	ctx := context.Background()
	store := storage.NewMemoryStore(0)
	if err := store.Set(ctx, "guild:42", Settings{Prefix: "!", Enabled: true}); err != nil {
		panic(err)
	}
	var settings Settings
	if err := store.Get(ctx, "guild:42", &settings); err != nil {
		panic(err)
	}
	keys, _ := store.Keys(ctx, "guild:")
	fmt.Println(settings.Prefix, settings.Enabled, keys)
	if err := store.Get(ctx, "missing", &settings); !errors.Is(err, storage.ErrNotFound) {
		panic("expected a missing-record error")
	}
}

Creating A Store ​

The constructor is the only constructor in the package. MemoryStore.TTL is exported so it can be inspected, but changing it after records are written does not recalculate existing expiration times. A zero MemoryStore value is not initialized with a records map; use NewMemoryStore.

Using A Store ​

All methods check a non-nil context for cancellation before touching the store. They accept a nil context for compatibility, but callers should pass a real context. Set returns JSON marshal errors, Get returns JSON unmarshal errors, and Delete succeeds even when the key is absent. Keys are opaque strings; prefix matching is a simple strings.HasPrefix test.

Common Patterns ​

Namespace keys such as guild:<id>:settings and user:<id>:preferences. Store versioned structs so future migrations can distinguish old records. Use errors.Is(err, storage.ErrNotFound) rather than comparing error text.

Best Practices ​

Keep records small, avoid storing secrets without encryption, and treat writes as replacements. Use a durable implementation before relying on the store for moderation or financial decisions. Honor context cancellation in custom Store implementations just as MemoryStore does.

Common Mistakes ​

MemoryStore is not durable or distributed. Keys does not return values and does not remove expired records. Passing a non-pointer target to Get causes a JSON decoding error, and a nil target is not a useful destination.

API Walkthrough ​

The complete exported API is ErrNotFound, Store, MemoryStore, NewMemoryStore, and MemoryStore.Get, Set, Delete, and Keys.

Examples ​

The Quick Start program is runnable and demonstrates JSON snapshot semantics, prefix listing, and the package error value.

Released under the Apache License 2.0.