When a user creates a website:
user_iduser_id from contextThis is how ownership is enforced.
JWT → AuthMiddleware →context(user_id)
↓
CreateWebsite handler
↓
WebsiteService
↓
WebsiteRepository → INSERT (user_id)
internal/handlers/websites.go
// 1. Get user_id for authentication
userID, err := contextutil.GetUserID(c)
if err != nil {
c.JSON(http.StatusUnauthorized, response.APIResponse{
Success: false,
Error: err.Error(),
})
return
}
// 2. Pass it to service
website, err := h.websiteService.CreateWebsite(ctx, userID, body.URL)
internal/service/websites.go
func (s *WebsiteService) CreateWebsite(
ctx context.Context,
userID string,
url string,
) (*domain.Website, error) {
// 1. Normalized URL
normalisedUrl := strings.TrimSpace(url)
if normalisedUrl == "" {
return nil, domain.ErrInvalidURL
}
if strings.Contains(normalisedUrl, "localhost") {
return nil, domain.ErrInvalidURL
}
/* 3. Create domain entity
- this creates a new domain object n memory
- & means you are creating a pointer of type domain.Website
*/
website := &domain.Website{
URL: normalisedUrl,
UserID: userID,
}
// 4. Persist
// s.Repo.Create : Repository, please save this Website entity to the database.
if err := s.repo.Create(ctx, website); err != nil {
return nil, err
}
// 5. Return fully populated entity
return website, nil
}
internal/repository/website_repository_pg.go
func (r *websiteRepository) Create(ctx context.Context, website *domain.Website) error {
query := `
INSERT INTO website (user_id, url)
VALUES ($1, $2)
RETURNING id, user_id, url, time_added
`
err := r.db.QueryRowxContext(
ctx,
query,
website.UserID,
website.URL,
).Scan(&website.ID, &website.UserID, &website.URL, &website.TimeAdded)
if err != nil {
/*
err.(*pq.Error) : err is an error interface, under the hood it might be a pg.Error
i.e POstgreSQL Error.
- , ok : if ok == true postgres error , ok == false some other error ( network, context )
- PostgreSQL uses SQLSTATE codes , 23505 means UNIQUE constraint violation.
- if pgError.Code == "23505" return "URL already exists"
*/
if pqError, ok := err.(*pq.Error); ok {
if pqError.Code == "23505" {
return domain.ErrURLAlreadyExists
}
}
return err
}
return nil
}