diff --git a/api/graphql/generated.go b/api/graphql/generated.go
index 1c40df4..a3be4d1 100644
--- a/api/graphql/generated.go
+++ b/api/graphql/generated.go
@@ -2985,9 +2985,9 @@ func (ec *executionContext) _Media_exif(ctx context.Context, field graphql.Colle
 	if resTmp == nil {
 		return graphql.Null
 	}
-	res := resTmp.(models.MediaEXIF)
+	res := resTmp.(*models.MediaEXIF)
 	fc.Result = res
-	return ec.marshalOMediaEXIF2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaEXIF(ctx, field.Selections, res)
+	return ec.marshalOMediaEXIF2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaEXIF(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _Media_videoMetadata(ctx context.Context, field graphql.CollectedField, obj *models.Media) (ret graphql.Marshaler) {
@@ -9753,8 +9753,11 @@ func (ec *executionContext) marshalOMedia2ᚖgithubᚗcomᚋviktorstrateᚋphoto
 	return ec._Media(ctx, sel, v)
 }
 
-func (ec *executionContext) marshalOMediaEXIF2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaEXIF(ctx context.Context, sel ast.SelectionSet, v models.MediaEXIF) graphql.Marshaler {
-	return ec._MediaEXIF(ctx, sel, &v)
+func (ec *executionContext) marshalOMediaEXIF2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaEXIF(ctx context.Context, sel ast.SelectionSet, v *models.MediaEXIF) graphql.Marshaler {
+	if v == nil {
+		return graphql.Null
+	}
+	return ec._MediaEXIF(ctx, sel, v)
 }
 
 func (ec *executionContext) marshalOMediaURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaURL(ctx context.Context, sel ast.SelectionSet, v *models.MediaURL) graphql.Marshaler {
diff --git a/api/graphql/models/media.go b/api/graphql/models/media.go
index 7369271..2651e04 100644
--- a/api/graphql/models/media.go
+++ b/api/graphql/models/media.go
@@ -17,7 +17,7 @@ type Media struct {
 	AlbumID         uint
 	Album           Album
 	ExifID          *uint
-	Exif            MediaEXIF
+	Exif            *MediaEXIF
 	MediaURL        []MediaURL
 	DateShot        time.Time
 	DateImported    time.Time
@@ -29,6 +29,10 @@ type Media struct {
 	SideCarHash     *string
 }
 
+func (Media) TableName() string {
+	return "media"
+}
+
 type MediaPurpose string
 
 const (
diff --git a/api/graphql/resolvers/media.go b/api/graphql/resolvers/media.go
index 2633607..fbf8a39 100644
--- a/api/graphql/resolvers/media.go
+++ b/api/graphql/resolvers/media.go
@@ -2,14 +2,13 @@ package resolvers
 
 import (
 	"context"
-	"database/sql"
-	"strings"
 
 	"github.com/pkg/errors"
 	api "github.com/viktorstrate/photoview/api/graphql"
 	"github.com/viktorstrate/photoview/api/graphql/auth"
 	"github.com/viktorstrate/photoview/api/graphql/models"
 	"github.com/viktorstrate/photoview/api/scanner"
+	"gorm.io/gorm"
 )
 
 func (r *queryResolver) MyMedia(ctx context.Context, filter *models.Filter) ([]*models.Media, error) {
@@ -23,18 +22,19 @@ func (r *queryResolver) MyMedia(ctx context.Context, filter *models.Filter) ([]*
 		return nil, err
 	}
 
-	rows, err := r.Database.Query(`
+	var media []*models.Media
+	err = r.Database.Raw(`
 		SELECT media.* FROM media, album
-		WHERE media.album_id = album.album_id AND album.owner_id = ?
+		WHERE media.album_id = albums.id AND albums.owner_id = ?
 		AND media.media_id IN (
-			SELECT media_id FROM media_url WHERE media_url.media_id = media.media_id
+			SELECT media_id FROM media_url WHERE media_url.media_id = media.id
 		)
-	`+filterSQL, user.UserID)
+	`+filterSQL, user.ID).Scan(&media).Error
 	if err != nil {
 		return nil, err
 	}
 
-	return models.NewMediaFromRows(rows)
+	return media, nil
 }
 
 func (r *queryResolver) Media(ctx context.Context, id int) (*models.Media, error) {
@@ -43,21 +43,21 @@ func (r *queryResolver) Media(ctx context.Context, id int) (*models.Media, error
 		return nil, auth.ErrUnauthorized
 	}
 
-	row := r.Database.QueryRow(`
+	var media models.Media
+	err := r.Database.Raw(`
 		SELECT media.* FROM media
-		JOIN album ON media.album_id = album.album_id
+		JOIN albums ON media.album_id = albums.id
 		WHERE media.media_id = ? AND album.owner_id = ?
 		AND media.media_id IN (
 			SELECT media_id FROM media_url WHERE media_url.media_id = media.media_id
 		)
-	`, id, user.UserID)
+	`, id, user.ID).Scan(&media).Error
 
-	media, err := models.NewMediaFromRow(row)
 	if err != nil {
 		return nil, errors.Wrap(err, "could not get media by media_id and user_id from database")
 	}
 
-	return media, nil
+	return &media, nil
 }
 
 func (r *queryResolver) MediaList(ctx context.Context, ids []int) ([]*models.Media, error) {
@@ -70,31 +70,28 @@ func (r *queryResolver) MediaList(ctx context.Context, ids []int) ([]*models.Med
 		return nil, errors.New("no ids provided")
 	}
 
-	mediaIDQuestions := strings.Repeat("?,", len(ids))[:len(ids)*2-1]
+	var media []*models.Media
+	// err := r.Database.
+	// 	Select("media.*").
+	// 	Joins("Album").
+	// 	Where("media.id IN ?", ids).
+	// 	Where("album.owner_id = ?", user.ID).
+	// 	Where("media.id IN (?)", r.Database.Model(&models.MediaURL{}).Select("media_id").Where("media_url.media_id = media.id")).
+	// 	Scan(&media).Error
 
-	queryArgs := make([]interface{}, 0)
-	for _, id := range ids {
-		queryArgs = append(queryArgs, id)
-	}
-	queryArgs = append(queryArgs, user.UserID)
-
-	rows, err := r.Database.Query(`
+	err := r.Database.Raw(`
 		SELECT media.* FROM media
-		JOIN album ON media.album_id = album.album_id
-		WHERE media.media_id IN (`+mediaIDQuestions+`) AND album.owner_id = ?
+		JOIN albums AS album ON media.album_id = album.id
+		WHERE media.media_id IN ? AND album.owner_id = ?
 		AND media.media_id IN (
 			SELECT media_id FROM media_url WHERE media_url.media_id = media.media_id
 		)
-	`, queryArgs...)
+	`, ids, user.ID).Error
+
 	if err != nil {
 		return nil, errors.Wrap(err, "could not get media list by media_id and user_id from database")
 	}
 
-	media, err := models.NewMediaFromRows(rows)
-	if err != nil {
-		return nil, errors.Wrap(err, "could not convert database rows to media")
-	}
-
 	return media, nil
 }
 
@@ -106,25 +103,20 @@ func (r *Resolver) Media() api.MediaResolver {
 	return &mediaResolver{r}
 }
 
-func (r *mediaResolver) Shares(ctx context.Context, obj *models.Media) ([]*models.ShareToken, error) {
-	rows, err := r.Database.Query("SELECT * FROM share_token WHERE media_id = ?", obj.MediaID)
-	if err != nil {
-		return nil, errors.Wrapf(err, "get shares for media (%s)", obj.Path)
+func (r *mediaResolver) Shares(ctx context.Context, media *models.Media) ([]*models.ShareToken, error) {
+	var shareTokens []*models.ShareToken
+	if err := r.Database.Where("media_id = ?", media.ID).Find(shareTokens).Error; err != nil {
+		return nil, errors.Wrapf(err, "get shares for media (%s)", media.Path)
 	}
 
-	return models.NewShareTokensFromRows(rows)
+	return shareTokens, nil
 }
 
-func (r *mediaResolver) Downloads(ctx context.Context, obj *models.Media) ([]*models.MediaDownload, error) {
+func (r *mediaResolver) Downloads(ctx context.Context, media *models.Media) ([]*models.MediaDownload, error) {
 
-	rows, err := r.Database.Query("SELECT * FROM media_url WHERE media_id = ?", obj.MediaID)
-	if err != nil {
-		return nil, errors.Wrapf(err, "get downloads for media (%s)", obj.Path)
-	}
-
-	mediaUrls, err := models.NewMediaURLFromRows(rows)
-	if err != nil {
-		return nil, err
+	var mediaUrls []*models.MediaURL
+	if err := r.Database.Where("media_id = ?", media.ID).Find(&mediaUrls).Error; err != nil {
+		return nil, errors.Wrapf(err, "get downloads for media (%s)", media.Path)
 	}
 
 	downloads := make([]*models.MediaDownload, 0)
@@ -154,112 +146,110 @@ func (r *mediaResolver) Downloads(ctx context.Context, obj *models.Media) ([]*mo
 	return downloads, nil
 }
 
-func (r *mediaResolver) HighRes(ctx context.Context, obj *models.Media) (*models.MediaURL, error) {
-	// Try high res first, then
-	web_types_questions := strings.Repeat("?,", len(scanner.WebMimetypes))[:len(scanner.WebMimetypes)*2-1]
-	args := make([]interface{}, 0)
-	args = append(args, obj.MediaID, models.PhotoHighRes, models.MediaOriginal)
-	for _, webtype := range scanner.WebMimetypes {
-		args = append(args, webtype)
-	}
+func (r *mediaResolver) HighRes(ctx context.Context, media *models.Media) (*models.MediaURL, error) {
+	var url models.MediaURL
+	err := r.Database.
+		Where("media_url = ?", media.ID).
+		Where("purpose = ? OR (purpose = ? AND content_type IN ?)", models.PhotoHighRes, models.MediaOriginal, scanner.WebMimetypes).
+		First(&url).Error
 
-	row := r.Database.QueryRow(`
-		SELECT * FROM media_url WHERE media_id = ? AND
-		(
-			purpose = ? OR (purpose = ? AND content_type IN (`+web_types_questions+`))
-		) LIMIT 1
-	`, args...)
-
-	url, err := models.NewMediaURLFromRow(row)
 	if err != nil {
-		if err == sql.ErrNoRows {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
 			return nil, nil
 		} else {
-			return nil, errors.Wrapf(err, "could not query high-res (%s)", obj.Path)
+			return nil, errors.Wrapf(err, "could not query high-res (%s)", media.Path)
 		}
 	}
 
-	return url, nil
+	return &url, nil
 }
 
-func (r *mediaResolver) Thumbnail(ctx context.Context, obj *models.Media) (*models.MediaURL, error) {
-	row := r.Database.QueryRow("SELECT * FROM media_url WHERE media_id = ? AND (purpose = ? OR purpose = ?)", obj.MediaID, models.PhotoThumbnail, models.VideoThumbnail)
+func (r *mediaResolver) Thumbnail(ctx context.Context, media *models.Media) (*models.MediaURL, error) {
+	var url models.MediaURL
+	err := r.Database.
+		Where("media_url = ?", media.ID).
+		Where("purpose = ? OR purpose = ?", models.PhotoThumbnail, models.VideoThumbnail).
+		First(&url).Error
 
-	url, err := models.NewMediaURLFromRow(row)
 	if err != nil {
-		return nil, errors.Wrapf(err, "could not query thumbnail (%s)", obj.Path)
+		return nil, errors.Wrapf(err, "could not query thumbnail (%s)", media.Path)
 	}
 
-	return url, nil
+	return &url, nil
 }
 
-func (r *mediaResolver) VideoWeb(ctx context.Context, obj *models.Media) (*models.MediaURL, error) {
-	row := r.Database.QueryRow("SELECT * FROM media_url WHERE media_id = ? AND (purpose = ?)", obj.MediaID, models.VideoWeb)
+func (r *mediaResolver) VideoWeb(ctx context.Context, media *models.Media) (*models.MediaURL, error) {
+
+	var url models.MediaURL
+	err := r.Database.
+		Where("media_url = ?", media.ID).
+		Where("purpose = ?", models.VideoWeb).
+		First(&url).Error
 
-	url, err := models.NewMediaURLFromRow(row)
 	if err != nil {
-		if err == sql.ErrNoRows {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
 			return nil, nil
 		} else {
-			return nil, errors.Wrapf(err, "could not query video web-format url (%s)", obj.Path)
+			return nil, errors.Wrapf(err, "could not query video web-format url (%s)", media.Path)
 		}
 	}
 
-	return url, nil
+	return &url, nil
 }
 
-func (r *mediaResolver) Album(ctx context.Context, obj *models.Media) (*models.Album, error) {
-	row := r.Database.QueryRow("SELECT album.* from media JOIN album ON media.album_id = album.album_id WHERE media_id = ?", obj.MediaID)
-	return models.NewAlbumFromRow(row)
-}
+// func (r *mediaResolver) Album(ctx context.Context, media *models.Media) (*models.Album, error) {
 
-func (r *mediaResolver) Exif(ctx context.Context, obj *models.Media) (*models.MediaEXIF, error) {
-	row := r.Database.QueryRow("SELECT media_exif.* FROM media NATURAL JOIN media_exif WHERE media.media_id = ?", obj.MediaID)
+// 	r.Database.Model(&media).Joins("Album")
 
-	exif, err := models.NewMediaExifFromRow(row)
-	if err != nil {
-		if err == sql.ErrNoRows {
-			return nil, nil
-		} else {
-			return nil, errors.Wrapf(err, "could not get exif of media from database")
-		}
-	}
+// 	row := r.Database.QueryRow("SELECT album.* from media JOIN album ON media.album_id = album.album_id WHERE media_id = ?", media.MediaID)
+// 	return models.NewAlbumFromRow(row)
+// }
 
-	return exif, nil
-}
+// func (r *mediaResolver) Exif(ctx context.Context, obj *models.Media) (*models.MediaEXIF, error) {
+// 	row := r.Database.QueryRow("SELECT media_exif.* FROM media NATURAL JOIN media_exif WHERE media.media_id = ?", obj.MediaID)
 
-func (r *mediaResolver) VideoMetadata(ctx context.Context, obj *models.Media) (*models.VideoMetadata, error) {
-	row := r.Database.QueryRow("SELECT video_metadata.* FROM media JOIN video_metadata ON media.video_metadata_id = video_metadata.metadata_id WHERE media.media_id = ?", obj.MediaID)
+// 	exif, err := models.NewMediaExifFromRow(row)
+// 	if err != nil {
+// 		if errors.Is(err, gorm.ErrRecordNotFound) {
+// 			return nil, nil
+// 		} else {
+// 			return nil, errors.Wrapf(err, "could not get exif of media from database")
+// 		}
+// 	}
 
-	metadata, err := models.NewVideoMetadataFromRow(row)
-	if err != nil {
-		if err == sql.ErrNoRows {
-			return nil, nil
-		} else {
-			return nil, errors.Wrapf(err, "could not get video metadata of media from database")
-		}
-	}
+// 	return exif, nil
+// }
 
-	return metadata, nil
-}
+// func (r *mediaResolver) VideoMetadata(ctx context.Context, obj *models.Media) (*models.VideoMetadata, error) {
+// 	row := r.Database.QueryRow("SELECT video_metadata.* FROM media JOIN video_metadata ON media.video_metadata_id = video_metadata.metadata_id WHERE media.media_id = ?", obj.MediaID)
+
+// 	metadata, err := models.NewVideoMetadataFromRow(row)
+// 	if err != nil {
+// 		if errors.Is(err, gorm.ErrRecordNotFound) {
+// 			return nil, nil
+// 		} else {
+// 			return nil, errors.Wrapf(err, "could not get video metadata of media from database")
+// 		}
+// 	}
+
+// 	return metadata, nil
+// }
 
 func (r *mutationResolver) FavoriteMedia(ctx context.Context, mediaID int, favorite bool) (*models.Media, error) {
 
 	user := auth.UserFromContext(ctx)
 
-	row := r.Database.QueryRow("SELECT media.* FROM media JOIN album ON media.album_id = album.album_id WHERE media.media_id = ? AND album.owner_id = ?", mediaID, user.UserID)
+	var media models.Media
 
-	media, err := models.NewMediaFromRow(row)
-	if err != nil {
+	if err := r.Database.Joins("Album").Where("Album.owner_id = ?", user.ID).First(&media, mediaID).Error; err != nil {
 		return nil, err
 	}
 
-	_, err = r.Database.Exec("UPDATE media SET favorite = ? WHERE media_id = ?", favorite, media.MediaID)
-	if err != nil {
-		return nil, errors.Wrap(err, "failed to update media favorite on database")
-	}
-
 	media.Favorite = favorite
 
-	return media, nil
+	if err := r.Database.Save(&media).Error; err != nil {
+		return nil, errors.Wrap(err, "failed to update media favorite on database")
+	}
+
+	return &media, nil
 }
diff --git a/api/graphql/resolvers/user.go b/api/graphql/resolvers/user.go
index 828514c..52576b6 100644
--- a/api/graphql/resolvers/user.go
+++ b/api/graphql/resolvers/user.go
@@ -2,12 +2,12 @@ package resolvers
 
 import (
 	"context"
-	"errors"
-	"log"
 
+	"github.com/pkg/errors"
 	"github.com/viktorstrate/photoview/api/graphql/auth"
 	"github.com/viktorstrate/photoview/api/graphql/models"
 	"golang.org/x/crypto/bcrypt"
+	"gorm.io/gorm"
 )
 
 // func (r *Resolver) User() UserResolver {
@@ -23,13 +23,8 @@ func (r *queryResolver) User(ctx context.Context, filter *models.Filter) ([]*mod
 		return nil, err
 	}
 
-	rows, err := r.Database.Query("SELECT * FROM user" + filterSQL)
-	if err != nil {
-		return nil, err
-	}
-	defer rows.Close()
-
-	users, err := models.NewUsersFromRows(rows)
+	var users []*models.User
+	err = r.Database.Raw("SELECT * FROM users" + filterSQL).Scan(&users).Error
 	if err != nil {
 		return nil, err
 	}
@@ -56,20 +51,20 @@ func (r *mutationResolver) AuthorizeUser(ctx context.Context, username string, p
 		}, nil
 	}
 
-	tx, err := r.Database.Begin()
-	if err != nil {
-		return nil, err
-	}
-
 	var token *models.AccessToken
 
-	token, err = user.GenerateAccessToken(tx)
-	if err != nil {
-		tx.Rollback()
-		return nil, err
-	}
+	transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
+		token, err = user.GenerateAccessToken(tx)
+		if err != nil {
+			return err
+		}
 
-	tx.Commit()
+		return nil
+	})
+
+	if transactionError != nil {
+		return nil, transactionError
+	}
 
 	return &models.AuthorizeResult{
 		Success: true,
@@ -78,28 +73,29 @@ func (r *mutationResolver) AuthorizeUser(ctx context.Context, username string, p
 	}, nil
 }
 func (r *mutationResolver) RegisterUser(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error) {
-	tx, err := r.Database.Begin()
-	if err != nil {
-		return nil, err
-	}
 
-	user, err := models.RegisterUser(tx, username, &password, rootPath, false)
-	if err != nil {
-		tx.Rollback()
+	var token *models.AccessToken
+
+	transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
+		user, err := models.RegisterUser(tx, username, &password, rootPath, false)
+		if err != nil {
+			return err
+		}
+
+		token, err = user.GenerateAccessToken(tx)
+		if err != nil {
+			tx.Rollback()
+			return err
+		}
+
+		return nil
+	})
+
+	if transactionError != nil {
 		return &models.AuthorizeResult{
 			Success: false,
-			Status:  err.Error(),
-		}, nil
-	}
-
-	token, err := user.GenerateAccessToken(tx)
-	if err != nil {
-		tx.Rollback()
-		return nil, err
-	}
-
-	if err := tx.Commit(); err != nil {
-		return nil, err
+			Status:  transactionError.Error(),
+		}, transactionError
 	}
 
 	return &models.AuthorizeResult{
@@ -119,35 +115,33 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
 		return nil, errors.New("not initial setup")
 	}
 
-	tx, err := r.Database.Begin()
-	if err != nil {
-		return nil, err
-	}
+	var token *models.AccessToken
 
-	if _, err := tx.Exec("UPDATE site_info SET initial_setup = false"); err != nil {
-		tx.Rollback()
-		return nil, err
-	}
+	transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
+		if err := tx.Exec("UPDATE site_info SET initial_setup = false").Error; err != nil {
+			return err
+		}
 
-	user, err := models.RegisterUser(tx, username, &password, rootPath, true)
-	if err != nil {
-		tx.Rollback()
+		user, err := models.RegisterUser(tx, username, &password, rootPath, true)
+		if err != nil {
+			return err
+		}
+
+		token, err = user.GenerateAccessToken(tx)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+
+	if transactionError != nil {
 		return &models.AuthorizeResult{
 			Success: false,
 			Status:  err.Error(),
 		}, nil
 	}
 
-	token, err := user.GenerateAccessToken(tx)
-	if err != nil {
-		tx.Rollback()
-		return nil, err
-	}
-
-	if err := tx.Commit(); err != nil {
-		return nil, err
-	}
-
 	return &models.AuthorizeResult{
 		Success: true,
 		Status:  "ok",
@@ -158,34 +152,23 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
 // Admin queries
 func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *string, rootPath *string, password *string, admin *bool) (*models.User, error) {
 
-	user_rows, err := r.Database.Query("SELECT * FROM user WHERE user_id = ?", id)
-	if err != nil {
+	if username == nil && rootPath == nil && password == nil && admin == nil {
+		return nil, errors.New("no updates requested")
+	}
+
+	var user models.User
+	if err := r.Database.First(&user, id).Error; err != nil {
 		return nil, err
 	}
-	if user_rows.Next() == false {
-		return nil, errors.New("user not found")
-	}
-	user_rows.Close()
-
-	update_str := ""
-	update_args := make([]interface{}, 0)
 
 	if username != nil {
-		update_str += "username = ?, "
-		update_args = append(update_args, username)
+		user.Username = *username
 	}
-	if rootPath != nil {
-		if !models.ValidRootPath(*rootPath) {
-			return nil, errors.New("invalid root path")
-		}
 
-		update_str += "root_path = ?, "
-		update_args = append(update_args, rootPath)
-	}
-	if admin != nil {
-		update_str += "admin = ?, "
-		update_args = append(update_args, admin)
+	if rootPath != nil {
+		user.RootPath = *rootPath
 	}
+
 	if password != nil {
 		hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
 		if err != nil {
@@ -193,54 +176,36 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *str
 		}
 		hashedPass := string(hashedPassBytes)
 
-		update_str += "password = ?, "
-		update_args = append(update_args, hashedPass)
+		user.Password = &hashedPass
 	}
 
-	if len(update_str) == 0 {
-		return nil, errors.New("no updates requested")
+	if admin != nil {
+		user.Admin = *admin
 	}
 
-	update_str = update_str[:len(update_str)-2]
-	log.Printf("Updating user with update string: %s\n", update_str)
-
-	update_args = append(update_args, id)
-
-	res, err := r.Database.Exec("UPDATE user SET "+update_str+" WHERE user_id = ?", update_args...)
-	if err != nil {
-		return nil, err
-	}
-	rows_aff, err := res.RowsAffected()
-	if err != nil {
-		return nil, err
-	}
-	if rows_aff == 0 {
-		return nil, errors.New("no users were updated")
+	if err := r.Database.Save(&user).Error; err != nil {
+		return nil, errors.Wrap(err, "failed to update user")
 	}
 
-	row := r.Database.QueryRow("SELECT * FROM user WHERE user_id = ?", id)
-	user, err := models.NewUserFromRow(row)
-	if err != nil {
-		return nil, err
-	}
-
-	return user, nil
+	return &user, nil
 }
 
 func (r *mutationResolver) CreateUser(ctx context.Context, username string, rootPath string, password *string, admin bool) (*models.User, error) {
-	tx, err := r.Database.Begin()
-	if err != nil {
-		return nil, err
-	}
 
-	user, err := models.RegisterUser(tx, username, password, rootPath, admin)
-	if err != nil {
-		tx.Rollback()
-		return nil, err
-	}
+	var user *models.User
 
-	if err := tx.Commit(); err != nil {
-		return nil, err
+	transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
+		var err error
+		user, err = models.RegisterUser(tx, username, password, rootPath, admin)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+
+	if transactionError != nil {
+		return nil, transactionError
 	}
 
 	return user, nil
@@ -248,24 +213,15 @@ func (r *mutationResolver) CreateUser(ctx context.Context, username string, root
 
 func (r *mutationResolver) DeleteUser(ctx context.Context, id int) (*models.User, error) {
 
-	row := r.Database.QueryRow("SELECT * FROM user WHERE user_id = ?", id)
-	user, err := models.NewUserFromRow(row)
-	if err != nil {
+	var user models.User
+
+	if err := r.Database.First(&user, id).Error; err != nil {
 		return nil, err
 	}
 
-	res, err := r.Database.Exec("DELETE FROM user WHERE user_id = ?", id)
-	if err != nil {
+	if err := r.Database.Delete(&user).Error; err != nil {
 		return nil, err
 	}
 
-	rows, err := res.RowsAffected()
-	if err != nil {
-		return nil, err
-	}
-	if rows == 0 {
-		return nil, errors.New("no users deleted")
-	}
-
-	return user, nil
+	return &user, nil
 }
