Repository: SlotifyApp/api
Pull request #15: Fenn7/request-id
Author: login: fenn7
Created: 2025-03-06T16:20:11Z | Merged: 2025-03-07T16:46:05Z

Log request IDs on backend for error handling.


Diff:
diff --git a/.gitignore b/.gitignore
index 65b83f55..95f32778 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
 venv/
-virenv/
\ No newline at end of file
+virenv/
diff --git a/api/auth_impl.go b/api/auth_impl.go
index 4a147842..9dfe44df 100644
--- a/api/auth_impl.go
+++ b/api/auth_impl.go
@@ -17,7 +17,6 @@ func (s Server) PostAPIRefresh(w http.ResponseWriter, r *http.Request) {
 	defer cancel()
 
 	refreshToken, ok := r.Context().Value(RefreshTokenCtxKey{}).(string)
-
 	if !ok {
 		s.Logger.Error("failed to parse refresh token from context value into string")
 		sendError(w, http.StatusUnauthorized, "failed to parse refresh token from context value into string")
@@ -26,7 +25,6 @@ func (s Server) PostAPIRefresh(w http.ResponseWriter, r *http.Request) {
 
 	claims, err := jwt.ParseJWT(refreshToken, jwt.RefreshTokenJWTSecretEnv)
 	if err != nil {
-		s.Logger.Errorf("failed to verify refreshToken", zap.Error(err))
 		sendError(w, http.StatusUnauthorized, "refresh token was invalid")
 		return
 	}
@@ -35,7 +33,6 @@ func (s Server) PostAPIRefresh(w http.ResponseWriter, r *http.Request) {
 
 	var rt database.RefreshToken
 	if rt, err = s.DB.GetRefreshTokenByUserID(ctx, userID); err != nil {
-		s.Logger.Error("failed to get refresh token for user", zap.Error(err))
 		sendError(w, http.StatusUnauthorized, "failed to refresh token")
 		return
 	}
@@ -50,7 +47,7 @@ func (s Server) PostAPIRefresh(w http.ResponseWriter, r *http.Request) {
 	// Generate new access token and new refresh token
 	var uq database.User
 	if uq, err = s.DB.GetUserByID(ctx, userID); err != nil {
-		s.Logger.Error("Failed to refresh token", zap.Error(err))
+		s.Logger.Error("Failed to refresh token, ", zap.Error(err))
 		sendError(w, http.StatusInternalServerError, err.Error())
 		return
 	}
@@ -58,7 +55,7 @@ func (s Server) PostAPIRefresh(w http.ResponseWriter, r *http.Request) {
 	var tks jwt.AccessAndRefreshTokens
 	tks, err = jwt.CreateAccessAndRefreshTokens(ctx, s.Logger, &s.DB.Queries, userID, uq.Email)
 	if err != nil {
-		s.Logger.Error("failed to create access and refresh tokens", zap.Error(err))
+		s.Logger.Error("failed to create access and refresh tokens, ", zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "failed to create access and refresh tokens")
 		return
 	}
@@ -72,41 +69,41 @@ func (s Server) PostAPIRefresh(w http.ResponseWriter, r *http.Request) {
 func (s Server) GetAPIAuthCallback(w http.ResponseWriter, r *http.Request, params GetAPIAuthCallbackParams) {
 	msftTokenRes, err := msftAuthoriseByCode(r.Context(), s.MSALClient, params.Code)
 	if err != nil {
-		s.Logger.Error("failed to get microsoft tokens", zap.Error(err))
+		s.Logger.Error("failed to get microsoft tokens, ", zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "Sorry, try again later. Failed to get Microsoft tokens.")
 		return
 	}
 
 	tx, err := s.DB.DB.Begin()
 	if err != nil {
-		s.Logger.Error("failed to start db transaction", zap.Error(err))
+		s.Logger.Error("failed to start db transaction, ", zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "callback route: failed to start db transaction")
 		return
 	}
 
 	defer func() {
 		if err = tx.Rollback(); err != nil {
-			s.Logger.Error("failed to rollback db transaction", zap.Error(err))
+			s.Logger.Error("failed to rollback db transaction, ", zap.Error(err))
 		}
 	}()
 
 	qtx := s.DB.WithTx(tx)
 	var u database.User
 	if u, err = getOrInsertUserByClaimEmail(r.Context(), qtx, msftTokenRes); err != nil {
-		s.Logger.Error("failed to get user for claim email from msft access token", zap.Error(err))
+		s.Logger.Error("failed to get user for claim email from msft access token, ", zap.Error(err))
 		sendError(w, http.StatusBadRequest, "failed to parse msft access token")
 		return
 	}
 
 	var tks jwt.AccessAndRefreshTokens
 	if tks, err = jwt.CreateAccessAndRefreshTokens(r.Context(), s.Logger, qtx, u.ID, u.Email); err != nil {
-		s.Logger.Error("failed to create and store tokens", zap.Error(err))
+		s.Logger.Error("failed to create and store tokens, ", zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "failed to create slotify access and refresh token")
 		return
 	}
 
 	if err = tx.Commit(); err != nil {
-		s.Logger.Error("failed to commit db transaction", zap.Error(err))
+		s.Logger.Error("failed to commit db transaction, ", zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "failed to commit db transaction")
 		return
 	}
@@ -116,7 +113,7 @@ func (s Server) GetAPIAuthCallback(w http.ResponseWriter, r *http.Request, param
 	frontendURL, present := os.LookupEnv("FRONTEND_URL")
 	if !present {
 		s.Logger.Error("failed to get FRONTEND_URL value")
-		sendError(w, http.StatusInternalServerError, "Sorry, failed to get required env var")
+		sendError(w, http.StatusInternalServerError, "Sorry, try again")
 		return
 	}
 
diff --git a/api/calendar_impl.go b/api/calendar_impl.go
index 789909b6..ad577aa6 100644
--- a/api/calendar_impl.go
+++ b/api/calendar_impl.go
@@ -3,6 +3,7 @@ package api
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"net/http"
 	"time"
@@ -19,17 +20,21 @@ func (s Server) GetAPICalendarMe(w http.ResponseWriter, r *http.Request, params
 	ctx, cancel := context.WithTimeout(r.Context(), time.Minute)
 	defer cancel()
 
-	// Get userID from request
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	graph, err := CreateMSFTGraphClient(ctx, s.MSALClient, s.DB, userID)
 	if err != nil {
-		s.Logger.Error("failed to create msgraph client", zap.Error(err))
+		s.Logger.Errorf("%s: failed to create msgraph client: ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to connect to microsoft graph API")
 		return
 	}
@@ -37,7 +42,7 @@ func (s Server) GetAPICalendarMe(w http.ResponseWriter, r *http.Request, params
 	// Make call to API route and parse events
 	calendarEvents, err := makeCalendarMeAPICall(graph, params.Start, params.End)
 	if err != nil {
-		s.Logger.Error("failed to make calendar me msgraph api call", zap.Error(err))
+		s.Logger.Error("%s: failed to make calendar me msgraph api call: ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to make calendar me msgraph api call")
 		return
 	}
@@ -50,23 +55,28 @@ func (s Server) PostAPICalendarMe(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(r.Context(), time.Minute)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	var eventRequest CalendarEvent
-	if err := json.NewDecoder(r.Body).Decode(&eventRequest); err != nil {
-		s.Logger.Error("failed to parse event body", zap.Error(ErrUnmarshalBody), zap.Error(err))
+	if err = json.NewDecoder(r.Body).Decode(&eventRequest); err != nil {
+		s.Logger.Errorf("%s: failed to parse event body: ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadRequest, "Invalid request body.")
 		return
 	}
 
 	graph, err := CreateMSFTGraphClient(ctx, s.MSALClient, s.DB, userID)
 	if err != nil {
-		s.Logger.Error("failed to create msgraph client", zap.Error(err))
+		s.Logger.Errorf("%s: failed to create msgraph client: ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to connect to microsoft graph API")
 		return
 	}
@@ -82,7 +92,7 @@ func (s Server) PostAPICalendarMe(w http.ResponseWriter, r *http.Request) {
 		return nil
 	}, retry.Attempts(3), retry.Delay(time.Millisecond*500))
 	if err != nil {
-		s.Logger.Error("failed to create calendar event", zap.Error(err))
+		s.Logger.Errorf("%s: failed to create calendar event, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to create event")
 		return
 	}
@@ -96,7 +106,7 @@ func (s Server) PostAPICalendarMe(w http.ResponseWriter, r *http.Request) {
 	err = s.NotificationService.SendNotification(ctx, s.Logger, s.DB, []uint32{userID}, notif)
 	if err != nil {
 		// dont send http error because the actual op succeeded
-		s.Logger.Error("failed to send notification", zap.Error(err))
+		s.Logger.Errorf("%s: failed to send notification, ", reqID, zap.Error(err))
 	}
 
 	createdEvent := parseEventableResp([]graphmodels.Eventable{createdEventable})[0]
diff --git a/api/impl.go b/api/impl.go
index f8ad1ab0..ee260259 100644
--- a/api/impl.go
+++ b/api/impl.go
@@ -44,7 +44,7 @@ func (s Server) GetAPIHealthcheck(w http.ResponseWriter, _ *http.Request) {
 	resp := "Healthcheck Successful!"
 	w.WriteHeader(http.StatusOK)
 	if err := json.NewEncoder(w).Encode(resp); err != nil {
-		s.Logger.Error("Failed to encode JSON", zap.String("body", resp))
+		s.Logger.Error("Failed to encode JSON, ", zap.String("body", resp))
 		sendError(w, http.StatusInternalServerError, "Failed to encode JSON")
 		return
 	}
diff --git a/api/invite_impl.go b/api/invite_impl.go
index 2b8bc123..e8ea5044 100644
--- a/api/invite_impl.go
+++ b/api/invite_impl.go
@@ -19,42 +19,43 @@ func (s Server) PostAPIInvites(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(r.Context(), 4*database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	var invitesCreateBody PostAPIInvitesJSONRequestBody
-	var err error
 	if err = json.NewDecoder(r.Body).Decode(&invitesCreateBody); err != nil {
-		s.Logger.Error(ErrUnmarshalBody, zap.Object("body", invitesCreateBody), zap.Error(err))
+		s.Logger.Error(ErrUnmarshalBody,
+			zap.Object("body", invitesCreateBody), zap.Error(err))
 		sendError(w, http.StatusBadRequest, ErrUnmarshalBody.Error())
 		return
 	}
-
 	var g database.SlotifyGroup
 	if g, err = s.DB.GetSlotifyGroupByID(ctx, invitesCreateBody.SlotifyGroupID); err != nil {
-		s.Logger.Errorf("invite api: failed to get group by id", zap.Error(err))
+		s.Logger.Errorf("%s: invite api: failed to get group by id, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadRequest, "failed to get group by id")
 		return
 	}
-
 	var u database.User
 	if u, err = s.DB.GetUserByID(ctx, userID); err != nil {
-		s.Logger.Errorf("invite api: failed to get user by id", zap.Error(err))
+		s.Logger.Errorf("%s: invite api: failed to get user by id, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadRequest, "failed to get user by id")
 		return
 	}
-
 	var toUser database.User
 	if toUser, err = s.DB.GetUserByID(ctx, invitesCreateBody.ToUserID); err != nil {
-		s.Logger.Errorf("invite api: failed to get user by id", zap.Error(err))
+		s.Logger.Errorf("%s: invite api: failed to get user by id, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadRequest, "failed to get user by id")
 		return
 	}
-
 	// check if fromUser is in group and check if toUser is in group
 	if err = checkIfUsersInGroup(checkIfUsersInGroupParams{
 		ctx:              ctx,
@@ -66,11 +67,10 @@ func (s Server) PostAPIInvites(w http.ResponseWriter, r *http.Request) {
 		slotifyGroupName: g.Name,
 		toUserID:         invitesCreateBody.ToUserID,
 	}); err != nil {
-		s.Logger.Errorf("invite api: ", zap.Error(err))
+		s.Logger.Errorf("%s: invite api: ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadRequest, err.Error())
 		return
 	}
-
 	params := database.CreateInviteParams{
 		SlotifyGroupID: invitesCreateBody.SlotifyGroupID,
 		FromUserID:     userID,
@@ -80,7 +80,6 @@ func (s Server) PostAPIInvites(w http.ResponseWriter, r *http.Request) {
 		Status:         database.InviteStatusPending,
 		CreatedAt:      invitesCreateBody.CreatedAt,
 	}
-
 	var inviteID int64
 	err = retry.Do(func() error {
 		if inviteID, err = s.DB.CreateInvite(ctx, params); err != nil {
@@ -89,11 +88,10 @@ func (s Server) PostAPIInvites(w http.ResponseWriter, r *http.Request) {
 		return nil
 	}, retry.Attempts(3), retry.Delay(time.Millisecond*500))
 	if err != nil {
-		s.Logger.Error("failed to create invite", zap.Error(err))
+		s.Logger.Errorf("%s: failed to create invite, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to create invite")
 		return
 	}
-
 	sendPostInviteNotification(sendPostInviteNotificationParams{
 		ctx:             ctx,
 		toUserID:        invitesCreateBody.ToUserID,
@@ -105,7 +103,6 @@ func (s Server) PostAPIInvites(w http.ResponseWriter, r *http.Request) {
 		toUserFirstName: toUser.FirstName,
 		toUserLastName:  toUser.LastName,
 	})
-
 	createdInvite := InvitesGroup{
 		CreatedAt:         invitesCreateBody.CreatedAt,
 		ExpiryDate:        openapi_types.Date{Time: invitesCreateBody.ExpiryDate.Time},
@@ -116,7 +113,6 @@ func (s Server) PostAPIInvites(w http.ResponseWriter, r *http.Request) {
 		Status: InviteStatusPending, ToUserEmail: openapi_types.Email(toUser.Email),
 		ToUserFirstName: toUser.FirstName, ToUserLastName: toUser.LastName,
 	}
-
 	SetHeaderAndWriteResponse(w, http.StatusCreated, createdInvite)
 }
 
@@ -125,29 +121,23 @@ func (s Server) GetAPIInvitesMe(w http.ResponseWriter, r *http.Request, params G
 	ctx, cancel := context.WithTimeout(r.Context(), 4*database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	invites, err := s.DB.ListInvitesMe(ctx, database.ListInvitesMeParams{Status: params.Status, ToUserID: userID})
 	if err != nil {
-		switch {
-		case errors.Is(err, context.Canceled):
-			s.Logger.Error("invite api: failed to get invites: context cancelled")
-			sendError(w, http.StatusInternalServerError, "user api: failed to get invites")
-			return
-		case errors.Is(err, context.DeadlineExceeded):
-			s.Logger.Error("invite api: failed to get invites: query timed out")
-			sendError(w, http.StatusInternalServerError, "invite api: failed to get invites")
-			return
-		default:
-			s.Logger.Error("invite api: failed to get invites")
-			sendError(w, http.StatusInternalServerError, "user api: failed to get invites")
-			return
-		}
+		s.Logger.Errorf("%s: invite api: failed to get invites, ", reqID, err)
+		sendError(w, http.StatusInternalServerError, "user api: failed to get invites")
+		return
 	}
 
 	SetHeaderAndWriteResponse(w, http.StatusOK, invites)
@@ -158,17 +148,21 @@ func (s Server) DeleteAPIInvitesInviteID(w http.ResponseWriter, r *http.Request,
 	ctx, cancel := context.WithTimeout(r.Context(), 2*database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	var invite database.Invite
-	var err error
 	if invite, err = s.DB.GetInviteByID(ctx, inviteID); err != nil {
-		s.Logger.Error("failed to get invite by id", zap.Error(err))
+		s.Logger.Errorf("%s: failed to get invite by id, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to get invite by id")
 		return
 	}
@@ -178,13 +172,13 @@ func (s Server) DeleteAPIInvitesInviteID(w http.ResponseWriter, r *http.Request,
 		UserID:         userID,
 		SlotifyGroupID: invite.SlotifyGroupID,
 	}); err != nil {
-		s.Logger.Error("failed to see if user is in group", zap.Error(err))
+		s.Logger.Errorf("%s: failed to see if user is in group, ", reqID, zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "failed to see if user is in group")
 		return
 	}
 
 	if !userIsInGroup {
-		s.Logger.Error("user is not in group, cannot delete invite", zap.Error(err))
+		s.Logger.Errorf("%s: user is not in group, cannot delete invite, ", reqID, zap.Error(err))
 		sendError(w, http.StatusUnauthorized, "You are not apart of the group, cannto delete invite.")
 		return
 	}
@@ -193,7 +187,7 @@ func (s Server) DeleteAPIInvitesInviteID(w http.ResponseWriter, r *http.Request,
 		return database.DeleteInviteByIDWrapper(ctx, s.DB, inviteID)
 	}, retry.Attempts(3), retry.Delay(time.Millisecond*500))
 	if err != nil {
-		s.Logger.Error("failed to delete invite", zap.Error(err))
+		s.Logger.Errorf("%s: failed to delete invite, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to create invite")
 		return
 	}
@@ -206,30 +200,34 @@ func (s Server) PatchAPIInvitesInviteID(w http.ResponseWriter, r *http.Request,
 	ctx, cancel := context.WithTimeout(r.Context(), 2*database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	var body PatchAPIInvitesInviteIDJSONRequestBody
-	var err error
 	if err = json.NewDecoder(r.Body).Decode(&body); err != nil {
-		s.Logger.Error(ErrUnmarshalBody, zap.Object("body", body), zap.Error(err))
+		s.Logger.Errorf(ErrUnmarshalBody.Error()+"%s: , ", reqID, zap.Object("body", body), zap.Error(err))
 		sendError(w, http.StatusBadRequest, ErrUnmarshalBody.Error())
 		return
 	}
 
 	var invite database.Invite
 	if invite, err = s.DB.GetInviteByID(ctx, inviteID); err != nil {
-		s.Logger.Error("failed to get invite details from invite id", zap.Error(err))
+		s.Logger.Errorf("%s: failed to get invite details from invite id, ", reqID, zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "failed to get invite details from invite id")
 		return
 	}
 
 	if invite.FromUserID != userID {
-		s.Logger.Error("user cannot", zap.Error(err))
+		s.Logger.Errorf("%s: user cannot edit invite message ", reqID, zap.Error(err))
 		sendError(w, http.StatusUnauthorized,
 			"can only edit your invite message, contact the person who created the invite")
 		return
@@ -251,8 +249,7 @@ func (s Server) PatchAPIInvitesInviteID(w http.ResponseWriter, r *http.Request,
 		if err != nil {
 			switch {
 			case errors.Is(err, context.Canceled):
-				return fmt.Errorf("context cancelled deleting invite: %w",
-					err)
+				return fmt.Errorf("context cancelled deleting invite: %w", err)
 			case errors.Is(err, context.DeadlineExceeded):
 				return fmt.Errorf("deadline exceeded during deleting invite: %w", err)
 			default:
@@ -262,7 +259,7 @@ func (s Server) PatchAPIInvitesInviteID(w http.ResponseWriter, r *http.Request,
 		return nil
 	}, retry.Attempts(3), retry.Delay(time.Millisecond*500))
 	if err != nil {
-		s.Logger.Error("failed to update invite message", zap.Error(err))
+		s.Logger.Errorf("%s: failed to update invite message, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to update invite message")
 		return
 	}
@@ -277,10 +274,15 @@ func (s Server) PatchAPIInvitesInviteIDDecline(w http.ResponseWriter, r *http.Re
 	ctx, cancel := context.WithTimeout(r.Context(), 2*database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
@@ -293,9 +295,8 @@ func (s Server) PatchAPIInvitesInviteIDDecline(w http.ResponseWriter, r *http.Re
 		newStatus: InviteStatusAccepted,
 	}
 
-	var err error
 	if _, err = validateAndUpdateInviteStatus(p); err != nil {
-		s.Logger.Error("failed to validate and update invite status", zap.Error(err))
+		s.Logger.Errorf("%s: failed to validate and update invite status, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, err.Error())
 		return
 	}
@@ -310,23 +311,28 @@ func (s Server) PatchAPIInvitesInviteIDAccept(w http.ResponseWriter, r *http.Req
 	ctx, cancel := context.WithTimeout(r.Context(), 2*database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	tx, err := s.DB.DB.Begin()
 	if err != nil {
-		s.Logger.Error("failed to start db transaction", zap.Error(err))
+		s.Logger.Errorf("%s: failed to start db transaction, ", reqID, zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "callback route: failed to start db transaction")
 		return
 	}
 
 	defer func() {
 		if err = tx.Rollback(); err != nil {
-			s.Logger.Error("failed to rollback db transaction", zap.Error(err))
+			s.Logger.Errorf("%s: failed to rollback db transaction, ", reqID, zap.Error(err))
 		}
 	}()
 
@@ -343,7 +349,7 @@ func (s Server) PatchAPIInvitesInviteIDAccept(w http.ResponseWriter, r *http.Req
 
 	var invite database.Invite
 	if invite, err = validateAndUpdateInviteStatus(p); err != nil {
-		s.Logger.Error("failed to validate and update invite status", zap.Error(err))
+		s.Logger.Errorf("%s: failed to validate and update invite status, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, err.Error())
 		return
 	}
@@ -358,7 +364,7 @@ func (s Server) PatchAPIInvitesInviteIDAccept(w http.ResponseWriter, r *http.Req
 	}
 
 	if err = AddUserToSlotifyGroup(addUserParams); err != nil {
-		s.Logger.Error("failed to add user to slotify group", zap.Error(err),
+		s.Logger.Errorf("%s: failed to add user to slotify group, ", reqID, zap.Error(err),
 			zap.Uint32("slotifyGroupID", invite.SlotifyGroupID),
 			zap.Uint32("userID", userID),
 		)
@@ -367,7 +373,7 @@ func (s Server) PatchAPIInvitesInviteIDAccept(w http.ResponseWriter, r *http.Req
 	}
 
 	if err = tx.Commit(); err != nil {
-		s.Logger.Error("failed to commit db transaction", zap.Error(err))
+		s.Logger.Errorf("%s: failed to commit db transaction, ", reqID, zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "failed to accept invite")
 		return
 	}
@@ -382,26 +388,27 @@ func (s Server) GetAPISlotifyGroupsSlotifyGroupIDInvites(w http.ResponseWriter,
 	ctx, cancel := context.WithTimeout(r.Context(), 2*database.DatabaseTimeout)
 	defer cancel()
 
+	reqID, _, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
+		return
+	}
+
 	invites, err := s.DB.ListInvitesByGroup(ctx,
 		database.ListInvitesByGroupParams{
 			SlotifyGroupID: slotifyGroupID,
 			Status:         params.Status,
 		})
 	if err != nil {
-		switch {
-		case errors.Is(err, context.Canceled):
-			s.Logger.Error("context cancelled getting invites group", zap.Error(err))
-			sendError(w, http.StatusBadGateway, "Failed to update invite")
-			return
-		case errors.Is(err, context.DeadlineExceeded):
-			s.Logger.Error("context deadline exceeded while getting invites group", zap.Error(err))
-			sendError(w, http.StatusBadGateway, "Failed to update invite")
-			return
-		default:
-			s.Logger.Error("failed to get group invites", zap.Error(err))
-			sendError(w, http.StatusBadGateway, "Failed to get group invites")
-			return
-		}
+		s.Logger.Errorf("%s: failed to get group invites, ", reqID, zap.Error(err))
+		sendError(w, http.StatusInternalServerError, "Failed to get group invites")
+		return
 	}
 
 	SetHeaderAndWriteResponse(w, http.StatusOK, invites)
diff --git a/api/middleware.go b/api/middleware.go
index 87a8ca97..47f6b37a 100644
--- a/api/middleware.go
+++ b/api/middleware.go
@@ -2,28 +2,42 @@ package api
 
 import (
 	"context"
+	"errors"
+	"fmt"
 	"log"
 	"net/http"
 	"time"
 
 	"github.com/SlotifyApp/slotify-backend/jwt"
 	"github.com/getkin/kin-openapi/openapi3"
+
 	chi_middleware "github.com/go-chi/chi/v5/middleware"
 	"github.com/go-chi/httprate"
+	"github.com/google/uuid"
 	"github.com/gorilla/mux"
 	oapi_middleware "github.com/oapi-codegen/nethttp-middleware"
 )
 
 const (
 	requestLimit = 100
+	reqHeader    = "X-Request-ID"
 )
 
+// ErrUserIDNotFound is returned when the user id is not set in the request context.
+var ErrUserIDNotFound = errors.New("user id not found in request context")
+
+// ErrRequestIDNotFound is returned when the request id is not set in the request context.
+var ErrRequestIDNotFound = errors.New("request id not found in request context")
+
 // RefreshTokenCtxKey is the key in context value for the refresh token value.
 type RefreshTokenCtxKey struct{}
 
 // UserIDCtxKey is the key in context value for the user id value.
 type UserIDCtxKey struct{}
 
+// RequestIDCtxKey is the key in context value for the request id.
+type RequestIDCtxKey struct{}
+
 // AuthMiddleware takes the http-only cookies and sets the access token under the Authorization header.
 // The refresh token is set in the request context.
 func AuthMiddleware(next http.Handler) http.Handler {
@@ -52,7 +66,7 @@ func AuthMiddleware(next http.Handler) http.Handler {
 			return
 		}
 
-		r.Header.Set("Authorization", "Bearer "+accessTokenCookie.Value)
+		r.Header.Set("Authorization", fmt.Sprintf("Bearer: %s", accessTokenCookie.Value))
 
 		// Some methods require refresh token and it is sent automatically
 		// anyway in any request from the frontend
@@ -105,6 +119,19 @@ func JWTMiddleware(next http.Handler) http.Handler {
 	})
 }
 
+func RequestIDMiddleware(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		reqID := uuid.NewString()
+
+		// set in context
+		ctx := context.WithValue(r.Context(), RequestIDCtxKey{}, reqID)
+		// set in response writer header
+		w.Header().Set(reqHeader, reqID)
+
+		next.ServeHTTP(w, r.WithContext(ctx))
+	})
+}
+
 // ApplyMiddlewares applies all the middleware functions for the server.
 func ApplyMiddlewares(r *mux.Router, swagger *openapi3.T) {
 	middlewares := []mux.MiddlewareFunc{
@@ -113,6 +140,7 @@ func ApplyMiddlewares(r *mux.Router, swagger *openapi3.T) {
 		// makes sure that requests and responses follow openapischema
 		oapi_middleware.OapiRequestValidator(swagger),
 
+		RequestIDMiddleware,
 		JWTMiddleware,
 
 		// logs requests and statuses.
@@ -131,3 +159,17 @@ func ApplyMiddlewares(r *mux.Router, swagger *openapi3.T) {
 		r.Use(middleware)
 	}
 }
+
+func GetCtxValues(r *http.Request) (string, uint32, error) {
+	var reqID string
+	var userID uint32
+	var ok bool
+	if reqID, ok = r.Context().Value(RequestIDCtxKey{}).(string); !ok {
+		return "", 0, ErrRequestIDNotFound
+	}
+	if userID, ok = r.Context().Value(UserIDCtxKey{}).(uint32); !ok {
+		return "", 0, ErrUserIDNotFound
+	}
+
+	return reqID, userID, nil
+}
diff --git a/api/msft_group_impl.go b/api/msft_group_impl.go
index 79f38d26..17f38c11 100644
--- a/api/msft_group_impl.go
+++ b/api/msft_group_impl.go
@@ -2,6 +2,7 @@ package api
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"net/http"
 	"strconv"
@@ -17,17 +18,21 @@ func (s Server) GetAPIMSFTGroups(w http.ResponseWriter, r *http.Request, params
 	ctx, cancel := context.WithTimeout(r.Context(), database.DatabaseTimeout)
 	defer cancel()
 
-	// Get userID from request
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	graph, err := CreateMSFTGraphClient(ctx, s.MSALClient, s.DB, userID)
 	if err != nil {
-		s.Logger.Error("failed to create msgraph client", zap.Error(err))
+		s.Logger.Errorf("%s: failed to create msgraph client, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to connect to microsoft graph API")
 		return
 	}
@@ -41,13 +46,13 @@ func (s Server) GetAPIMSFTGroups(w http.ResponseWriter, r *http.Request, params
 
 	gets, err := graph.Groups().Get(ctx, configuration)
 	if err != nil {
-		s.Logger.Error("failed to get group")
+		s.Logger.Errorf("%s: failed to get group, ", reqID)
 		sendError(w, http.StatusInternalServerError, "Failed to connect to get group")
 		return
 	}
 
 	if gets.GetValue() == nil || len(gets.GetValue()) == 0 {
-		s.Logger.Error("no group found")
+		s.Logger.Errorf("%s: no group found, ", reqID)
 		sendError(w, http.StatusNotFound, "Failed to find a group with name")
 		return
 	}
@@ -55,7 +60,7 @@ func (s Server) GetAPIMSFTGroups(w http.ResponseWriter, r *http.Request, params
 	// uses the first group
 	group, err := GroupableToMSFTGroup(gets.GetValue()[0])
 	if err != nil {
-		s.Logger.Error("error converting groupable")
+		s.Logger.Errorf("%s: error converting groupable, ", reqID)
 		sendError(w, http.StatusInternalServerError, "Failed to convert groupable")
 		return
 	}
@@ -68,17 +73,21 @@ func (s Server) GetAPIMSFTGroupsMe(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(r.Context(), database.DatabaseTimeout)
 	defer cancel()
 
-	// Get userID from request
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	graph, err := CreateMSFTGraphClient(ctx, s.MSALClient, s.DB, userID)
 	if err != nil {
-		s.Logger.Error("failed to create msgraph client", zap.Error(err))
+		s.Logger.Errorf("%s: failed to create msgraph client, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to connect to microsoft graph API")
 		return
 	}
@@ -87,14 +96,14 @@ func (s Server) GetAPIMSFTGroupsMe(w http.ResponseWriter, r *http.Request) {
 
 	gets, err := graph.Users().ByUserId(userIDStr).MemberOf().Get(ctx, nil)
 	if err != nil {
-		s.Logger.Error("failed to retrieve memberof of user")
+		s.Logger.Errorf("%s: failed to retrieve memberof of user, ", reqID)
 		sendError(w, http.StatusInternalServerError, "Failed to get from microsoft")
 		return
 	}
 
 	groups, err := GetsToMSFTGroups(gets)
 	if err != nil {
-		s.Logger.Error("failed to retrive groups", zap.Error(err))
+		s.Logger.Errorf("%s: failed to retrive , ", reqID, zap.Error(err))
 		sendError(w, http.StatusInternalServerError, fmt.Sprintf("failed retrieving groups: %v", err))
 		return
 	}
@@ -106,17 +115,21 @@ func (s Server) GetAPIMSFTGroupsGroupID(w http.ResponseWriter, r *http.Request,
 	ctx, cancel := context.WithTimeout(r.Context(), database.DatabaseTimeout)
 	defer cancel()
 
-	// Get userID from request
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	graph, err := CreateMSFTGraphClient(ctx, s.MSALClient, s.DB, userID)
 	if err != nil {
-		s.Logger.Error("failed to create msgraph client", zap.Error(err))
+		s.Logger.Errorf("%s: failed to create msgraph client, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to connect to microsoft graph API")
 		return
 	}
@@ -125,7 +138,7 @@ func (s Server) GetAPIMSFTGroupsGroupID(w http.ResponseWriter, r *http.Request,
 
 	groupable, err := graph.Groups().ByGroupId(groupIDStr).Get(ctx, nil)
 	if err != nil {
-		s.Logger.Error("failed to get group from microsoft")
+		s.Logger.Errorf("%s: failed to get group from microsoft, ", reqID)
 		sendError(w, http.StatusNotFound, "Failed to find group")
 		return
 	}
@@ -135,12 +148,12 @@ func (s Server) GetAPIMSFTGroupsGroupID(w http.ResponseWriter, r *http.Request,
 	if groupable != nil && groupable.GetId() != nil && groupable.GetDisplayName() != nil {
 		group, err = GroupableToMSFTGroup(groupable)
 		if err != nil {
-			s.Logger.Error("error converting groupable")
+			s.Logger.Errorf("%s: error converting groupable, ", reqID)
 			sendError(w, http.StatusInternalServerError, "Failed to convert groupable")
 			return
 		}
 	} else {
-		s.Logger.Error("got invalid group from microsoft")
+		s.Logger.Errorf("%s: got invalid group from microsoft, ", reqID)
 		sendError(w, http.StatusInternalServerError, "Invalid group")
 		return
 	}
@@ -153,17 +166,21 @@ func (s Server) GetAPIMSFTGroupsGroupIDUsers(w http.ResponseWriter, r *http.Requ
 	ctx, cancel := context.WithTimeout(r.Context(), database.DatabaseTimeout)
 	defer cancel()
 
-	// Get userID from request
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	graph, err := CreateMSFTGraphClient(ctx, s.MSALClient, s.DB, userID)
 	if err != nil {
-		s.Logger.Error("failed to create msgraph client", zap.Error(err))
+		s.Logger.Errorf("%s: failed to create msgraph client, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to connect to microsoft graph API")
 		return
 	}
@@ -172,7 +189,7 @@ func (s Server) GetAPIMSFTGroupsGroupIDUsers(w http.ResponseWriter, r *http.Requ
 
 	groupable, err := graph.Groups().ByGroupId(groupIDStr).Members().Get(ctx, nil)
 	if err != nil {
-		s.Logger.Error("failed to get group from microsoft")
+		s.Logger.Errorf("%s: failed to get group from microsoft, ", reqID)
 		sendError(w, http.StatusNotFound, "Failed to find group")
 		return
 	}
@@ -181,12 +198,11 @@ func (s Server) GetAPIMSFTGroupsGroupIDUsers(w http.ResponseWriter, r *http.Requ
 
 	if groupable.GetValue() != nil {
 		for _, dirs := range groupable.GetValue() {
-			var usr models.Userable
-			if usr, ok = dirs.(models.Userable); ok {
+			if usr, ok := dirs.(models.Userable); ok {
 				var user MSFTGroupUser
 				user, err = UserableToMSFTGroupUser(usr)
 				if err != nil {
-					s.Logger.Error("failed to convert userable to user")
+					s.Logger.Errorf("%s: failed to convert userable to user, ", reqID)
 					sendError(w, http.StatusInternalServerError, "Failed to convert userable to user")
 					return
 				}
diff --git a/api/notification_impl.go b/api/notification_impl.go
index 5b8e6b43..39361b8d 100644
--- a/api/notification_impl.go
+++ b/api/notification_impl.go
@@ -2,6 +2,7 @@ package api
 
 import (
 	"context"
+	"errors"
 	"net/http"
 
 	"github.com/SlotifyApp/slotify-backend/database"
@@ -10,10 +11,15 @@ import (
 
 // (GET /api/events), HTTP SSE route.
 func (s Server) RenderEvent(w http.ResponseWriter, r *http.Request) {
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
@@ -33,8 +39,8 @@ func (s Server) RenderEvent(w http.ResponseWriter, r *http.Request) {
 
 	f.Flush()
 
-	if err := s.NotificationService.RegisterUserClient(s.Logger, userID, w); err != nil {
-		s.Logger.Errorf("failed to register user client", zap.Error(err))
+	if err = s.NotificationService.RegisterUserClient(s.Logger, userID, w); err != nil {
+		s.Logger.Errorf("%s: failed to register user client, ", reqID, zap.Error(err))
 		sendError(w, http.StatusUnauthorized, "failed to register user client")
 		return
 	}
@@ -51,10 +57,15 @@ func (s Server) PatchAPINotificationsNotificationIDRead(w http.ResponseWriter, r
 	ctx, cancel := context.WithTimeout(r.Context(), database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
@@ -65,7 +76,7 @@ func (s Server) PatchAPINotificationsNotificationIDRead(w http.ResponseWriter, r
 
 	rowsAffected, err := s.DB.MarkNotificationAsRead(ctx, dbParams)
 	if err != nil {
-		s.Logger.Error("failed to mark notification as read", zap.Error(err))
+		s.Logger.Errorf("%s: failed to mark notification as read, ", reqID, zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "Failed to mark notification as read.")
 		return
 	}
@@ -75,7 +86,7 @@ func (s Server) PatchAPINotificationsNotificationIDRead(w http.ResponseWriter, r
 			ActualRows:   rowsAffected,
 			ExpectedRows: []int64{1},
 		}
-		s.Logger.Error("failed to mark notification as read", zap.Error(err))
+		s.Logger.Errorf("%s: failed to mark notification as read, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadRequest, "Failed to mark notification as read.")
 		return
 	}
@@ -88,16 +99,21 @@ func (s Server) GetAPIUsersMeNotifications(w http.ResponseWriter, r *http.Reques
 	ctx, cancel := context.WithTimeout(r.Context(), database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	notifs, err := s.DB.GetUnreadUserNotifications(ctx, userID)
 	if err != nil {
-		s.Logger.Error("failed to get unread user notifications")
+		s.Logger.Errorf("%s: failed to get unread user notifications, request ID: ", reqID)
 		sendError(w, http.StatusInternalServerError, "failed to get unread user notifications from db")
 		return
 	}
diff --git a/api/scheduling_impl.go b/api/scheduling_impl.go
index 14900825..2270f641 100644
--- a/api/scheduling_impl.go
+++ b/api/scheduling_impl.go
@@ -3,6 +3,7 @@ package api
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"net/http"
 	"time"
 
@@ -15,32 +16,36 @@ func (s Server) PostAPISchedulingFree(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(r.Context(), time.Minute*3)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	var body SchedulingSlotsBodySchema
-	var err error
 	if err = json.NewDecoder(r.Body).Decode(&body); err != nil {
 		// TODO: Add zap log for body
-		s.Logger.Error(ErrUnmarshalBody, zap.Error(err))
+		s.Logger.Errorf(ErrUnmarshalBody.Error(), zap.String("request id", reqID), zap.Error(err))
 		sendError(w, http.StatusBadRequest, ErrUnmarshalBody.Error())
 		return
 	}
 
 	graph, err := CreateMSFTGraphClient(ctx, s.MSALClient, s.DB, userID)
 	if err != nil {
-		s.Logger.Error("failed to create msgraph client", zap.Error(err))
+		s.Logger.Error("%s: %s: failed to create msgraph client, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to connect to microsoft graph API")
 		return
 	}
 
 	respBody, err := makeFindMeetingTimesAPICall(ctx, graph, body)
 	if err != nil {
-		s.Logger.Error("failed to make msgraph api call to findMeetings", zap.Error(err))
+		s.Logger.Error("failed to make msgraph api call to findMeetings, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadGateway, "Failed to process/send microsoft graph API request for findMeeting")
 		return
 	}
diff --git a/api/slotify_group_impl.go b/api/slotify_group_impl.go
index 451b10c3..55b21257 100644
--- a/api/slotify_group_impl.go
+++ b/api/slotify_group_impl.go
@@ -19,27 +19,31 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 	ctx, cancel := context.WithTimeout(r.Context(), 2*database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	var count int64
-	var err error
 	if count, err = s.DB.CheckMemberInSlotifyGroup(ctx, database.CheckMemberInSlotifyGroupParams{
 		UserID:         userID,
 		SlotifyGroupID: slotifyGroupID,
 	}); err != nil {
-		s.Logger.Error("failed to get count of member in slotify group", zap.Error(err))
+		s.Logger.Errorf("%s: failed to get count of member in slotify group, ", reqID, zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "Failed to check if member is in slotify group")
 		return
 	}
 
 	// User not a member of the group
 	if count == 0 {
-		s.Logger.Error("user was not a member of the group, so can't leave", zap.Error(err),
+		s.Logger.Errorf("%s: user was not a member of the group, so can't leave, ", reqID, zap.Error(err),
 			zap.Uint32("userID", userID),
 			zap.Uint32("slotifyGroupID", slotifyGroupID),
 		)
@@ -49,7 +53,7 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 
 	var memberCount int64
 	if memberCount, err = s.DB.CountSlotifyGroupMembers(ctx, slotifyGroupID); err != nil {
-		s.Logger.Error("failed to get member count", zap.Error(err),
+		s.Logger.Errorf("%s: failed to get member count, ", reqID, zap.Error(err),
 			zap.Uint32("userID", userID),
 			zap.Uint32("slotifyGroupID", slotifyGroupID),
 		)
@@ -61,7 +65,7 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 	if memberCount == 1 {
 		var rowsAffected int64
 		if rowsAffected, err = s.DB.DeleteSlotifyGroupByID(ctx, slotifyGroupID); err != nil {
-			s.Logger.Error("failed to delete slotify group", zap.Error(err),
+			s.Logger.Errorf("%s: failed to delete slotify group, ", reqID, zap.Error(err),
 				zap.Uint32("userID", userID),
 				zap.Uint32("slotifyGroupID", slotifyGroupID),
 			)
@@ -72,7 +76,7 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 		var expectedRows int64 = 2
 		if rowsAffected != expectedRows {
 			err = database.WrongNumberSQLRowsError{ActualRows: rowsAffected, ExpectedRows: []int64{expectedRows}}
-			s.Logger.Error("failed to delete slotify group", zap.Error(err),
+			s.Logger.Errorf("%s: failed to delete slotify group, ", reqID, zap.Error(err),
 				zap.Uint32("userID", userID),
 				zap.Uint32("slotifyGroupID", slotifyGroupID),
 			)
@@ -86,7 +90,7 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 			SlotifyGroupID: slotifyGroupID,
 		}); err != nil {
 			err = database.WrongNumberSQLRowsError{ActualRows: rowsAffected, ExpectedRows: []int64{1}}
-			s.Logger.Error("failed to remove slotify member", zap.Error(err),
+			s.Logger.Errorf("%s: failed to remove slotify member, ", reqID, zap.Error(err),
 				zap.Uint32("userID", userID),
 				zap.Uint32("slotifyGroupID", slotifyGroupID),
 			)
@@ -105,7 +109,7 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 	}
 	// Still successful, just notifications failed
 	if err = sendLeaverNotifications(p); err != nil {
-		s.Logger.Error("failed to send leaver notifications", zap.Error(err),
+		s.Logger.Errorf("%s: failed to send leaver notifications, ", reqID, zap.Error(err),
 			zap.Uint32("userID", userID),
 			zap.Uint32("slotifyGroupID", slotifyGroupID),
 		)
@@ -116,31 +120,36 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 
 // (GET /api/slotify-groups/me).
 func (s Server) GetAPISlotifyGroupsMe(w http.ResponseWriter, r *http.Request) {
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
-		return
-	}
-
 	ctx, cancel := context.WithTimeout(r.Context(), 2*database.DatabaseTimeout)
 	defer cancel()
 
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
+		return
+	}
+
 	slotifyGroups, err := s.DB.GetUsersSlotifyGroups(ctx, userID)
 	if err != nil {
 		switch {
 		case errors.Is(err, context.Canceled):
-			s.Logger.Error("getting users slotifyGroup failed: context was cancelled",
+			s.Logger.Errorf("%s: getting users slotifyGroup failed: context was cancelled, ", reqID,
 				zap.Uint32("userID", userID))
 			sendError(w, http.StatusUnauthorized, "Try again later.")
 			return
 		case errors.Is(err, context.DeadlineExceeded):
-			s.Logger.Error("getting users slotifyGroup query timed out",
+			s.Logger.Errorf("%s: getting users slotifyGroup query timed out, ", reqID,
 				zap.Uint32("userID", userID))
 			sendError(w, http.StatusUnauthorized, "Try again later.")
 			return
 		default:
-			s.Logger.Error("failed to get users slotifyGroups", zap.Uint32("userID", userID))
+			s.Logger.Errorf("%s: failed to get users slotifyGroups, ", reqID, zap.Uint32("userID", userID))
 			sendError(w, http.StatusInternalServerError, fmt.Sprintf("Sorry, failed to get user id(%d)'s slotifyGroups", userID))
 			return
 		}
@@ -153,31 +162,36 @@ func (s Server) PostAPISlotifyGroups(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(r.Context(), 4*database.DatabaseTimeout)
 	defer cancel()
 
-	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
-	if !ok {
-		s.Logger.Error("failed to get userid from request context")
-		sendError(w, http.StatusUnauthorized, "Try again later.")
+	reqID, userID, err := GetCtxValues(r)
+	if err != nil {
+		if errors.Is(err, ErrRequestIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusInternalServerError, "Try again later.")
+		} else if errors.Is(err, ErrUserIDNotFound) {
+			s.Logger.Error(err)
+			sendError(w, http.StatusUnauthorized, "Try again later.")
+		}
 		return
 	}
 
 	var slotifyGroupBody PostAPISlotifyGroupsJSONRequestBody
-	var err error
 	if err = json.NewDecoder(r.Body).Decode(&slotifyGroupBody); err != nil {
-		s.Logger.Error(ErrUnmarshalBody, zap.Object("body", slotifyGroupBody), zap.Error(err))
+		s.Logger.Errorf(ErrUnmarshalBody.Error(), zap.String("request id", reqID),
+			zap.Object("body", slotifyGroupBody), zap.Error(err))
 		sendError(w, http.StatusBadRequest, ErrUnmarshalBody.Error())
 		return
 	}
 
 	tx, err := s.DB.DB.Begin()
 	if err != nil {
-		s.Logger.Error("failed to start db transaction", zap.Error(err))
+		s.Logger.Errorf("%s: failed to start db transaction, ", reqID, zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "callback route: failed to start db transaction")
 		return
 	}
 
 	defer func() {
 		if err = tx.Rollback(); err != nil {
-			s.Logger.Error("failed to rollback db transaction", zap.Error(err))
+			s.Logger.Errorf("%s: failed to rollback db transaction, ", reqID, zap.Error(err))
 		}
 	}()
 
@@ -188,13 +202,14 @@ func (s Server) PostAPISlotifyGroups(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		switch {
 		case database.IsDuplicateEntrySQLError(err):
-			s.Logger.Error("slotifyGroup api: slotifyGroup already exists",
+			s.Logger.Errorf("%s: slotifyGroup api: slotifyGroup already exists, ", reqID,
 				zap.Object("req_body", slotifyGroupBody), zap.Error(err))
 			sendError(w, http.StatusBadRequest,
 				fmt.Sprintf("slotifyGroup with name %s already exists", slotifyGroupBody.Name))
 			return
 		default:
-			s.Logger.Error("failed to create slotifyGroup", zap.Object("body", slotifyGroupBody), zap.Error(err))
+			s.Logger.Errorf("%s: failed to create slotifyGroup, ", reqID,
+				zap.Object("body", slotifyGroupBody), zap.Error(err))
 			sendError(w, http.StatusBadRequest, "slotifyGroup api: slotifyGroup creation unsuccessful")
 			return
 		}
@@ -211,7 +226,7 @@ func (s Server) PostAPISlotifyGroups(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if err = AddUserToSlotifyGroup(p); err != nil {
-		s.Logger.Error("creating group was successful, but adding user to the group was not",
+		s.Logger.Errorf("%s: creating group was successful, but adding user to the group was not, ", reqID,
 			zap.Uint32("userID", userID),
 			zap.Int64("slotifyGroupID", slotifyGroupID),
 			zap.Error(err))
@@ -220,7 +235,7 @@ func (s Server) PostAPISlotifyGroups(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if err = tx.Commit(); err != nil {
-		s.Logger.Error("failed to commit db transaction", zap.Error(err))
+		s.Logger.Errorf("%s: failed to commit db transaction, ", reqID, zap.Error(err))
 		sendError(w, http.StatusInternalServerError, "failed to create a group and add you to it")
 		return
 	}
@@ -237,25 +252,20 @@ func (s Server) DeleteAPISlotifyGroupsSlotifyGroupID(w http.ResponseWriter, r *h
 	ctx, cancel := context.WithTimeout(r.Context(), database.DatabaseTimeout)
 	defer cancel()
 
-	rowsDeleted, err := s.DB.DeleteSlotifyGroupByID(ctx, slotifyGroupID)
-	if err != nil {
-		switch {
-		case errors.Is(err, context.Canceled):
-			s.Logger.Error("slotifyGroup api: delete slotifyGroup by id query timed out",
-				zap.Uint32("slotifyGroupID", slotifyGroupID))
-			sendError(w, http.StatusInternalServerError, "add slotifyGroup query timed out")
-			return
-		case errors.Is(err, context.DeadlineExceeded):
-			s.Logger.Error("slotifyGroup api: delete slotifyGroup by id query timed out",
-				zap.Uint32("slotifyGroupID", slotifyGroupID))
-			sendError(w, http.StatusInternalServerError, "add slotifyGroup query timed out")
-			return
-		default:
-			s.Logger.Error("slotifyGroup api: failed to DeleteSlotifyGroupsSlotifyGroupID", zap.Error(err))
-			sendError(w, http.StatusInternalServerError,
-				"slotifyGroup api: slotifyGroup deletion unsuccessful")
-			return
-		}
+	reqID, _, err := GetCtxValues(r)
+	if err != nil && errors.Is(err, ErrRequestIDNotFound) {
+		s.Logger.Error(err)
+		sendError(w, http.StatusInternalServerError, "Try again later.")
+		return
+	}
+
+	var rowsDeleted int64
+	if rowsDeleted, err = s.DB.DeleteSlotifyGroupByID(ctx, slotifyGroupID); err != nil {
+		s.Logger.Errorf("%s: slotifyGroup api: failed to DeleteSlotifyGroupsSlotifyGroupID",
+			reqID, zap.Error(err))
+		sendError(w, http.StatusInternalServerError,
+			"slotifyGroup api: slotifyGroup deletion unsuccessful")
+		return
 	}
 
 	if rowsDeleted != 1 {
@@ -263,7 +273,7 @@ func (s Server) DeleteAPISlotifyGroupsSlotifyGroupID(w http.ResponseWriter, r *h
 			ActualRows:   rowsDeleted,
 			ExpectedRows: []int64{1},
 		}
-		s.Logger.Errorf("slotifyGroup api failed to delete slotifyGroup", zap.Error(err))
+		s.Logger.Errorf("%s: slotifyGroup api failed to delete slotifyGroup, ", reqID, zap.Error(err))
 		sendError(w, http.StatusBadRequest, "slotifyGroup api: incorrect slotifyGroup id")
 		return
 	}
@@ -276,31 +286,30 @@ func (s Server) GetAPISlotifyGroupsSlotifyGroupID(w http.ResponseWriter, r *http
 	ctx, cancel := context.WithTimeout(r.Context(), database.DatabaseTimeout)
 	defer cancel()
 
+	reqID, _, err := GetCtxValues(r)
+	if err != nil && errors.Is(err, ErrRequestIDNotFound) {
+		s.Logger.Error(err)
+		sendError(w, http.StatusInternalServerError, "Try again later.")
+		return
+	}
+
 	slotifyGroup, err := s.DB.GetSlotifyGroupByID(ctx, slotifyGroupID)
 	if err != nil {
 		switch {
-		case errors.Is(err, context.Canceled):
-			s.Logger.Error("slotifyGroup api: context cancelled",
-				zap.Uint32("slotifyGroupID", slotifyGroupID), zap.Error(err))
-			sendError(w, http.StatusInternalServerError, "sorry try again")
-			return
-		case errors.Is(err, context.DeadlineExceeded):
-			s.Logger.Error("slotifyGroup api: delete slotifyGroup by id query timed out",
-				zap.Uint32("slotifyGroupID", slotifyGroupID))
-			sendError(w, http.StatusInternalServerError, "add slotifyGroup query timed out")
-			return
 		case errors.Is(err, sql.ErrNoRows):
-			s.Logger.Error("slotifyGroup api: failed to get slotifyGroup by id, no matching rows",
+			s.Logger.Error("%s: slotifyGroup api: failed to get slotifyGroup by id, no matching rows, ",
+				reqID,
 				zap.Error(err))
 			sendError(w, http.StatusNotFound,
 				fmt.Sprintf("slotifyGroup api: slotifyGroup with id %d does not exist", slotifyGroupID))
-			return
 		default:
-			s.Logger.Error("slotifyGroup api: failed to GetSlotifyGroupsSlotifyGroupID", zap.Error(err))
+			s.Logger.Error("%s: slotifyGroup api: failed to get slotifyGroup by id", reqID,
+				zap.Error(err))
 			sendError(w, http.StatusInternalServerError, "slotifyGroup api: failed to get slotifyGroup")
-			return
 		}
+		return
 	}
+
 	SetHeaderAndWriteResponse(w, http.StatusOK, slotifyGroup)
 }
 
@@ -309,9 +318,17 @@ func (s Server) GetAPISlotifyGroupsSlotifyGroupIDUsers(w http.ResponseWriter, r
 	ctx, cancel := context.WithTimeout(r.Context(), 5*database.DatabaseTimeout)
 	defer cancel()
 
+	reqID, _, err := GetCtxValues(r)
+	if err != nil && errors.Is(err, ErrRequestIDNotFound) {
+		s.Logger.Error(err)
+		sendError(w, http.StatusInternalServerError, "Try again later.")
+		return
+	}
+
 	count, err := s.DB.CountSlotifyGroupByID(ctx, slotifyGroupID)
 	if err != nil {
-		s.Logger.Error("slotifyGroup api: failed to get users of a group: failed to get count of group by id",
+		s.Logger.Errorf("%s: slotifyGroup api: failed to get users of a group: failed to get count of group by id, ",
+			reqID,
 			zap.Uint32("slotifyGroupID",
 				slotifyGroupID),
 			zap.Error(err),
@@ -321,7 +338,8 @@ func (s Server) GetAPISlotifyGroupsSlotifyGroupIDUsers(w http.ResponseWriter, r
 	}
 
 	if count == 0 {
-		s.Logger.Error("slotifyGroup api: slotifyGroup members of non-existent slotifyGroup requested",
+		s.Logger.Errorf("%s: slotifyGroup api: slotifyGroup members of non-existent slotifyGroup requested, ",
+			reqID,
 			zap.Uint32("slotifyGroupID", slotifyGroupID), zap.Error(err))
 		sendError(w, http.StatusNotFound,
 			fmt.Sprintf("slotifyGroup api: slotifyGroup with id(%d) does not exist", slotifyGroupID))
@@ -330,7 +348,7 @@ func (s Server) GetAPISlotifyGroupsSlotifyGroupIDUsers(w http.ResponseWriter, r
 
 	users, err := s.DB.GetAllSlotifyGroupMembers(ctx, slotifyGroupID)
 	if err != nil {
-		s.Logger.Error("slotifyGroup api: failed to get users of a group",
+		s.Logger.Errorf("%s: slotifyGroup api: failed to get users of a group, ", reqID,
 			zap.Uint32("slotifyGroupID",
 				slotifyGroupID),
 			zap.Error(err),
diff --git a/integration/invite_impl_test.go b/integration/invite_impl_test.go
index 78588100..d85ccf3f 100644
--- a/integration/invite_impl_test.go
+++ b/integration/invite_impl_test.go
@@ -13,6 +13,7 @@ import (
 	"github.com/SlotifyApp/slotify-backend/api"
 	"github.com/SlotifyApp/slotify-backend/mocks"
 	"github.com/SlotifyApp/slotify-backend/testutil"
+	"github.com/google/uuid"
 	openapi_types "github.com/oapi-codegen/runtime/types"
 	"github.com/stretchr/testify/require"
 	"go.uber.org/mock/gomock"
@@ -94,6 +95,7 @@ func TestInvites_PostInvites(t *testing.T) {
 			req.Header.Add("Content-Type", "application/json")
 
 			ctx := context.WithValue(req.Context(), api.UserIDCtxKey{}, fromUser.Id)
+			ctx = context.WithValue(ctx, api.RequestIDCtxKey{}, uuid.NewString())
 			req = req.WithContext(ctx)
 
 			server.PostAPIInvites(rr, req)
diff --git a/integration/slotify_group_impl_test.go b/integration/slotify_group_impl_test.go
index 93960e19..97450765 100644
--- a/integration/slotify_group_impl_test.go
+++ b/integration/slotify_group_impl_test.go
@@ -14,6 +14,7 @@ import (
 	"github.com/SlotifyApp/slotify-backend/mocks"
 	"github.com/SlotifyApp/slotify-backend/testutil"
 	"github.com/brianvoe/gofakeit/v7"
+	"github.com/google/uuid"
 	"github.com/stretchr/testify/require"
 	"go.uber.org/mock/gomock"
 )
@@ -79,6 +80,7 @@ func TestSlotifyGroup_PostSlotifyGroups(t *testing.T) {
 			req.Header.Add("Content-Type", "application/json")
 
 			ctx := context.WithValue(req.Context(), api.UserIDCtxKey{}, user.Id)
+			ctx = context.WithValue(ctx, api.RequestIDCtxKey{}, uuid.NewString())
 			req = req.WithContext(ctx)
 
 			server.PostAPISlotifyGroups(rr, req)
@@ -143,6 +145,9 @@ func TestSlotifyGroup_DeleteSlotifyGroupsSlotifyGroupID(t *testing.T) {
 			rr := httptest.NewRecorder()
 			req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/slotify-groups/%d", tt.slotifyGroupID), nil)
 
+			ctx := context.WithValue(req.Context(), api.RequestIDCtxKey{}, uuid.NewString())
+			req = req.WithContext(ctx)
+
 			server.DeleteAPISlotifyGroupsSlotifyGroupID(rr, req, tt.slotifyGroupID)
 
 			testutil.OpenAPIValidateTest(t, rr, req)
@@ -193,6 +198,8 @@ func TestSlotifyGroup_GetSlotifyGroupsSlotifyGroupID(t *testing.T) {
 
 			rr := httptest.NewRecorder()
 			req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/slotify-groups/%d", tt.slotifyGroupID), nil)
+			ctx := context.WithValue(req.Context(), api.RequestIDCtxKey{}, uuid.NewString())
+			req = req.WithContext(ctx)
 
 			server.GetAPISlotifyGroupsSlotifyGroupID(rr, req, tt.slotifyGroupID)
 
@@ -259,6 +266,9 @@ func TestSlotifyGroup_GetSlotifyGroupsSlotifyGroupIDUsers(t *testing.T) {
 			rr := httptest.NewRecorder()
 			req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/slotify-groups/%d/users", tt.slotifyGroupID), nil)
 
+			ctx := context.WithValue(req.Context(), api.RequestIDCtxKey{}, uuid.NewString())
+			req = req.WithContext(ctx)
+
 			server.GetAPISlotifyGroupsSlotifyGroupIDUsers(rr, req, tt.slotifyGroupID)
 
 			testutil.OpenAPIValidateTest(t, rr, req)
@@ -326,6 +336,8 @@ func TestSlotifyGroup_GetAPISlotifyGroupsMe(t *testing.T) {
 			req := httptest.NewRequest(http.MethodGet, "/api/slotify-groups/me", nil)
 
 			ctx := context.WithValue(req.Context(), api.UserIDCtxKey{}, tt.userID)
+			ctx = context.WithValue(ctx, api.RequestIDCtxKey{}, uuid.NewString())
+
 			req = req.WithContext(ctx)
 
 			server.GetAPISlotifyGroupsMe(rr, req)


Reviews:
user: login: saths008
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:53:30Z
user: login: fenn7
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:55:08Z
user: login: saths008
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:58:51Z
user: login: saths008
body: More comments about `ReadReqUUID`
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T08:11:50Z
user: login: saths008
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T08:24:31Z

Review comments:
diff_hunk: @@ -0,0 +1,32 @@
+package api
+
+import (
+	"context"
+	"errors"
+	"net/http"
+
+	"github.com/google/uuid"
+)
+
+type ctxKey string
+
+const (
+	ReqUUIDKey ctxKey = "reqUUID"
+	ReqHeader  string = "X-Request-ID"
+)
+
+func WriteReqUUID(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) {
+		reqUUID := uuid.New().String()
+		ctx := context.WithValue(req.Context(), ReqUUIDKey, reqUUID)
+		writer.Header().Set(ReqHeader, reqUUID)
+		next.ServeHTTP(writer, req.WithContext(ctx))
+	})
+}
+
+func ReadReqUUID(req *http.Request) string {
+	if reqUUID, ok := req.Context().Value(ReqUUIDKey).(string); ok {
+		return reqUUID
+	}
+	return errors.New("error: unable to fetch id of this request").Error()
path: api/request_id.go
user: login: saths008
body: the return type should be (string, error) so something like 

```go
return "", errors.New("blah")
```
 for the last line
created_at: 2025-03-06T17:47:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,32 @@
+package api
+
+import (
+	"context"
+	"errors"
+	"net/http"
+
+	"github.com/google/uuid"
+)
+
+type ctxKey string
path: api/request_id.go
user: login: saths008
body: ```
// UserIDCtxKey is the key in context value for the user id value.
type UserIDCtxKey struct{}
```

create a ctx key like this, `reqUUID` seems like a common name that some other pkg may inject into the context. If you do a struct then there are no conflicts.
created_at: 2025-03-06T17:50:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -36,15 +27,16 @@ func (s Server) RenderEvent(w http.ResponseWriter, r *http.Request) {
 	f, ok := w.(http.Flusher)
 
 	if !ok {
-		s.Logger.Error("failed to flush event stream headers, responsewriter did not implement Flusher interface")
+		s.Logger.Error("failed to flush event stream headers, responsewriter did not implement Flusher interface" +
+			", request ID: " + reqUUID)
 		sendError(w, http.StatusInternalServerError, "failed to flush event stream headers")
 		return
 	}
 
 	f.Flush()
 
 	if err := s.NotificationService.RegisterUserClient(s.Logger, userID, w); err != nil {
-		s.Logger.Errorf("failed to register user client", zap.Error(err))
+		s.Logger.Errorf("failed to register user client, request ID: "+reqUUID+", ", zap.Error(err))
path: api/notification_impl.go
user: login: saths008
body: have req id be a zap object instead of the weird formatting we have here:
 ```go
 s.Logger.Error("failed to register user client, zap.String("request id", reqUUID), zap.Error(err))
 ```
created_at: 2025-03-06T17:51:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,83 +6,88 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
-	"os"
 	"time"
 
 	"github.com/SlotifyApp/slotify-backend/database"
 	"github.com/avast/retry-go"
+	openapi_types "github.com/oapi-codegen/runtime/types"
 	"go.uber.org/zap"
 )
 
 // (POST /api/invites) Create a new invite.
 func (s Server) PostAPIInvites(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(r.Context(), 4*database.DatabaseTimeout)
 	defer cancel()
+	reqUUID := ReadReqUUID(r)
 
 	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
 	if !ok {
-		s.Logger.Error("failed to get userid from request context")
+		s.Logger.Error("failed to get userid from request context, request ID: " + reqUUID)
 		sendError(w, http.StatusUnauthorized, "Try again later.")
 		return
 	}
-
 	var invitesCreateBody PostAPIInvitesJSONRequestBody
 	var err error
 	if err = json.NewDecoder(r.Body).Decode(&invitesCreateBody); err != nil {
-		s.Logger.Error(ErrUnmarshalBody, zap.Object("body", invitesCreateBody), zap.Error(err))
+		s.Logger.Error(ErrUnmarshalBody.Error()+"request ID: "+reqUUID+", ",
+			zap.Object("body", invitesCreateBody), zap.Error(err))
 		sendError(w, http.StatusBadRequest, ErrUnmarshalBody.Error())
path: api/invite_impl.go
user: login: saths008
body: Don't we also want to send the request id as part of the error being sent back so the user has some reference which they can report?
created_at: 2025-03-06T17:53:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,83 +6,88 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
-	"os"
 	"time"
 
 	"github.com/SlotifyApp/slotify-backend/database"
 	"github.com/avast/retry-go"
+	openapi_types "github.com/oapi-codegen/runtime/types"
 	"go.uber.org/zap"
 )
 
 // (POST /api/invites) Create a new invite.
 func (s Server) PostAPIInvites(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(r.Context(), 4*database.DatabaseTimeout)
 	defer cancel()
+	reqUUID := ReadReqUUID(r)
 
 	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
 	if !ok {
-		s.Logger.Error("failed to get userid from request context")
+		s.Logger.Error("failed to get userid from request context, request ID: " + reqUUID)
 		sendError(w, http.StatusUnauthorized, "Try again later.")
 		return
 	}
-
 	var invitesCreateBody PostAPIInvitesJSONRequestBody
 	var err error
 	if err = json.NewDecoder(r.Body).Decode(&invitesCreateBody); err != nil {
-		s.Logger.Error(ErrUnmarshalBody, zap.Object("body", invitesCreateBody), zap.Error(err))
+		s.Logger.Error(ErrUnmarshalBody.Error()+"request ID: "+reqUUID+", ",
+			zap.Object("body", invitesCreateBody), zap.Error(err))
 		sendError(w, http.StatusBadRequest, ErrUnmarshalBody.Error())
path: api/invite_impl.go
user: login: fenn7
body: I was under the impression that the request ID stuff was meant mostly for backend logging but i can output it for the user for this purpose
created_at: 2025-03-06T17:55:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,32 @@
+package api
+
+import (
+	"context"
+	"errors"
+	"net/http"
+
+	"github.com/google/uuid"
+)
+
+type ctxKey string
+
+const (
+	ReqUUIDKey ctxKey = "reqUUID"
+	ReqHeader  string = "X-Request-ID"
+)
+
+func WriteReqUUID(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) {
+		reqUUID := uuid.New().String()
+		ctx := context.WithValue(req.Context(), ReqUUIDKey, reqUUID)
+		writer.Header().Set(ReqHeader, reqUUID)
+		next.ServeHTTP(writer, req.WithContext(ctx))
+	})
+}
+
+func ReadReqUUID(req *http.Request) string {
+	if reqUUID, ok := req.Context().Value(ReqUUIDKey).(string); ok {
+		return reqUUID
+	}
+	return errors.New("error: unable to fetch id of this request").Error()
path: api/request_id.go
user: login: saths008
body: take a look at this article: https://gobyexample.com/errors
created_at: 2025-03-06T17:58:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,83 +6,88 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
-	"os"
 	"time"
 
 	"github.com/SlotifyApp/slotify-backend/database"
 	"github.com/avast/retry-go"
+	openapi_types "github.com/oapi-codegen/runtime/types"
 	"go.uber.org/zap"
 )
 
 // (POST /api/invites) Create a new invite.
 func (s Server) PostAPIInvites(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(r.Context(), 4*database.DatabaseTimeout)
 	defer cancel()
+	reqUUID := ReadReqUUID(r)
 
 	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
 	if !ok {
-		s.Logger.Error("failed to get userid from request context")
+		s.Logger.Error("failed to get userid from request context, request ID: " + reqUUID)
 		sendError(w, http.StatusUnauthorized, "Try again later.")
 		return
 	}
-
 	var invitesCreateBody PostAPIInvitesJSONRequestBody
 	var err error
 	if err = json.NewDecoder(r.Body).Decode(&invitesCreateBody); err != nil {
-		s.Logger.Error(ErrUnmarshalBody, zap.Object("body", invitesCreateBody), zap.Error(err))
+		s.Logger.Error(ErrUnmarshalBody.Error()+"request ID: "+reqUUID+", ",
+			zap.Object("body", invitesCreateBody), zap.Error(err))
 		sendError(w, http.StatusBadRequest, ErrUnmarshalBody.Error())
path: api/invite_impl.go
user: login: saths008
body: might be useful so someone doesnt manually have to sift through logs and determine exactly what time a user accessed some route
created_at: 2025-03-06T18:07:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -43,6 +43,7 @@ func main() {
 
 	r := mux.NewRouter()
 
+	r.Use(api.WriteReqUUID)
path: cmd/server/main.go
user: login: saths008
body: I think you can just add to the `ApplyMiddleware` function
created_at: 2025-03-07T08:07:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,8 +20,10 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 	defer cancel()
 
 	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
+	reqUUID, _ := ReadReqUUID(r)
+	uuidStr := zap.String("request ID: ", reqUUID)
path: api/slotify_group_impl.go
user: login: saths008
body: just do the `zap.string` inline with the log, it makes it clearer
created_at: 2025-03-07T08:07:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,8 +20,10 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 	defer cancel()
 
 	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
+	reqUUID, _ := ReadReqUUID(r)
path: api/slotify_group_impl.go
user: login: saths008
body: you are ignoring the error, this is like having a try-catch but doing nothing in the catch. you need to do something like

```go
var reqUUID string
if reqUUID, err = ReadReqUUID(r); err != nil{
//error was not nil, do something
}


```
created_at: 2025-03-07T08:09:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,8 +20,10 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 	defer cancel()
 
 	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
+	reqUUID, _ := ReadReqUUID(r)
path: api/slotify_group_impl.go
user: login: saths008
body: Also do this after the `!ok` check of userID
created_at: 2025-03-07T08:09:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,8 +20,10 @@ func (s Server) DeleteSlotifyGroupsSlotifyGroupIDLeaveMe(w http.ResponseWriter,
 	defer cancel()
 
 	userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32)
+	reqUUID, _ := ReadReqUUID(r)
path: api/slotify_group_impl.go
user: login: saths008
body: Also get rid of the `ReadReqUUID` function, just do it inline like 
```go
userID, ok := r.Context().Value(UserIDCtxKey{}).(uint32
```
you cant make a function simpler than that 
created_at: 2025-03-07T08:11:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+package api
+
+import (
+	"context"
+	"errors"
+	"net/http"
+
+	"github.com/google/uuid"
+)
+
+type uuidCtxKey struct {
+	uuid string
+}
+
+type ctxKey string
+
+const (
+	ReqUUIDKey ctxKey = "reqUUID"
+	ReqHeader  string = "X-Request-ID"
+)
+
+func WriteReqUUID(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) {
+		reqUUID := uuid.New().String()
+		reqCtxKey := uuidCtxKey{uuid: reqUUID}
+		ctx := context.WithValue(req.Context(), ReqUUIDKey, reqCtxKey)
path: api/request_id.go
user: login: saths008
body: Look at `middleware.go` line 103, you want the struct to be the key. Also just move this into `middleware.go`, you only need the `Write` fn anyway
created_at: 2025-03-07T08:24:31Z
author_association: CONTRIBUTOR

PR comments:
user: login: saths008
created_at: 2025-03-07T08:05:48Z
body: Dropped the `temp` commit again containing vendor files. To sync ur local repo:
```bash
git pull --rebase

# fix merge conflicts or whatever
git log # check that 'temp' is no longer a commit
git add -A # stage all your files if you're happy
git push
```
author_association: CONTRIBUTOR
user: login: saths008
created_at: 2025-03-07T09:05:41Z
body: Also the request id is in the header of the response right? So we may not even have to `sendError` with this and just have the frontend grab it from the headers?
author_association: CONTRIBUTOR