Created
January 8, 2026 14:57
-
-
Save icchan/ceb4eac748def5433b3313ce3754def7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package service | |
| import ( | |
| "context" | |
| "errors" | |
| "log" | |
| "reflect" | |
| "cloud.google.com/go/firestore" | |
| ) | |
| // FirestoreRepository defines the basic CRUD operations for Firestore collections. | |
| // It provides a generic way to perform firebase operations in a typesafe way | |
| // Instead of return objects and casting to required object you pass in the required interface and it will populate | |
| type FirestoreRepository interface { | |
| AddDocument(ctx context.Context, collection string, data interface{}) (string, error) | |
| GetDocument(ctx context.Context, collection string, docID string, dest interface{}) error | |
| UpdateDocument(ctx context.Context, collection string, docID string, updates map[string]interface{}) error | |
| DeleteDocument(ctx context.Context, collection string, docID string) error | |
| GetDocumentsByField(ctx context.Context, collection string, field string, value interface{}, dest interface{}) error | |
| GetCollection(ctx context.Context, collection string, dest interface{}) error | |
| Close() error | |
| } | |
| func NewFirestoreService(client *firestore.Client) FirestoreRepository { | |
| return &firestoreService{client: client} | |
| } | |
| // FirestoreService implements FirestoreRepository using the Firestore Go client. | |
| type firestoreService struct { | |
| client *firestore.Client | |
| } | |
| func (fs *firestoreService) AddDocument(ctx context.Context, collection string, data interface{}) (string, error) { | |
| doc, _, err := fs.client.Collection(collection).Add(ctx, data) | |
| if err != nil { | |
| return "", err | |
| } | |
| return doc.ID, nil | |
| } | |
| func (fs *firestoreService) GetDocument(ctx context.Context, collection string, docID string, dest interface{}) error { | |
| dsnap, err := fs.client.Collection(collection).Doc(docID).Get(ctx) | |
| if err != nil { | |
| return err | |
| } | |
| return dsnap.DataTo(dest) | |
| } | |
| func (fs *firestoreService) UpdateDocument(ctx context.Context, collection string, docID string, updates map[string]interface{}) error { | |
| _, err := fs.client.Collection(collection).Doc(docID).Set(ctx, updates, firestore.MergeAll) | |
| return err | |
| } | |
| func (fs *firestoreService) DeleteDocument(ctx context.Context, collection string, docID string) error { | |
| _, err := fs.client.Collection(collection).Doc(docID).Delete(ctx) | |
| return err | |
| } | |
| func (fs *firestoreService) GetDocumentsByField(ctx context.Context, collection, field string, value interface{}, dest interface{}) error { | |
| q := fs.client.Collection(collection).Where(field, "==", value) | |
| docs, err := q.Documents(ctx).GetAll() | |
| if err != nil { | |
| return err | |
| } | |
| return populateSliceFromDocs(docs, dest) | |
| } | |
| func (fs *firestoreService) GetCollection(ctx context.Context, collection string, dest interface{}) error { | |
| docs, err := fs.client.Collection(collection).Documents(ctx).GetAll() | |
| if err != nil { | |
| log.Printf("[firestoreService] Error getting collection %s: %v", collection, err) | |
| return err | |
| } | |
| log.Printf("[firestoreService] Retrieved %d documents from collection %s", len(docs), collection) | |
| return populateSliceFromDocs(docs, dest) | |
| } | |
| // Internal helper to populate a slice from Firestore documents | |
| func populateSliceFromDocs(docs []*firestore.DocumentSnapshot, dest interface{}) error { | |
| slicePtr := reflect.ValueOf(dest) | |
| if slicePtr.Kind() != reflect.Ptr || slicePtr.Elem().Kind() != reflect.Slice { | |
| return errors.New("dest must be a pointer to a slice") | |
| } | |
| sliceVal := slicePtr.Elem() | |
| elemType := sliceVal.Type().Elem() | |
| for _, doc := range docs { | |
| elemPtr := reflect.New(elemType) | |
| if err := doc.DataTo(elemPtr.Interface()); err != nil { | |
| log.Printf("[populateSliceFromDocs] Error populating element from doc %s: %v", doc.Ref.ID, err) | |
| continue | |
| } | |
| sliceVal.Set(reflect.Append(sliceVal, elemPtr.Elem())) | |
| } | |
| return nil | |
| } | |
| func (fs *firestoreService) Close() error { | |
| return fs.client.Close() | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
note to self: GetCollection does not return the docID... there may be cases where its required, so it needs to be updated