// internal/api/design.gopackageapiimport("encoding/json""fmt""net/http""strconv""strings""time")// Resource represents a RESTful resourcetypeResourcestruct{IDstring`json:"id"`Typestring`json:"type"`Attributesinterface{}`json:"attributes"`LinksLinks`json:"links,omitempty"`MetaMeta`json:"meta,omitempty"`}// Links represents resource linkstypeLinksstruct{Selfstring`json:"self,omitempty"`Relatedstring`json:"related,omitempty"`Nextstring`json:"next,omitempty"`Prevstring`json:"prev,omitempty"`}// Meta represents metadatatypeMetastruct{TotalCountint`json:"total_count,omitempty"`Pageint`json:"page,omitempty"`PerPageint`json:"per_page,omitempty"`Timestamptime.Time`json:"timestamp"`}// Response represents a standard API responsetypeResponsestruct{Datainterface{}`json:"data,omitempty"`Errors[]Error`json:"errors,omitempty"`MetaMeta`json:"meta,omitempty"`LinksLinks`json:"links,omitempty"`Included[]Resource`json:"included,omitempty"`}// Error represents an API errortypeErrorstruct{IDstring`json:"id,omitempty"`Statusstring`json:"status"`Codestring`json:"code"`Titlestring`json:"title"`Detailstring`json:"detail"`SourceSource`json:"source,omitempty"`}// Source represents error sourcetypeSourcestruct{Pointerstring`json:"pointer,omitempty"`Parameterstring`json:"parameter,omitempty"`}
// internal/api/status.gopackageapiimport"net/http"// StatusCode represents HTTP status codestypeStatusCodeintconst(StatusOKStatusCode=http.StatusOKStatusCreatedStatusCode=http.StatusCreatedStatusAcceptedStatusCode=http.StatusAcceptedStatusNoContentStatusCode=http.StatusNoContentStatusBadRequestStatusCode=http.StatusBadRequestStatusUnauthorizedStatusCode=http.StatusUnauthorizedStatusForbiddenStatusCode=http.StatusForbiddenStatusNotFoundStatusCode=http.StatusNotFoundStatusMethodNotAllowedStatusCode=http.StatusMethodNotAllowedStatusConflictStatusCode=http.StatusConflictStatusUnprocessableEntityStatusCode=http.StatusUnprocessableEntityStatusTooManyRequestsStatusCode=http.StatusTooManyRequestsStatusInternalServerErrorStatusCode=http.StatusInternalServerError)// GetStatusText returns the status text for a status codefuncGetStatusText(codeStatusCode)string{returnhttp.StatusText(int(code))}
// internal/api/models.gopackageapiimport("time""github.com/google/uuid")// User represents a user resourcetypeUserstruct{IDstring`json:"id"`Typestring`json:"type"`AttributesUserAttributes`json:"attributes"`LinksLinks`json:"links"`}// UserAttributes represents user attributestypeUserAttributesstruct{Namestring`json:"name"`Emailstring`json:"email"`CreatedAttime.Time`json:"created_at"`UpdatedAttime.Time`json:"updated_at"`}// CreateUserRequest represents a user creation requesttypeCreateUserRequeststruct{DataUser`json:"data"`}// UpdateUserRequest represents a user update requesttypeUpdateUserRequeststruct{DataUser`json:"data"`}// ListUsersRequest represents a user list requesttypeListUsersRequeststruct{Pageint`json:"page,omitempty"`PerPageint`json:"per_page,omitempty"`Sortstring`json:"sort,omitempty"`Filterstring`json:"filter,omitempty"`Includestring`json:"include,omitempty"`}// ListUsersResponse represents a user list responsetypeListUsersResponsestruct{Data[]User`json:"data"`MetaMeta`json:"meta"`LinksLinks`json:"links"`Included[]Resource`json:"included,omitempty"`}
// internal/api/versioning.gopackageapiimport("net/http""strings")// Version represents an API versiontypeVersionstringconst(VersionV1Version="v1"VersionV2Version="v2")// VersionHandler handles API versioningtypeVersionHandlerstruct{versionsmap[Version]http.HandlerdefaultVersionVersion}// NewVersionHandler creates a new version handlerfuncNewVersionHandler()*VersionHandler{return&VersionHandler{versions:make(map[Version]http.Handler),defaultVersion:VersionV1,}}// RegisterVersion registers a handler for a versionfunc(vh*VersionHandler)RegisterVersion(versionVersion,handlerhttp.Handler){vh.versions[version]=handler}// ServeHTTP implements http.Handlerfunc(vh*VersionHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){version:=vh.extractVersion(r)handler,exists:=vh.versions[version]if!exists{vh.handleVersionNotFound(w,r)return}handler.ServeHTTP(w,r)}// extractVersion extracts the version from the requestfunc(vh*VersionHandler)extractVersion(r*http.Request)Version{// Extract from URL path: /api/v1/userspath:=r.URL.Pathifstrings.HasPrefix(path,"/api/"){parts:=strings.Split(path,"/")iflen(parts)>=3{returnVersion(parts[2])}}// Extract from headerifversion:=r.Header.Get("API-Version");version!=""{returnVersion(version)}// Extract from query parameterifversion:=r.URL.Query().Get("version");version!=""{returnVersion(version)}returnvh.defaultVersion}// handleVersionNotFound handles version not foundfunc(vh*VersionHandler)handleVersionNotFound(whttp.ResponseWriter,r*http.Request){w.Header().Set("Content-Type","application/json")w.WriteHeader(http.StatusNotFound)response:=Response{Errors:[]Error{{Status:"404",Code:"VERSION_NOT_FOUND",Title:"API Version Not Found",Detail:"The requested API version is not supported",},},}json.NewEncoder(w).Encode(response)}
// internal/api/header_versioning.gopackageapiimport("net/http""strings")// HeaderVersioningMiddleware handles versioning via headersfuncHeaderVersioningMiddleware(nexthttp.Handler)http.Handler{returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){version:=r.Header.Get("API-Version")ifversion==""{version="v1"// Default version}// Add version to contextctx:=context.WithValue(r.Context(),"api_version",version)r=r.WithContext(ctx)next.ServeHTTP(w,r)})}// GetVersionFromContext extracts version from contextfuncGetVersionFromContext(ctxcontext.Context)string{ifversion,ok:=ctx.Value("api_version").(string);ok{returnversion}return"v1"}
// internal/api/validation.gopackageapiimport("fmt""net/http""regexp""strings""time")// Validator represents a request validatortypeValidatorinterface{Validate()error}// UserValidator validates user requeststypeUserValidatorstruct{Namestring`json:"name"`Emailstring`json:"email"`}// Validate validates the user requestfunc(uv*UserValidator)Validate()error{varerrors[]stringifstrings.TrimSpace(uv.Name)==""{errors=append(errors,"name is required")}elseiflen(uv.Name)<2{errors=append(errors,"name must be at least 2 characters")}elseiflen(uv.Name)>100{errors=append(errors,"name must be less than 100 characters")}ifstrings.TrimSpace(uv.Email)==""{errors=append(errors,"email is required")}elseif!isValidEmail(uv.Email){errors=append(errors,"email must be a valid email address")}iflen(errors)>0{returnfmt.Errorf("validation failed: %s",strings.Join(errors,", "))}returnnil}// isValidEmail validates email formatfuncisValidEmail(emailstring)bool{pattern:=`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`matched,_:=regexp.MatchString(pattern,email)returnmatched}// PaginationValidator validates pagination parameterstypePaginationValidatorstruct{Pageint`json:"page"`PerPageint`json:"per_page"`}// Validate validates pagination parametersfunc(pv*PaginationValidator)Validate()error{ifpv.Page<1{returnfmt.Errorf("page must be greater than 0")}ifpv.PerPage<1{returnfmt.Errorf("per_page must be greater than 0")}ifpv.PerPage>100{returnfmt.Errorf("per_page must be less than or equal to 100")}returnnil}
// internal/api/schema.gopackageapiimport("encoding/json""fmt""net/http")// SchemaValidator validates JSON schematypeSchemaValidatorstruct{schemamap[string]interface{}}// NewSchemaValidator creates a new schema validatorfuncNewSchemaValidator(schemamap[string]interface{})*SchemaValidator{return&SchemaValidator{schema:schema}}// Validate validates JSON against schemafunc(sv*SchemaValidator)Validate(data[]byte)error{varjsonDatainterface{}iferr:=json.Unmarshal(data,&jsonData);err!=nil{returnfmt.Errorf("invalid JSON: %w",err)}// Implement schema validation logic// This is a simplified examplereturnsv.validateObject(jsonData)}// validateObject validates a JSON objectfunc(sv*SchemaValidator)validateObject(objinterface{})error{objMap,ok:=obj.(map[string]interface{})if!ok{returnfmt.Errorf("expected object")}// Check required fieldsifrequired,ok:=sv.schema["required"].([]string);ok{for_,field:=rangerequired{if_,exists:=objMap[field];!exists{returnfmt.Errorf("required field '%s' is missing",field)}}}returnnil}
// internal/api/response.gopackageapiimport("encoding/json""net/http""time")// ResponseBuilder builds API responsestypeResponseBuilderstruct{responseResponse}// NewResponseBuilder creates a new response builderfuncNewResponseBuilder()*ResponseBuilder{return&ResponseBuilder{response:Response{Meta:Meta{Timestamp:time.Now(),},},}}// WithData sets the response datafunc(rb*ResponseBuilder)WithData(datainterface{})*ResponseBuilder{rb.response.Data=datareturnrb}// WithError adds an error to the responsefunc(rb*ResponseBuilder)WithError(errError)*ResponseBuilder{rb.response.Errors=append(rb.response.Errors,err)returnrb}// WithMeta sets the response metadatafunc(rb*ResponseBuilder)WithMeta(metaMeta)*ResponseBuilder{rb.response.Meta=metareturnrb}// WithLinks sets the response linksfunc(rb*ResponseBuilder)WithLinks(linksLinks)*ResponseBuilder{rb.response.Links=linksreturnrb}// Build builds the responsefunc(rb*ResponseBuilder)Build()Response{returnrb.response}// WriteResponse writes a response to the HTTP response writerfuncWriteResponse(whttp.ResponseWriter,statusCodeint,responseResponse){w.Header().Set("Content-Type","application/json")w.WriteHeader(statusCode)json.NewEncoder(w).Encode(response)}// WriteSuccess writes a success responsefuncWriteSuccess(whttp.ResponseWriter,datainterface{}){response:=NewResponseBuilder().WithData(data).Build()WriteResponse(w,http.StatusOK,response)}// WriteCreated writes a created responsefuncWriteCreated(whttp.ResponseWriter,datainterface{}){response:=NewResponseBuilder().WithData(data).Build()WriteResponse(w,http.StatusCreated,response)}// WriteError writes an error responsefuncWriteError(whttp.ResponseWriter,statusCodeint,code,title,detailstring){response:=NewResponseBuilder().WithError(Error{Status:fmt.Sprintf("%d",statusCode),Code:code,Title:title,Detail:detail,}).Build()WriteResponse(w,statusCode,response)}
// internal/api/pagination.gopackageapiimport("encoding/base64""encoding/json""fmt""strconv""time")// Cursor represents a pagination cursortypeCursorstruct{IDstring`json:"id"`Timestamptime.Time`json:"timestamp"`Directionstring`json:"direction"`}// EncodeCursor encodes a cursor to a stringfuncEncodeCursor(cursorCursor)(string,error){data,err:=json.Marshal(cursor)iferr!=nil{return"",err}returnbase64.URLEncoding.EncodeToString(data),nil}// DecodeCursor decodes a cursor from a stringfuncDecodeCursor(cursorStrstring)(Cursor,error){data,err:=base64.URLEncoding.DecodeString(cursorStr)iferr!=nil{returnCursor{},err}varcursorCursorerr=json.Unmarshal(data,&cursor)returncursor,err}// PaginationParams represents pagination parameterstypePaginationParamsstruct{Cursorstring`json:"cursor,omitempty"`Limitint`json:"limit,omitempty"`Orderstring`json:"order,omitempty"`}// Validate validates pagination parametersfunc(pp*PaginationParams)Validate()error{ifpp.Limit<1{pp.Limit=20// Default limit}ifpp.Limit>100{returnfmt.Errorf("limit must be less than or equal to 100")}ifpp.Order!=""&&pp.Order!="asc"&&pp.Order!="desc"{returnfmt.Errorf("order must be 'asc' or 'desc'")}returnnil}
// internal/api/offset_pagination.gopackageapiimport("fmt""strconv")// OffsetPaginationParams represents offset-based pagination parameterstypeOffsetPaginationParamsstruct{Pageint`json:"page"`PerPageint`json:"per_page"`}// Validate validates offset pagination parametersfunc(opp*OffsetPaginationParams)Validate()error{ifopp.Page<1{opp.Page=1}ifopp.PerPage<1{opp.PerPage=20}ifopp.PerPage>100{returnfmt.Errorf("per_page must be less than or equal to 100")}returnnil}// GetOffset calculates the offset from page and per_pagefunc(opp*OffsetPaginationParams)GetOffset()int{return(opp.Page-1)*opp.PerPage}// ParseOffsetPagination parses offset pagination from query parametersfuncParseOffsetPagination(queryParamsmap[string]string)OffsetPaginationParams{params:=OffsetPaginationParams{Page:1,PerPage:20,}ifpageStr,exists:=queryParams["page"];exists{ifpage,err:=strconv.Atoi(pageStr);err==nil&&page>0{params.Page=page}}ifperPageStr,exists:=queryParams["per_page"];exists{ifperPage,err:=strconv.Atoi(perPageStr);err==nil&&perPage>0{params.PerPage=perPage}}returnparams}
// internal/api/errors.gopackageapiimport("fmt""net/http")// APIError represents an API errortypeAPIErrorstruct{Statusint`json:"status"`Codestring`json:"code"`Titlestring`json:"title"`Detailstring`json:"detail"`SourceSource`json:"source,omitempty"`}// Error implements the error interfacefunc(ae*APIError)Error()string{returnfmt.Sprintf("[%d] %s: %s",ae.Status,ae.Title,ae.Detail)}// NewAPIError creates a new API errorfuncNewAPIError(statusint,code,title,detailstring)*APIError{return&APIError{Status:status,Code:code,Title:title,Detail:detail,}}// Common API errorsvar(ErrBadRequest=NewAPIError(http.StatusBadRequest,"BAD_REQUEST","Bad Request","The request is invalid",)ErrUnauthorized=NewAPIError(http.StatusUnauthorized,"UNAUTHORIZED","Unauthorized","Authentication required",)ErrForbidden=NewAPIError(http.StatusForbidden,"FORBIDDEN","Forbidden","Access denied",)ErrNotFound=NewAPIError(http.StatusNotFound,"NOT_FOUND","Not Found","The requested resource was not found",)ErrConflict=NewAPIError(http.StatusConflict,"CONFLICT","Conflict","The request conflicts with the current state",)ErrUnprocessableEntity=NewAPIError(http.StatusUnprocessableEntity,"UNPROCESSABLE_ENTITY","Unprocessable Entity","The request is well-formed but contains semantic errors",)ErrInternalServerError=NewAPIError(http.StatusInternalServerError,"INTERNAL_SERVER_ERROR","Internal Server Error","An unexpected error occurred",))
// internal/api/error_handler.gopackageapiimport("encoding/json""log""net/http")// ErrorHandler handles API errorstypeErrorHandlerstruct{loggerLogger}// NewErrorHandler creates a new error handlerfuncNewErrorHandler(loggerLogger)*ErrorHandler{return&ErrorHandler{logger:logger}}// HandleError handles an errorfunc(eh*ErrorHandler)HandleError(whttp.ResponseWriter,r*http.Request,errerror){eh.logger.Error("API error",map[string]interface{}{"method":r.Method,"path":r.URL.Path,"error":err.Error(),})varapiErr*APIErrorswitcherr:=err.(type){case*APIError:apiErr=errcase*ValidationError:apiErr=NewAPIError(http.StatusBadRequest,"VALIDATION_ERROR","Validation Error",err.Error(),)case*NotFoundError:apiErr=NewAPIError(http.StatusNotFound,"NOT_FOUND","Not Found",err.Error(),)default:apiErr=ErrInternalServerError}eh.writeErrorResponse(w,apiErr)}// writeErrorResponse writes an error responsefunc(eh*ErrorHandler)writeErrorResponse(whttp.ResponseWriter,apiErr*APIError){w.Header().Set("Content-Type","application/json")w.WriteHeader(apiErr.Status)response:=Response{Errors:[]Error{{Status:fmt.Sprintf("%d",apiErr.Status),Code:apiErr.Code,Title:apiErr.Title,Detail:apiErr.Detail,},},}json.NewEncoder(w).Encode(response)}
// internal/api/openapi.gopackageapiimport("encoding/json""net/http")// OpenAPISpec represents an OpenAPI specificationtypeOpenAPISpecstruct{OpenAPIstring`json:"openapi"`InfoInfo`json:"info"`Servers[]Server`json:"servers,omitempty"`Pathsmap[string]PathItem`json:"paths"`ComponentsComponents`json:"components,omitempty"`}// Info represents API informationtypeInfostruct{Titlestring`json:"title"`Versionstring`json:"version"`Descriptionstring`json:"description,omitempty"`}// Server represents a servertypeServerstruct{URLstring`json:"url"`Descriptionstring`json:"description,omitempty"`}// PathItem represents a path itemtypePathItemstruct{Get*Operation`json:"get,omitempty"`Post*Operation`json:"post,omitempty"`Put*Operation`json:"put,omitempty"`Delete*Operation`json:"delete,omitempty"`}// Operation represents an operationtypeOperationstruct{Summarystring`json:"summary,omitempty"`Descriptionstring`json:"description,omitempty"`Parameters[]Parameter`json:"parameters,omitempty"`RequestBody*RequestBody`json:"requestBody,omitempty"`Responsesmap[string]Response`json:"responses"`Tags[]string`json:"tags,omitempty"`}// Parameter represents a parametertypeParameterstruct{Namestring`json:"name"`Instring`json:"in"`Descriptionstring`json:"description,omitempty"`Requiredbool`json:"required,omitempty"`SchemaSchema`json:"schema,omitempty"`}// RequestBody represents a request bodytypeRequestBodystruct{Descriptionstring`json:"description,omitempty"`Contentmap[string]Content`json:"content"`Requiredbool`json:"required,omitempty"`}// Response represents a responsetypeResponsestruct{Descriptionstring`json:"description"`Contentmap[string]Content`json:"content,omitempty"`}// Content represents contenttypeContentstruct{SchemaSchema`json:"schema,omitempty"`}// Schema represents a schematypeSchemastruct{Typestring`json:"type,omitempty"`Propertiesmap[string]Schema`json:"properties,omitempty"`Required[]string`json:"required,omitempty"`}// Components represents componentstypeComponentsstruct{Schemasmap[string]Schema`json:"schemas,omitempty"`}// OpenAPIHandler handles OpenAPI requeststypeOpenAPIHandlerstruct{specOpenAPISpec}// NewOpenAPIHandler creates a new OpenAPI handlerfuncNewOpenAPIHandler()*OpenAPIHandler{return&OpenAPIHandler{spec:OpenAPISpec{OpenAPI:"3.0.0",Info:Info{Title:"My API",Version:"1.0.0",},Paths:make(map[string]PathItem),},}}// ServeHTTP implements http.Handlerfunc(h*OpenAPIHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){w.Header().Set("Content-Type","application/json")json.NewEncoder(w).Encode(h.spec)}
// 1. Basic API responsefunc(h*Handler)GetUser(whttp.ResponseWriter,r*http.Request){user,err:=h.userService.GetUser(userID)iferr!=nil{WriteError(w,http.StatusNotFound,"NOT_FOUND","User not found",err.Error())return}WriteSuccess(w,user)}// 2. Request validationfunc(h*Handler)CreateUser(whttp.ResponseWriter,r*http.Request){varreqCreateUserRequestiferr:=ReadJSON(r,&req);err!=nil{WriteError(w,http.StatusBadRequest,"BAD_REQUEST","Invalid JSON",err.Error())return}iferr:=req.Validate();err!=nil{WriteError(w,http.StatusBadRequest,"VALIDATION_ERROR","Validation failed",err.Error())return}// Process request...}// 3. API versioningversionHandler:=NewVersionHandler()versionHandler.RegisterVersion("v1",v1Handler)versionHandler.RegisterVersion("v2",v2Handler)
// Error handlingfunc(h*Handler)HandleRequest(whttp.ResponseWriter,r*http.Request){iferr:=h.processRequest(r);err!=nil{h.errorHandler.HandleError(w,r,err)return}WriteSuccess(w,result)}// Paginationfunc(h*Handler)ListUsers(whttp.ResponseWriter,r*http.Request){params:=ParseOffsetPagination(r.URL.Query())iferr:=params.Validate();err!=nil{WriteError(w,http.StatusBadRequest,"BAD_REQUEST","Invalid pagination",err.Error())return}users,total,err:=h.userService.ListUsers(params)iferr!=nil{WriteError(w,http.StatusInternalServerError,"INTERNAL_ERROR","Failed to list users",err.Error())return}response:=NewResponseBuilder().WithData(users).WithMeta(Meta{TotalCount:total,Page:params.Page,PerPage:params.PerPage}).Build()WriteResponse(w,http.StatusOK,response)}