- Ruggine Backend
The following tables describe the database schema as inferred from the queries and DTOs throughout the codebase.
Represents a registered user.
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
integer | No | Primary key, auto-incremented |
username |
text | No | Unique login handle |
name |
text | Yes | Optional display name shown in the UI |
password_hash |
text | No | Hash of the user's password |
Represents either a group chat or a private (one-to-one) conversation.
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
integer | No | Primary key, auto-incremented |
name |
text | Yes | Display name; always NULL for private chats |
is_group |
integer | No | 1 for group chats, 0 for private chats |
created_at |
text | No | Creation timestamp in ISO 8601 format (YYYY-MM-DDTHH:MM:SS.sssZ) |
Join table that links users to chats. Each row represents a membership and tracks per-user read state.
| Column | Type | Nullable | Description |
|---|---|---|---|
user_id |
integer | No | Foreign key referencing user.id |
chat_id |
integer | No | Foreign key referencing chat.id |
join_at |
text | No | Timestamp of when the user joined the chat (ISO 8601) |
last_read_at |
text | Yes | Timestamp of the last read action by this user; used to compute unread_count in GET /api/chats |
Represents a single message sent inside a chat.
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
integer | No | Primary key, auto-incremented |
chat_id |
integer | No | Foreign key referencing chat.id |
user_id |
integer | No | Foreign key referencing user.id (the sender) |
content |
text | No | Text body of the message |
sent_at |
text | No | Send timestamp in ISO 8601 format |
Represents a pending invitation sent by a chat member to a user who is not yet in the chat.
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
integer | No | Primary key, auto-incremented |
inviter_id |
integer | No | Foreign key referencing user.id (who sent the invite) |
invitee_id |
integer | No | Foreign key referencing user.id (who received it) |
chat_id |
integer | No | Foreign key referencing chat.id |
created_at |
text | No | Timestamp of when the request was created (ISO 8601) |
A request row is deleted once the invitee accepts or declines via PATCH /api/requests/:id.
The following accounts are pre-seeded in the database for testing purposes.
| Name | Username | Password |
|---|---|---|
| Steve Jobs | stewy |
password |
| Alan Turing | turro |
password |
| Bill Gates | billy |
password |
| Edsger Dijkstra | eddy |
password |
Authenticates a user and issues a session cookie.
-
Request Body:
{ "username": "string", "password": "string" } -
Response
200 OK:{ "id": "int", "name": "string", "username": "string" }A
Set-Cookie: sid=<token>header is also returned. -
Errors:
400 Bad Request— username or password field is empty401 Unauthorized— user not found or password mismatch500 Internal Server Error— unexpected server error
Returns the currently authenticated user. Can be used as a "whoami" or session-check endpoint.
-
Auth: Required (session cookie)
-
Response
200 OK:{ "id": "int", "name": "string", "username": "string" } -
Errors:
401 Unauthorized— missing or invalid session cookie500 Internal Server Error— unexpected server error
Logs out the current user by clearing the session cookie.
-
Auth: Not strictly required (the cookie is cleared regardless)
-
Response
204 No Content
Searches for users by username or display name. The authenticated user is excluded from results.
-
Auth: Required (session cookie)
-
Query Parameters:
Parameter Type Required Default Description querystring No ""Substring to match against username or name (case-insensitive) limitint No 10Maximum number of results to return -
Response
200 OK:[ { "id": "int", "name": "string", "username": "string" } ] -
Errors:
401 Unauthorized— missing or invalid session cookie500 Internal Server Error— unexpected server error
Registers a new user and immediately creates a session cookie.
-
Auth: Not required
-
Request Body:
{ "name": "string", "username": "string", "password": "string" } -
Response
201 Created:{ "id": "int", "name": "string", "username": "string" }A
Set-Cookie: sid=<token>header is also returned. -
Errors:
400 Bad Request— name, username, or password is empty; username too short; password too short; or username already exists500 Internal Server Error— unexpected server error
Returns all chats the authenticated user belongs to, ordered by creation date (newest first). Each chat includes the last message and the count of unread messages.
-
Auth: Required (session cookie)
-
Query Parameters:
Parameter Type Required Default Description limitint No 100Maximum number of chats offsetint No 0Number of chats to skip -
Response
200 OK:[ { "id": "int", "name": "string | null", "created_at": "string (ISO 8601)", "is_group": "bool", "last_message": { "content": "string", "sent_at": "string (ISO 8601)", "sender_name": "string" }, "unread_count": "int" } ]nameisnullfor private chats that have no display name configured.last_messageisnullif no messages have been sent yet. -
Errors:
401 Unauthorized— missing or invalid session cookie500 Internal Server Error— unexpected server error
Creates a new group chat. The creator is automatically added as the first member.
-
Auth: Required (session cookie)
-
Request Body:
{ "name": "string" } -
Response
200 OK:{ "id": "int", "name": "string", "created_at": "string (ISO 8601)", "is_group": true, "last_message": null, "unread_count": 0 } -
Errors:
400 Bad Request—namefield is empty401 Unauthorized— missing or invalid session cookie500 Internal Server Error— unexpected server error
Creates a new private (one-to-one) chat between the authenticated user and another user identified by username. Fails if a private chat between the two users already exists, or if the user attempts to chat with themselves.
-
Auth: Required (session cookie)
-
Request Body:
{ "username": "string" } -
Response
200 OK:{ "id": "int", "name": "string", "created_at": "string (ISO 8601)", "is_group": false, "last_message": null, "unread_count": 0 }nameis set to the other user's display name or username. -
Errors:
400 Bad Request—usernamefield is empty, the chat already exists, or the user tried to chat with themselves401 Unauthorized— missing or invalid session cookie404 Not Found— target username does not exist500 Internal Server Error— unexpected server error
Returns the list of all members currently in a chat. Requires the authenticated user to be a member of the chat.
-
Auth: Required (session cookie)
-
Path Parameters:
Parameter Type Description chat_idint ID of the chat -
Response
200 OK:[ { "id": "int", "name": "string", "username": "string" } ] -
Errors:
401 Unauthorized— missing or invalid session cookie403 Forbidden— user is not a member of this chat500 Internal Server Error— unexpected server error
Marks the chat as fully read for the authenticated user by updating their last_read_at timestamp. This resets the unread message counter for this chat.
-
Auth: Required (session cookie)
-
Path Parameters:
Parameter Type Description chat_idint ID of the chat -
Response
200 OK:{} -
Errors:
401 Unauthorized— missing or invalid session cookie403 Forbidden— user is not a member of this chat500 Internal Server Error— unexpected server error
Removes the authenticated user from a group chat. This operation is not permitted on private chats.
-
Auth: Required (session cookie)
-
Path Parameters:
Parameter Type Description chat_idint ID of the chat -
Response
204 No Content -
Errors:
401 Unauthorized— missing or invalid session cookie403 Forbidden— user is not a member of this chat, or the chat is a private chat404 Not Found— chat does not exist500 Internal Server Error— unexpected server error
Returns all messages in a chat, ordered chronologically (oldest first). Requires the authenticated user to be a member of the chat.
-
Auth: Required (session cookie)
-
Path Parameters:
Parameter Type Description chat_idint ID of the chat -
Response
200 OK:[ { "id": "int", "from": { "id": "int", "name": "string", "username": "string" }, "chat_id": "int", "content": "string", "sent_at": "string (ISO 8601)" } ] -
Errors:
401 Unauthorized— missing or invalid session cookie403 Forbidden— user is not a member of this chat500 Internal Server Error— unexpected server error
Sends a new message in a chat. After the message is persisted, a message.received event is pushed via WebSocket to all other members of the chat who have an active connection.
-
Auth: Required (session cookie)
-
Path Parameters:
Parameter Type Description chat_idint ID of the chat -
Request Body:
{ "content": "string" } -
Response
201 Created(empty body) -
Errors:
400 Bad Request—contentfield is empty401 Unauthorized— missing or invalid session cookie403 Forbidden— user is not a member of this chat500 Internal Server Error— unexpected server error
Join requests allow a member of a group chat to invite another user. The invited user can then accept or decline.
Returns all pending join requests addressed to the authenticated user.
-
Auth: Required (session cookie)
-
Response
200 OK:[ { "id": "int", "sent_at": "string (ISO 8601)", "from": { "id": "int", "name": "string", "username": "string" }, "chat": { "id": "int", "name": "string | null", "created_at": "string (ISO 8601)" } } ] -
Errors:
401 Unauthorized— missing or invalid session cookie500 Internal Server Error— unexpected server error
Sends a join request for a group chat to a target user. The inviter must be a member of the chat. If the invitee has an active WebSocket connection, an invitation.created event is pushed immediately.
-
Auth: Required (session cookie)
-
Path Parameters:
Parameter Type Description chat_idint ID of the chat -
Request Body:
{ "username": "string" } -
Response
204 No Content -
Errors:
401 Unauthorized— missing or invalid session cookie404 Not Found— target username or chat does not exist500 Internal Server Error— unexpected server error
Accepts or declines a pending join request. The authenticated user must be the invitee. The request is deleted regardless of the action taken.
If accepted, the user is added to the chat and a chat.member_joined event is pushed via WebSocket to all existing members of the chat.
If declined, the request is simply removed with no further side effects.
-
Auth: Required (session cookie)
-
Path Parameters:
Parameter Type Description idint ID of the request -
Request Body:
{ "status": "accept | decline" } -
Response
200 OK:If accepted, returns the chat that was joined:
{ "id": "int", "name": "string | null", "created_at": "string (ISO 8601)" }If declined, returns a placeholder object:
{ "id": -1, "name": null, "created_at": "" } -
Errors:
401 Unauthorized— missing or invalid session cookie404 Not Found— request does not exist or does not belong to the authenticated user500 Internal Server Error— unexpected server error
Establishes a persistent WebSocket connection for receiving real-time events. Authentication is performed via the sid session cookie at upgrade time. If the cookie is missing or invalid, the connection is closed immediately.
A single user may hold multiple simultaneous connections (e.g. multiple browser tabs). Events are delivered to all active connections for that user.
The server does not send any messages proactively other than the event types below. The client may send a standard WebSocket close frame to terminate the connection gracefully.
Event Types
All events share the same envelope:
{
"type": "string",
"data": {}
}message.received
Pushed to all members of a chat (except the sender) when a new message is posted.
{
"type": "message.received",
"data": {
"id": "int",
"from": {
"id": "int",
"name": "string",
"username": "string"
},
"chat_id": "int",
"content": "string",
"sent_at": "string (ISO 8601)"
}
}invitation.created
Pushed to a user when they receive a new join request.
{
"type": "invitation.created",
"data": {
"request_id": "int",
"from": {
"id": "int",
"name": "string",
"username": "string"
},
"chat": {
"id": "int",
"name": "string | null",
"created_at": "string (ISO 8601)"
},
"sent_at": "string (ISO 8601)"
}
}chat.member_joined
Pushed to all existing members of a chat when a join request is accepted.
{
"type": "chat.member_joined",
"data": {
"chat_id": "int",
"user": {
"id": "int",
"name": "string",
"username": "string"
}
}
}chat.member_left
Pushed to all existing members of a chat when a user left the chat.
{
"type": "chat.member_left",
"data": {
"chat_id": "int",
"user_id": "int"
}
}| Status Code | Meaning |
|---|---|
400 |
Bad Request — the request body is malformed or a field fails validation |
401 |
Unauthorized — session cookie is missing, invalid, or expired |
403 |
Forbidden — the user does not have permission to perform this action |
404 |
Not Found — the requested resource does not exist |
500 |
Internal Server Error — an unexpected error occurred on the server |