Let's Go Further Ch 1-5
For the next part in my backend dev journey, I’ll be starting Let's Go Further
! This time,
instead of putting all my notes for the chapters in one post, I’m going to
spread some of them out into mini posts instead. That way I can easily search for them
later when I need them. I’ll be putting relevant links here.
Using the Correct HTTP Request Method
Method | Usage |
---|---|
GET | use for retrieving information only |
POST | use for non-indempotent actions. Generally used for creating a new resource |
PUT | use for indempotent actions. Generally used for updating or replacing a new resource |
PATCH | use for actions thaat partially update a resource at a specific URL |
DELETE | use for deleting a resource at a specific URL |
Enveloping JSON Responses
There are many ways you can design your JSON Repsonses and there’s no right or wrong way but it’s important to think about the formatting upfront. A good format is to always envelope your response with a parent JSON object.
A few benefits:
- self-documenting
- reduces risk of errors on the client side
- mitigate a security vulnerability in older browsers
Something cool with embedding structs
We can take advantage of struct embedding to create an alias of a struct and copy over fields and values. That way don’t somehow change values from the original data and make a copy of it instead.
type Movie struct {
ID int64 `json:"id"`
CreatedAt time.Time `json:"created_at"`
Title string `json:"title"`
Year int32 `json:"year,omitempty"`
Runtime Runtime `json:"runtime,omitempty"`
Genres []string `json:"genres,omitempty"`
Version int32 `json:"version"`
}
type MovieAlias Movie
func (m Movie) MarshalJSON() ([]byte, error) {
var runtime string
if m.Runtime != 0 {
runtime = fmt.Sprintf("%d mins", m.Runtime)
}
aux := struct {
MovieAlias
Runtime string `json:"runtime,omitempty"`
} {
MovieAlias: MovieAlias(m),
Runtime: runtime,
}
return json.Marshal(aux)
}
JSON Validators
Given this struct:
type Validator struct {
Errors map[string]string
}
func New() *Validator {
return &Validator{Errors: make(map[string]string)}
}
❓Why would you create a new Validator
pointer instead of just a new Validator?
Guess: Maybe we create a new pointer when we only expect to have one instance of it? For example, you wouldn’t need more than one validator object since it’s a helper struct to just store all validation errors and nothing more.
Answer?:
❗In Go, when a struct is passed into a function, it is is passed by copy not by
reference. Which means that when we validate data and then store errors messages
inside Validator.Errors
, that data is lost in the parent function. You lose access to the new data.
A workaround for this is to return a new object Validator
but that would waste
memory space.
When you pass by reference, you’ll be able to grab the changes by grabbing it from memory. Rather than needing to create a new variable to store the changed data.
So the answer is:
- we need access to that data in the parent function (the one calling the validation methods)
- reduce memory usage