Group Schema
Planned — not yet live. The group feature is not implemented. This schema
reflects the intended Firestore structure based on the
product doc.
Firestore Document (groups/{groupId})
| Field |
Type |
Required |
Constraints |
Description |
id |
string |
yes |
non-empty |
Unique group identifier (nanoId); matches document ID |
name |
string |
yes |
3–100 chars, non-empty trimmed |
Display name of the group |
description |
string |
yes |
non-empty trimmed |
Group description; visible to all users regardless of type |
poster |
string \| null |
no |
valid URL |
Cover image URL |
ownerId |
string |
yes |
non-empty |
UID of the group owner; changes only via ownership transfer |
adminsId |
string[] |
yes |
— |
UIDs of group admins; may be empty |
type |
"public" \| "private" |
yes |
enum |
public = listed in discovery feed; private = invite-only |
baseLocation.name |
string |
yes |
non-empty |
Human-readable city or neighbourhood name |
baseLocation.lat |
number |
yes |
−90 to 90 |
WGS-84 latitude |
baseLocation.lng |
number |
yes |
−180 to 180 |
WGS-84 longitude |
inviteCode |
string \| null |
yes |
non-empty when set |
Rotating invite code; null when invite is disabled. Embed in link as ?code={inviteCode} |
settings.requireApproval |
boolean |
yes |
— |
If true, new members require owner/admin approval |
settings.inviteEnabled |
boolean |
yes |
— |
Whether invite link and QR code are enabled |
settings.allowAdminChangeName |
boolean |
yes |
default false |
Whether admins (not just owner) can change the group name |
settings.allowAdminChangeDescription |
boolean |
yes |
default true |
Whether admins can change the group description |
settings.allowMembersToCreateRides |
boolean |
yes |
default false |
Whether any member can create rides; when false, only admins can |
archivedAt |
Timestamp \| null |
yes |
— |
Set when the group is archived; null for active groups |
deletedAt |
Timestamp \| null |
no |
— |
Set when the group is soft-deleted; triggers DeleteGroupWorkflow |
memberCount |
number |
yes |
integer ≥ 1 |
Denormalised count of current members; updated on join/leave/remove |
createdAt |
Timestamp |
yes |
server-set |
Document creation time |
updatedAt |
Timestamp |
yes |
server-set |
Last modification time; changes on group property or settings updates; does NOT change when members join or leave |
{
"id": "grp_abc123",
"name": "Bangalore Riders",
"description": "Weekend rides across Karnataka",
"poster": "https://example.com/poster.jpg",
"ownerId": "uid_owner",
"adminsId": ["uid_admin1"],
"type": "public",
"baseLocation": {
"name": "Bangalore",
"lat": 12.9716,
"lng": 77.5946,
},
"inviteCode": "x7kP2mQn", // null when inviteEnabled is false
"settings": {
"requireApproval": true,
"inviteEnabled": true,
"allowAdminChangeName": false,
"allowAdminChangeDescription": true,
"allowMembersToCreateRides": false,
},
"archivedAt": null,
"deletedAt": null,
"memberCount": 24,
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-06-01T08:00:00.000Z",
}
Sub-collections
groups/{groupId}/members/{userId}
| Field |
Type |
Required |
Constraints |
Description |
id |
string |
yes |
non-empty |
UID of the member (matches document ID) |
name |
string |
yes |
non-empty |
Display name at time of joining (denormalised) |
photoUrl |
string \| null |
no |
valid URL |
Profile photo URL at time of joining (denormalised) |
role |
"owner" \| "admin" \| "member" |
yes |
enum |
Member's role in the group |
joinedAt |
Timestamp |
yes |
server-set |
When the member joined |
createdAt |
Timestamp |
yes |
server-set |
Document creation time |
updatedAt |
Timestamp |
yes |
server-set |
Changes on role change or profile update |
{
"id": "uid_rider",
"name": "Arjun Mehta",
"photoUrl": "https://example.com/avatar.jpg",
"role": "member", // "owner" | "admin" | "member"
"joinedAt": "2025-02-01T10:00:00.000Z",
"createdAt": "2025-02-01T10:00:00.000Z",
"updatedAt": "2025-02-01T10:00:00.000Z",
}
groups/{groupId}/requests/{requestId}
Temporary records for pending join requests. Deleted when approved or rejected.
| Field |
Type |
Required |
Constraints |
Description |
id |
string |
yes |
non-empty |
Request ID (nanoId); matches document ID |
type |
"join" |
yes |
enum |
Request type; currently only "join" |
userId |
string |
yes |
non-empty |
UID of the requesting user |
name |
string |
yes |
non-empty |
Display name of the requester (denormalised) |
photoUrl |
string \| null |
no |
valid URL |
Avatar URL of the requester (denormalised) |
createdAt |
Timestamp |
yes |
server-set |
When the request was submitted |
{
"id": "req_xyz789",
"type": "join",
"userId": "uid_requester",
"name": "Priya Sharma",
"photoUrl": "https://example.com/avatar.jpg",
"createdAt": "2025-06-01T09:00:00.000Z",
}
User Membership Index
Group membership for a user is also tracked in the user's groups
sub-collection: users/{userId}/groups/{groupId} — see
User Schema.
Indexes
| Fields |
Query Type |
Reason |
type asc + archivedAt asc + baseLocation.lat asc |
Composite |
Public group discovery by proximity (active groups only) |
type asc + archivedAt asc + baseLocation.name asc |
Composite |
Public group discovery by city name search |
ownerId asc |
Single-field |
Fetch all groups owned by a user |
archivedAt asc |
Single-field |
Find groups eligible for archiving scan |
deletedAt asc |
Single-field |
Find groups pending deletion cleanup |