I'm thinking about using a new model structure for my new api.
Current state
I have GET
, POST
, PUT
and DELETE
endpoints. Just forget about DELETE
because it only takes a query parameter and has no model.
Those three endpoints all use the same model:
Car Model
id: string | undefined;
manufacturer: string;
date: Date;
Problem
When requesting a single car via the GET
endpoint I theoretically could get a car without a id
. So when I want to pass the ID
to the DELETE
endpoint I always need to check that the ID is not undefined.
Possible solution
Providing different models for each HTTP method.
1. Car Model for POST request
id: string | undefined;
manufacturer: string;
date: Date;
2. Car Model for [PUT request, GET response, ...]
id: string;
manufacturer: string;
date: Date;
I also want to provide PATCH
request I would also need this model
3. Car Model for PATCH request
id: string | undefined;
manufacturer: string | undefined;
date: Date | undefined;
Questions
- In total I would need three different models. But is that really worth going for and considered as a 'Good Practice'?
- If that is a 'Good Practice' how should I name the models?
CodePudding user response:
You've only got one model, called Car
. It looks like this.
interface Car {
id: string;
manufacturer: string;
date: Date;
}
Calling the other ones models is missing the point. At no point are those partial things stored in a database or in any sort of long-term storage. They're transient.
As you've already noticed, for PUT
requests and GET
responses, you're always receiving or returning (respectively) a fully defined object, so Car
suffices for that.
Your proposed PATCH
type is simply Partial<Car>
. No need for a new type; TypeScript provides this type for you. This can also be used if you want to have a query interface, where a GET
request gives you a partially-defined car and finds all matches.
For your proposed POST
, you want an optional id
but all other fields required. That's a bit more complex but should look something like
Omit<Car, "id"> & { id?: string }
which reads "remove the id
field from the Car
object and replace it with this optional one". Omit
is another utility type that TypeScript provides us with.
You can give these things names. In fact, I encourage you to. I'm always an advocate of naming more things, as it provides more documentation in the code itself as to why we're doing what we're doing.
type PatchCar = Partial<Car>;
type PostCar = Omit<Car, "id"> & { id?: string };
PatchCar
screams "Car
but for PATCH
" in its name, while Partial<Car>
doesn't really say why we've just made all of the fields optional.
The important thing is that there's one representation of the Car
model: namely the interface called Car
. Everything else is just an offshoot of that. If we add fields to Car
, we only have to update it in one place, not several. Remember, don't repeat yourself.
CodePudding user response:
In my experience, it's almost always a bad practice to have multiple variants of the same model structure in your code. (see DRY principle.)
TypeScript utility types can possibly help you out.