In this speech, I'll talk about the benefit of using GraphQL and show a small demo of an endpoint created with Fastify Mercurius and Prisma.
GraphQL with Mercurius and Prisma, love at first sight
AI Generated Video Summary
The Talk discusses the use of Prisma and Mercurius in moving from REST API to GraphQL. Prisma improves the developer experience and allows for easy data modeling and type-safe queries. Mercurius is easy to learn if you already know Fastify and offers a performant GraphQL server. Fastify is preferred over Express for its active community and good performance. The ideal stack for Node.js includes Fastify for REST API and GraphQL, Prisma for the database, and the default provider like MongoDB.
1. Introduction to Prisma and Mercurius
Today I will be speaking about Prisma with Mercurius and our experience in moving from REST API to GraphQL. Over the last 10 years, I have primarily worked with REST API, which served browsers and desktop applications well. However, with the rise of other devices such as smartphones and smart TVs, we needed to improve the flexibility of our API. This led us to explore the potential of GraphQL. Working with small and medium companies, we identified the need for an easy-to-maintain framework that supports junior and middle developers while allowing senior developers to focus on business logic. With these requirements, we chose to use Mercurius for the GraphQL part and Prisma for data access.
So, why I'm here? First because the committee decided to vote my talk, so thank you. And then because in the last period, I tried to help a customer for move from REST API to GraphQL. So I wanna share with you our experience and how we decided to move in this way.
So, in the last 10 years, typically, I work only with the REST API. When I start to work, the API typically serve only browser or maybe some desktop application. And REST API works very well in this way, in this case, and you can use it without any problem. I don't have any mistake or problem against REST API because they work very well and I use it in my daily in this moment, but in some scenario, we have to improve the flexibility of the API. Typically in the last period, we start to use other devices that use our API, for instance, smartphone, and also a smartphone and a smart TV. So we start to learn the potential of GraphQL because we need to give more powerful, more power to the client, unfortunately. And so we start to learn and try GraphQL.
Before moving to the decision, I wanna show you also the need for the customer in this case. Typically, I work with a small and medium company, and they have a lot of junior and middle developer. And some senior developer. The need for this customer are these, typically. They want a framework or a code base easy to maintain. Then the framework or the architecture need to help in the day by day. The developer need to help the junior, typically because they want to put value and increase the skill, and also help the senior to not spend time to resolve a framework problem. Okay. Then we need, it's a must, have a TypeScript in our application, and the developer must be focused on business and not to resolve a framework problem. So with this need, we decided to move in this way, with Mercury in the GraphQL part and Prisma to call the database and the part of the data access. The developer stay in the middle. So I have to create the GraphQL signature and create the code to access to the database using Prisma.
2. Reasons for Choosing Mercurius and Prisma
We decided to move to Mercurius and Prisma for our GraphQL and database implementations, respectively. Mercurius is easy to learn if you already know Fastify, and it has excellent documentation. Prisma improves the developer experience and allows for easy data modeling and type-safe queries. It also integrates well with Fastify. Now, let's move on to the demo.
Why did we decide to move in this way? First, let's talk about the GraphQL implementation. We already know Fastify, and Mercurius is built on top of Fastify, so if you already know Fastify, it's easy to learn Mercurius. Mercurius is easy to use, and the documentation is awesome. You can find everything you need on the website, and if you have any questions, the community is very responsive. To keep the support out of the box, there is a small npm package called Mercurius Code Gen that helps convert your GraphQL schema into TypeScript definitions. This helps ensure the definition of your GraphQL and allows you to create operations to test your GraphQL server. It also helps catch errors during code generation, preventing issues from reaching production.
Now let's talk about the database implementation. We decided to move to Prisma because it helps improve the developer experience and is easy to use and maintain. If you are familiar with TypeScript, creating a database and data access layer with Prisma is simple. Prisma has a good data modeling feature, making it easy to create entities. It also allows you to create type-safe queries, guaranteeing the correctness of your queries during the build time of your application. Prisma's migration feature helps keep your database up to date in production. Additionally, integrating Prisma into your Fastify application is straightforward with a simple plugin. Both Mercurius and Prisma are TypeScript-friendly, making them a great fit for our development stack.
Now that I've provided some background, let's move on to the demo where I'll show you the code and the benefits of this solution. First, I'll run the server so you can see the demo in action.
3. Database Implementation with Prisma
We decided to move to Prisma for the database implementation. Prisma improves the developer experience, is easy to use and maintain, and supports TypeScript. It offers good data modeling, typesafe queries, and migration capabilities. Prisma can be easily integrated into a Fastify environment. Now, let's move on to the demo.
Then, for the database implementation, for the data access implementation, we decide to move to Prisma. Typically, I don't love ORM, but Prisma is a bit different in this case. It helps us to improve the developer experience of the developer, and also it's easy to use and easy to maintain. And if you are familiar with TypeScript, the syntax for creating a database and data access layer with Prisma, it's pretty simple.
Then, Prisma has a good data modeling, so it's very easy to create your entity using Prisma. You can create typesafe query, so you can guarantee your query during the build time of your application if you use TypeScript. And you can also use the migration to keep up to date your database in production or in stage and so on. Last but not the least, you can integrate Prisma in your Fastify application by a simple plugin, so it's very easy to integrate in a Fastify environment. And last, both Mercurius and Prisma love TypeScript.
So I speak too much, so I wanna show you the demo so you can see the code to understand the benefit of the solution. So first of all, I run the server so you can see the demo in this case. I show you the demo before in the... It's okay, bigger, not bigger. Okay, better. Perfect. Okay, this is GraphiQL. GraphiQL is a UI interface to test or to show or to see the documentation of your GraphQL server. You can use the docs if you want and you can check which are the query in this case. You can check which are the mutation or which are the subscription. So if you want you can see all the documentation of your GraphQL server. Then you can also test your application. For instance, I already created a query for a welcome. So this is a Tonino pizza menu, so sorry I'm Italian and the pizza is in my blood. The first query is the welcome. So it's simple. You call the query welcome and the server is responsive with welcome to Tonino pizza. You can also create a query with a parameter if you want. Pretty simple. You can call in this case, HiPupo. I pass the name, my name in this case.
4. Tonino Pizza and Prisma Schema
In this section, we will explore a more complex example using Tonino pizza. We have already created a pizza for the Ninja Turtle, specifically a pepperoni pizza. You can view the pizza list, create new pizzas such as the pizza margherita, and more. Moving on to the code implementation, we will start with the Prisma side. The Schema Prisma file is the single source of your database, containing the generator, data source, and model sections.
HiPupo, welcome to Tonino pizza. To see a more complex example we can do in the pizza. I already created a pizza. This is the pizza for the Ninja Turtle. So you can see the list of the pizza. In this case, the pizza are only one, the pepperoni pizza.
And as you can see, you can see all the implement, you can see the list of the pizza in the menu in this case. You can also, for instance create a new pizza, the pizza margherita and so on. Pretty simple. I don't want to spend too much time in the GraphQL because it's not the real focus of the topic. You can create pizza and then you can see in the pizza list, your pizza. Pretty simple in this case.
Okay, now I wanna move in the code to show how we can implement this GraphQL server. So, this is my application. I wanna start from the Prisma side. So, if you wanna work with Prisma, typically you have to work in a single folder called Prisma. And there is only one file. It's okay, the code, it's visible. Okay, perfect. The Schema Prisma is the only file to work with Prisma. So, in this case, the Schema Prisma is the single source of two of your database. The Schema Prisma is pretty simple. There is the first part where you can see the generator. The generator is the provider used to build your Node client to call your database. The data source is the info to understand which is the provider. In this case, the provider is the Postgres SQL. But you can use the MySQL, SQL Server, and other provider. The URL is used for the connection string. And then, the other part important is the model. The models are the entity of your database in this case.
5. Database Implementation with Prisma Schema
The Prisma schema allows you to define the structure of your database entities and properties. You can specify the type of each property and add specific decorators, such as the primary key or default values. Prisma CLI provides commands like 'init' to initialize your project, 'generate' to create the Prisma client, and 'dev' or 'deploy' for migrations. Migrations are stored in the Prisma folder, and you can use 'dev' for development mode and 'deploy' for production. Let's now move on to the GraphQL part.
The syntax, as you can notice, is similar to TypeScript. The model indicate that this model, the topic, is a simple entity for the database. And then you can describe your column or your property. So in this case, the ID, the name, and the create-head, and so on. You can indicate the type of the column, in this case, string, date, time, and so on. And you can decorate each property with a specific decoration. For instance, the ID for the primary key, the default, if you wanted to put a default UID in your entity, and so on.
You can also create relations for rank key using a simple syntax with the relation. Pretty simple, and if you wanna improve your developer experience, you can install the Prisma extension in VS Code to get some intellisense, for instance, you can get the help for creating the column, and so on. If you start to work with Prisma, you have to know four command for the CLI. Typically, you have to start from the mpx Prisma. Prisma is the CLI, and the init is the first command. Init is used to initialize your project in this case, and I already created it, so I don't run it. Then the second is the generate. Generate is used to convert the schema in your Prisma client to call the database from your code base. So if you run it, Prisma start to read your schema and create the Prisma client to, as an object. There is some, okay. Okay, there is the Prisma client used to call the database and make the query and so on. The last, but not the least. The Prisma CLI permit to create a demigration. There are two command for demigration, the dev and the deploy. The dev is used during the development mode. So if you change the schema for instance, and you add a new column or a new table, you have to call the dev command to create the new migration. The migration are inside of the Prisma folder, inside of the migration folder. It's migration, it's inside of a single folder with the timestamp and a simple description that you can put, and the demigration is a simple file. In this case, there is only one migration to create the topping table, the pizza table, and so on. You can use the dev, but you can also use the deploy. In my case, if I run the deploy, I try to apply the migration, but the migration are already applied, and in this case, nothing happened. It's common to use the deploy in your CI or in the deploy script, depend on how you implement this stuff.
Now it's time to move to the GraphQL part.
6. GraphQL Implementation with Fastify and Prisma
In this implementation, there are two important folders: the GraphQL folder, which contains the schema for the jql server, and the app folder, which contains the real application. The real application loads plugins, including the configuration and the DB Context. The DB Context is a Fastify plugin used to initialize the Prisma client. The Prisma client is created using the connection URL and can be accessed using the app.dbcontext. There is also a hook to disconnect the connection on server shutdown.
So in this case, there are two important folders for this implementation. The first is the GraphQL folder, where you can find the old jql file. That is the schema that contains all the descriptions for your jql server. In this case, mutation contains all the mutations. Close this, okay. Mutation contains the mutation, query contains all the queries, and subscription contains all the subscriptions. You can also create one file with all the code inside. I prefer to split because it's clearer than only one file.
And then you can also create a folder with, I don't know, the input for the pizza, the pizza type, and so on. Pretty simple if you already know GraphQL, and I hope you already know it. The operation is used by Mercurius to create our test case, for instance. You can create the query for the welcome and Mercurius code gen converts this operation into an object that you can use to test your server during the development mode.
Now it's time to see the code. So in the source code, typically there is the server. As you can notice, the server is a simple Fastify server. So you have to initialize a simple Fastify instance and then you can register something. In this case, the port is 3000 and in the app folder, in the app file, you can find the real application. The real application has two main points. First, try to load the plugins and the plugins in this case are two. The configuration, so it's pretty simple in this case. I use TypeBox to check if the process environment contains the node-ev and also the database URL. So in this case, I'm sure that the process contains these two properties and these two environments.
And then the important part is the DB Context. DB Context is a simple Fastify plugin that is used to initialize the Prisma client. In this case, FP is the Fastify plugin function and you can create the Prisma client in this way, so you can use the new Prisma client and you can pass the URL, in this case, the URL is used as a connection string. Then you have to create the connection in this way and then you can decorate your server using the decorate property function and in this case, the DB Context contains our Prisma client instance. So in this way, you can use the app.dbcontext to have access to the Prisma's implementation. Last but not least, there is a hook on the onClose to disconnect the connection if the server shuts down. Pretty simple. Now it's time to see the important part for the GraphQL.
7. GraphQL and Mercurius Configuration
GraphQL is registered as another plugin, and Mercurius is a simple plugin registered in Fastify. The load schema function converts the JQL folder into real code for Mercurius. You can specify the location of the JQL file and the library to use. The converter transforms the files into schema format for Mercurius. Watch mode can be enabled to update the server in real time.
GraphQL, it register as another plugin in this case and in the GraphQL file you can find all the implementation. As you can notice, Mercurius is a simple plugin registered in Fastify and you have to pass some configuration. The first is the schema. So, using the load schema function, I use the mercurius.code.gen library to convert the JQL folder in real code for Mercurius. The first thing is the load schema file. Using the syntax, you can say to mercurius.code.gen where it can find the JQL file and the library, check, okay. It's not a good day probably for my Mac, okay. And the converter, all the file in schema form Mercurius. And also, if you want, you can enable the watch mode to change in real time your server. In this case, for me, it's only in developer mode. And in this case, when something change, I refresh the GraphQL part for other new feature or something like this.
8. Code-Gen Mercurius and Resolver Implementation
The Code-Gen Mercurius function converts the JQL file to TypeScript, generating the server definition. The resolver folder contains separate folders for queries, mutations, and subscriptions. Each folder contains the corresponding operations for pizza and topping. Let's focus on the createPizza mutation, which uses the TypeScript syntax and respects the signature defined in the jQuery file. The function takes the parent, the pizza data from the client, and the GraphQL context, including the Prisma client for database operations.
Okay, the last before going to the real code is the Code-Gen Mercurius function that use the idea of the JQL file to convert the JQL file to TypeScript. So in the target path as source resolver generated TS, you can find all the definition of your server. So you can find, for instance, the mutation, the query and the mutation. This type is used by the developer to be sure the server respect the JQL definition.
Now, if I go back to the GraphQL part, when you set up this, you have only to pay attention or take care about creating the code for your business. So for creating the loader and the resolver. So today, I speak only about the resolver because I have only 20 minutes, but it's similar for the loader. Inside of the resolver folder, you can find the indexer. The resolver is a simple object with a three property, the query, the mutation and the subscription. And in my case, I create a simple folder for each of one of these. So inside of the query, the mutation, you can find all the mutation for the pizza, for the topping and so on. Inside of the query, you can find all the query for the pizza and the topping and so on. And pretty simple in this case. I want to show you, for instance, the mutation for the createPizza. In this case, the implementation is pretty simple. So using this syntax, the typically TypeScript syntax, I create a new function called createPizza. And using the type of the mutation, I say to this function that must respect the signature of the createPizza in the mutation types. In this way, I'm sure that the code must respect the jQuery file described before.
So the function is pretty simple. So there is the parent. In this case, you don't have to pay much attention to the parent. That is the first parameter. The second parameter is the data arrived from the client, in this case, the pizza, so the name and the list of the topping. The third parameter contain the GraphQL context, if you want, the instance of the Fastify application, and also the pubsub object in this case. So using the app, you can get, for instance, the logger, in this case, Pino, or the DbContext. The DbContext, as you can see, is the Prisma client, registered before with the plugin. And in this way, you can call, for instance, the DbContext.pizza to insert the pizza in your database. The DbContext is pretty simple to use, so you can use DbContext, and you can have the pizza, in our case, the topping, and the recipe, if you want. Each model create an object with all the possibility method for insert, select, update, and delete. Pretty simple.
9. Conclusion and Resources
Then you can put together the stuff and create your business logic. PubSub allows you to notify subscriptions when something happens. The query object is used to retrieve toppings with pagination. This architecture offers benefits for working with GraphQL. Fastify and Mercurio provide a performant GraphQL server. Prisma creates a layer between your application and the database, improving developer experience and velocity. Developers can focus on business features without wasting time on framework problems. They both love TypeScript.
Then you can put together the stuff, and you can create your business logic, in this case. PubSub, what is PubSub? If you have some subscription, you can use the PubSub object to notify that something happened. In this case, I create the pizza created event, in this case, and this notify the subscription. And if something is subscribed, receive the notification, pretty simple.
The query is very similar, so I show you for instance, the topping. In this case, I use the query object, and using the getTopping, in this case, you have different object, because in this case, the query wants the pagination. You can get the limit and the offset, and you can use the find method to find the many toppings in this case, or you can also get using the getTopping to get the topping by an ID for instance. I know the example is pretty simple, but probably you have understand the benefit of using this architecture.
From the demo, I think that's all, so I wanna go to the slide again to close the talk, so... Perfect. Okay. Conclusion, I'm not a salesman, so I'm not here to sell this solution, but it's a possible solution if you want to work with GraphQL. Why we decide to move in this way? First, Fastify and Mercurio is a good option to create a GraphQL server. Fastify it's very fast if you want, and Mercurio also. And the combination help you to create a very performance server. Yes, if you know GraphQL, you can create a very bad mistake if you want. But in this case, it's not in Mercurio's side, but in the developer side, probably the problem. Using Prisma, you can create a layer between your application and the database. You can have a good developer experience. Yes, you have to create, you have to have a trade-off because go direct to the database or using NRM in this case. But with Prisma, typically you can improve the velocity of your team. And if you have some problem with the query, it's very easy to customize the query and create your own query if you want. Last but not least, Developers are focused on creating features for your business and not waste time to resolve a framework problem because there is a patch, a monkey patch that broke your server. And they both love TypeScript.
So I think that's all. If you want, here you can find the slide and also the demo. I share only my social there then. If you want, go dive to Prisma. I wrote a Prisma series and you can find it in dev.io or in this QR code. And that's all.
Contact Information and Q&A
Thank you to everyone for your support and engagement. You can connect with me on LinkedIn and Twitter, subscribe to my YouTube channel, and visit my blog for more content. If you have any questions, please feel free to ask. Now, let's address some of the questions raised. First, the code for the demo is available on GitHub. You can fork or clone the project and run the server. As for using Mercurius for GraphQL subscriptions, it supports web socket connections by default. Comparing it to GraphQL Apollo, I have recently shifted my focus and may not have the latest insights.
Thank you very much to everyone. These are my contact. So LinkedIn and Twitter are open. So you are welcome if you want to chat with me. If you want to subscribe to my YouTube channel, this is the name. And if you want to read something, dev.io or my blog post are in my blog. And that's all. Thank you. I hope you enjoyed this content and thank you to everyone and to Node Congress. Thank you, Luca. Awesome.
And we have quite a few questions already. Well, first one, I think might be partially answered already, not the one that I right now selected. One second. Is a code for a demo available somewhere? Is it on the same QR code? Yes. We can go back to the code. You have to go to this QR code. It's the GitHub code, the GitHub link, and you have to fork or clone it and run the server. It's not available in a link, but you can download, clone the project and run it. So I hope it's okay. Yeah, thanks for that. Yeah. Next one. Will you recommend using Mercurius for GraphQL subscriptions web socket connection? How would you compare it to GraphQL Apollo? Okay. In reality, GraphQL subscription, which you're using? In reality GraphQL subscription use a web socket by default. So if you want to build another web socket to handle it, you can do this without any problem. But by default, GraphQL, you can use GraphQL subscription with a web socket. Very simple. How to compare it to GraphQL Apollo? Okay. In the last period, I lose the control of Apollo probably.
Comparison of Apollo and Mercurios
I tried Apollo two years ago, but it was based on Express. They now have their own server, which may be better. However, you can integrate Apollo in Fastify without any issues. As for Apotos GraphQL, I don't have information on it. Regarding Mercurios, we chose it because it's built on top of Fastify, which our team loves. Fastify offers good performance and maintenance. The Express community has not released new versions in the past eight years, while Fastify's community is more active. A quick reminder: avoid creating too many loaders and queries, as it can increase waiting time for users.
I tried it two years ago. The problem with Apollo is based on Express. I know now they created their own server. Probably, it's better in this moment. But if you want, you can also integrate Apollo in Fastify without any problem. There is a plugin. Thank you, Matteo.
In reality, I hate to compare two stuff. There is pros and cons in the solution. Typically, you have to find the best way for your solution probably. All right. Thank you.
Any thoughts on Apotos, GraphQL, which flips the schema generation to be TypeScript first? I don't know what is Photos GraphQL in reality. So I don't have the answer probably in this case. Please give us more information. Yeah, another one.
Why Mercurios? This is a good question. In reality, as I said before, we already know Fastify and Mercurios, in this case, it's built on top of Fastify. The other motivation is the team love of Fastify, and we moved from Express to Fastify. The way to create a Node, before Node REST API with Fastify, it's similar to Express, but you have a good performance, a good maintainer. Thank you, Matteo.
Also, you can have new release. If you check the Express release, it stopped to the version four, three, I don't remember, four, three or four. Sorry, eight years. Yes, the community behind is better in this moment than other framework for the Node environment.
Quick reminder for everyone? Oh, yes. Yeah. A quick reminder to. Also, pay attention to create a lot of loader that create a lot of query that create a lot of waiting time for your user probably. Nice.
Choosing Ideal Stack for Node.js
If you want to have control of your database, use the provider exposed by the database. For the ideal stack in Node.js, use Fastify for the REST API and GraphQL. Choose Prisma for the database, and if you know the provider well, use the default provider like MongoDB.
Yeah, I think I just have one maybe question that can be a very quick one. So if you could choose like your ideal stack for let's say database plus API on yeah, let's limit it to Node.js. What would you choose? Only limit it to Node.js. Probably for the REST API and Graphql Fastify. And for the database, if you have to choose NORM, choose Prisma. If you have, if you know very well, if you have time and you know very well the provider, use the default provider for your provider. So use the MongoDB provider and so on. Typically, if you want to, if you want control of your database, use direct the provider exposed by the database, in this case, because give you more control of the staff.