All right.
So our first step in actually integrating Prisma into reservations is to create a service that's going
to act as the entry point for any other calls to Prisma and to our database.
So this is going to be very similar to how we've already set up an abstract repository for MongoDB,
which we were able to swap out for type ORM very easily.
Prisma is not going to be the same because of how it actually uses the Prisma client type generation.
We're not going to utilize the abstract repository.
Instead, we're going to use the Prisma client directly.
And this is because Prisma is actually acting as the abstract repository itself.
Now it's going to be encapsulating all of the calls we make to the database in a consistent API.
And we can swap out this database provider here with any other provider at runtime.
And essentially this is going to be mimicking what our abstract repository previously gave us by encapsulating
all of this in a common set of library API calls.
So let's go ahead and create a new Prisma service inside of the reservation source folder.
And this will be an injectable class that we're going to inject wherever we need to interact with the
database.
And so we'll export Prisma service.
Now the key bit to get access to our underlying schema type generation is to now extend Prisma client.
And we go ahead and provide an import to Prisma client.
Now this needs to be a custom import from Dot Prisma slash client.
Now remember this is the output path that we specified earlier inside of our Schema.prisma to this Dot
Prisma client.
And this is going to make sure our code looks for the local node modules folder and not at the root
of our project.
So this is going to give us access to all of the underlying types that Prisma client has generated for
us.
Let's go ahead and implement now on module init from nest.
And this is going to be an optional process now where we actually directly connect to the database.
And I just want to do this on startup.
So we'll implement on module init to connect to the database as soon as we start up.
You can also do this lazily.
And the first time you interact with Prisma client it'll connect on its own.
In our case I do want to connect on startup, so I'll call await this dot connect function, which we
get from extending the Prisma client a method to actually connect to our database.
Now let's head back to our reservations module.
And let's firstly go ahead and now provide this new Prisma service in the providers array.
Now let's continue refactoring our service to no longer utilize MongoDB but instead the new Prisma service.
So let's start off by going to our validation object and switching out the MongoDB URI here in the reservations
module.
We're no longer expecting this, but now the database URL instead.
We're also going to go ahead and remove our two imports to the database module, since we're no longer
utilizing this.
Instead, we're utilizing our Prisma service to connect to the database.
We can then go ahead and clean up these unused files that are no longer utilizing.
So the reservations repository, let's go ahead and remove it as well as our reference to it in here
in the module.
Remove it from the providers array and remove the import as well.
And we can even remove the models folder as we're no longer needing to define our own schema in TypeScript.
We know this schema is going to be generated for us from our Prisma client.
So let's go ahead and remove our models.
So now back in the reservation service let's remove the reservations repository from the constructor
as well.
And now let's instead inject the private read only Prisma service of type Prisma service.
So now we need to go ahead and swap out our calls to the reservations repository to instead now use
the Prisma service.
Let's go ahead and do this.
Next I'll first start off in the create function here.
So of course after we get our create charge back from the payment service, we know we're persisting
our reservation to the database instead of calling the reservations repository.
Now we're going to return this dot Prisma service.
And now we can access the dot reservation property on it, which is going to be all of the client types
that our schema has generated for us for this given reservation, which of course is a type that we
defined in our schema and then generated types for using the Prisma client type generation.
So now on the reservation object, we have the consistent API that Prisma offers us regardless of the
underlying database implementation.
We have these methods we actually now call to execute operations on the database that we're using.
So in our case we're going to utilize the create function to create a single reservation in the reservations
table.
Now within this create options object we actually need to specify a new property called data.
And this is where the actual properties for the new reservation need to be added onto.
These are all the properties that we've defined already in our schema.
So we can see here.
For example, our code is already complaining because type ORM knows this user ID needs to be a number
and not a string, which it currently is.
So we can see this is coming from the reservation.
Create input here, which are all the properties we expect to receive when creating a new reservation.
So let's firstly go ahead and now refactor this user DTO that's coming from our libs common folder.
So right now we have a common user schema and this user DTO.
So what I want to do is actually create a new folder in common.
Call this interfaces.
And let's move this user dot DTO into interfaces.
Rename it now to user dot interface.
And let's just export the interface as the user.
So this is going to be the user type that we can actually utilize in all of our applications.
We need to define it ourselves and not reference it from Prisma client.
Because of course this user entity is going to be associated with the auth microservice and it's Prisma
client when we eventually generate it.
So we need a way to get access to this user type.
Well we can't access the auth Prisma client.
We need to redefine it as a common type here.
So after doing this, I'll go ahead and delete our existing models folder with our user schema.
Let's delete it.
And now create a new Index.ts in interfaces.
And let's export the user interface.
And now go into our root Index.ts and change out the export to models to now export interfaces.
Now let's go back to our reservation service where instead of utilizing this user DTO, let's just import
the user interface now and set it as user.
One final change we need to make to the user interface is swap the underscore ID, which is a mongo
ID out for just our regular type ORM id, and change this to now be a number as well.
So now in the reservation service, we can destructure the ID properly here and now type ORM is no longer
complaining because we're supplying the user ID property as the expected number.
We also need to go ahead and no longer spread everything on the create reservation DTO.
And that's because type ORM is going to be stricter than Mongo and actually throw an error if we provide
additional properties that the schema does not expect.
In this case, that would include the charge object.
So let's manually supply the rest of the properties that it expects, including the start date which
will pull off of the create reservation DTO the end date, which we'll do the same for.
And now we are supplying the expected reservation object to type ORM and it will persist it properly
as expected because this lines up with our schema.
Let's go ahead and refactor the rest of these calls.
So find all.
We'll turn into Prisma service dot reservation dot find many now and still an empty filter object.
To find all find one.
We'll use Prisma service dot reservation.
And then we're going to use the dot find.
And we can see we have many different finds here.
In our case I want to use find unique or throw to maintain our same behavior of finding a single entity
by its unique ID property or throw an error.
We just need to now swap out this id parameter to now be id type number.
Now this needs to be inside of a where clause.
So this is a where object.
And now we specify the ID of the reservation we're looking for.
Now for our update method.
This is going to be a combination of both of what we've seen before in these other methods.
We're going to firstly change our ID here to now be a number, since this is what we're storing in the
database.
And instead of the reservations repository we'll call it Prisma service dot reservation dot update to
update the record.
And now to find the correct record, we use the same process for find unique or throw.
We give it the Where clause that it expects.
So this needs to be a single options object provided into update.
We have the Where clause.
So we look for the ID of the reservation we want to update.
And then we have the data attribute which is the new updates to be applied to the entity.
So we'll simply pass it our DTO.
Finally, for remove, we're just going to change the ID to a number and call Prisma service dot reservation.
And now it's just going to be simply delete.
And you guessed it, we need to supply the where and pass in that ID of the reservation.
So now at this point we actually need to provide the Postgres database URL to our application.
Now we know in our.mv dot local we're already doing this for when we execute migrations when our apps
running locally.
But of course we need to provide this same connection string to our dot env so that our application
can connect to the database as well.
So I'll simply swap out our MongoDB URI with the database URL.
Now a key difference to our local now is going to be.
Since this dot env is going to be supplied to our running docker container running in docker compose.
Well, the networking isn't going to be on our local host machine.
We're instead going to be using Docker networking.
We know to communicate to another container, we need to address it by the name we've given it in our
docker compose.
So of course the host name will now instead be localhost.
It'll be the name of the container here Postgres.
So let's update our dot env to.
Instead of use localhost for the hosts, we change this to Postgres, the name of the container.
So let's go ahead and continue refactoring our app.
Let's go into the reservations controller.
Next in here we're going to have to remove our reference to the user dot DTO from app slash common and
instead import the user interface.
We know we've refactored this too, so swap out the user DTO with just the user in our current user
decorator here, which is our new interface.
And now if we scroll down, we can see we have an error for a few of our methods here.
And that's because the ID of course, we're supplying right now is a string pulled off of our query
parameters.
We need to simply convert this into the number that our app expects.
So we can just use the plus operator here to coerce it into an integer.
Go ahead and do that for all of these ID strings in the rest of our controller routes.
Next up, let's go into our current user decorator where we're currently referencing this user document.
We're going to have to remove this.
Instead, we can provide the new user interface that we know we've created inside of our interfaces
folder.
So now our current user decorator is looking good.
We finally need to go into our common JWT auth guard, which we know is inside of the common folder,
the libs common folder inside of our auth directory.
Open up the common JWT auth guard and delete the user DTO, which we're currently utilizing to reference
the return type from our auth service.
Instead, we'll again use our new user interface here.