Code Patterns & Behavior
In this lesson, we'll continue from the previous lesson and spend more time discussing common patterns we'll employ as we build both the server and client projects of the TinyHouse application.
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
Lesson Transcript
[00:00 - 00:14] In this lesson, we'll continue from what we've done in the previous lesson. However, here is where we'll quickly go over some high-level patterns and behavior that will sort of keep as we build the server and client projects for the tiny house application.
[00:15 - 00:31] The very first thing we're going to talk about is sort of how we're going to structure our folders and the way we want to export information from specific folders and files. The very first thing we should mention, which is important, but somewhat obvious, is we'll always look to group similar functionality together.
[00:32 - 00:43] For example, here's the source directory of our finished server project. If we had any GraphQL resolver information, we'll obviously create it and keep it within the source GraphQL resolver's folder.
[00:44 - 01:01] If we had a specific function we needed to interact with the third-party API, we'll keep it in the source lib API folder. As we start to build this functionality in our application, we're going to specify and declare where we want to group certain things and as things continue, we'll continue to group things together.
[01:02 - 01:08] So, for example, we're not going to have a GraphQL resolver map be created in the lib API folder. There's no reason for that.
[01:09 - 01:22] We have a section within our source directory that's supposed to keep that information. In multiple areas of both our server code structure and our client code structure, we're going to create explicit index files.
[01:23 - 01:39] And these index files within certain folders will help barrel export functionality together. So, for example, in the source lib directory of our client project exists a components folder where many different other section components can use and depend on.
[01:40 - 01:54] We're going to create an index file in this components folder that's going to be responsible in exporting the functionality from each individual components folder above. The app header skeleton, the error banner, the listing card, the page skeleton.
[01:55 - 02:06] In this particular example, and what we tend to do out of preference is use the star symbol to simply re export everything that exists within these files. You could specify a named export as well.
[02:07 - 02:18] But the takeaway here is that these index files will hold the responsibility to essentially be interfaces that export the functionality within this directory. Why are we doing this?
[02:19 - 02:28] There's one primary reason why we generally really prefer this approach. And that comes down to how we import this functionality in other components.
[02:29 - 02:45] For example, assume we needed a component in our react application that needed to import the four separate lib components within the lib components folder. With the public explicit index file, we can simply import everything directly from the lib components folder.
[02:46 - 03:00] As opposed to importing each component one by one by one. This will become especially important as we proceed through the course and we 'll notice how many different exports and imports we're going to do in our server project and especially in our client project.
[03:01 - 03:07] So this will keep things a lot more neat and a lot more concise. Now there is caveats or trade offs with this approach.
[03:08 - 03:26] The trade off that we can consider is the fact that we're going to have a lot of things grouped within folders. So instead of having these components simply on the highest root level, we're going to create a lib folder, a components folder and then have these components be created that then have an index file and then these components will have index files as well , et cetera, et cetera.
[03:27 - 03:30] We prefer this approach personally. However, it's up to you at the end of the day.
[03:31 - 03:44] If you generally don't want to follow this approach, as you proceed through the course, you can define your exports and imports the way you please. But we tend to stick with a particular structure and this is one very important asset to that structure.
[03:45 - 03:56] The next thing we're going to talk about is routing and URL parameters. If you've used web applications before, you're already probably familiar with routes and URLs.
[03:57 - 04:10] Routing is essentially the capability to direct us from a single URL to another URL where we're able to then see information that pertains to that particular URL. Why is routing helpful or useful?
[04:11 - 04:23] Routing allows us to keep context to where we are in our application. It allows us to use the browser back for history functionality and it even allows us to bookmark URLs and share it with others.
[04:24 - 04:33] We're going to use routes and the capability to have URL parameters to get specific information in certain routes. Let's see an example of this.
[04:34 - 04:58] Assume we wanted to have an area or location that we can sort of surface or show to another person that will allow them to get all the listings for the region of Los Angeles, California, United States. We're going to have this area or this region be a route that has a URL parameter at the very end and this URL parameter will dictate the information we serve in our application.
[04:59 - 05:07] How would that happen? We're going to go into details later on but the summary of this is we're simply going to retrieve the value of this URL parameter from the client.
[05:08 - 05:37] The client will then send this value as a variable to the server through the GraphQL API. The GraphQL API would serve that information back and the client will then surface that information and for many different areas in our application whether we're getting listings, depending on a certain listing, information about a specific user, we're going to employ this very similar pattern and you'll get more understanding of this when we start to build this out in our application.
[05:38 - 06:01] Now the next thing we're going to talk about sort of ties a little bit into what we mentioned right now when it comes to queries, we're also going to address the mutations that are going to be conducted in our app and some of the patterns around queries and mutations. And the very first pattern that you're going to get accustomed with very quickly is that queries are usually run on a page level while mutations are usually going to be run on user action.
[06:02 - 06:12] Here's an example. For example, let's look at the user page and assume you want to show information about a certain user and this certain user right now is myself, Hassan Jirdi.
[06:13 - 06:32] We're going to run a page level query, the minute this page loads to get this user information. However, the additional actions that can be done in this page, such as connecting with Stripe, this connecting with Stripe will be done based on a user action and this will then conduct a mutation.
[06:33 - 06:35] This is a usual pattern. It isn't always going to happen.
[06:36 - 06:57] There's going to be certain cases in our app where we're going to do things a little differently, but for the, I'll say majority of the cases or 90% of the time, this is the pattern we're going to notice. Similarly, following that pattern, query errors are often going to be a page level error while mutation errors would oftentimes be a notification.
[06:58 - 07:13] If we stick with that same user page example, assuming that query ever fails completely, there's going to be no information to show to the user. So we'll resort to still showing the loading indicator and showing a banner that tells the user, hey, something went wrong.
[07:14 - 07:23] We couldn't surface this user information. However, if a mutation error occurs, like you can't disconnect from Stripe or you couldn't connect from Stripe, the page data is still available.
[07:24 - 07:32] So we're not going to remove this page data. We'll simply surface an error message or a notification that just says, I'm sorry, we weren't able to do this action.
[07:33 - 07:53] Try again later. Following this same pattern again, and when it comes to building these queries and mutations in our React application, we'll notice that we're going to call queries on the parent React components to get all the data that's necessary and then use props to pass that data down to the child components that need it.
[07:54 - 08:15] And for specific mutations, these mutations will be executed on the specific React child components. So when it comes to that same user page example, this user index, essentially this user component that's going to be rendered at the user route of our application would make the page level query to get the information that's needed.
[08:16 - 08:28] And we'll use props to pass that data down to these child components. And when it comes to a specific mutation that's going to be triggered, we'll have this mutation be triggered in the specific child component that it actually occurs.
[08:29 - 08:34] Now another very important thing to again mention, this is a general pattern. This is not a must.
[08:35 - 08:38] There's going to be a few small caveats. Let me do things a little differently.
[08:39 - 08:44] And that's usually when we handle a walk in our application. However, we enjoy using that pattern.
[08:45 - 08:56] So as much as we can, we'll employ that pattern to structure the way our react application is established. The last thing we'll talk about is how we intend to build new features through part two of the course.
[08:57 - 09:13] And regardless of what we intend to do, we're going to try and follow a series of steps to get to where we want to. The very beginning, we're always going to try and understand the scope and what we intend to accomplish.
[09:14 - 09:19] Or stripe payments. We're going to explain the concept of what we intend to do before we begin coding.
[09:20 - 09:31] Then we're going to try and establish the graph QL fields we'll need in our API . At this stage, and sometimes we may not even talk about the type definitions or the resolver function.
[09:32 - 09:46] You may just simply set up simple dummy fields to explain the flow of how we want the client to interact with the server. Once these fields are understood, we'll then look to implement the type definitions and resolver functions for these new fields.
[09:47 - 09:53] This is where we need to think about, okay, what do we expect these fields to do? And what do we expect these fields to return to the client?
[09:54 - 10:13] Once that is successful and complete, this is where usually we'll look to try out and test our implementation in the graph QL Playground to verify that what we've done is correct. Once that works, we'll then move over to the client side to then build the UI and the components to consume these new graph QL fields.
[10:14 - 10:23] This is for the most part the pattern that we'll try to follow. At certain points in the course, we may realize that it's easier to just bypass one step or do both steps together.
[10:24 - 10:51] We'll move around, but for the most part, we'll try to conform to this pattern of first understanding what we need to do, building the graph QL fields, implementing the type definitions and resolver functions, and then finally building the client side UI to consume what we've done. Once you begin to build the tiny house application and you start working through Part 2, you'll get a lot more detail and a lot more understanding of a lot of the different things we've talked about in this lesson and the previous lesson.
[00:00 - 00:14] In this lesson, we'll continue from what we've done in the previous lesson. However, here is where we'll quickly go over some high-level patterns and behavior that will sort of keep as we build the server and client projects for the tiny house application.
[00:15 - 00:31] The very first thing we're going to talk about is sort of how we're going to structure our folders and the way we want to export information from specific folders and files. The very first thing we should mention, which is important, but somewhat obvious, is we'll always look to group similar functionality together.
[00:32 - 00:43] For example, here's the source directory of our finished server project. If we had any GraphQL resolver information, we'll obviously create it and keep it within the source GraphQL resolver's folder.
[00:44 - 01:01] If we had a specific function we needed to interact with the third-party API, we'll keep it in the source lib API folder. As we start to build this functionality in our application, we're going to specify and declare where we want to group certain things and as things continue, we'll continue to group things together.
[01:02 - 01:08] So, for example, we're not going to have a GraphQL resolver map be created in the lib API folder. There's no reason for that.
[01:09 - 01:22] We have a section within our source directory that's supposed to keep that information. In multiple areas of both our server code structure and our client code structure, we're going to create explicit index files.
[01:23 - 01:39] And these index files within certain folders will help barrel export functionality together. So, for example, in the source lib directory of our client project exists a components folder where many different other section components can use and depend on.
[01:40 - 01:54] We're going to create an index file in this components folder that's going to be responsible in exporting the functionality from each individual components folder above. The app header skeleton, the error banner, the listing card, the page skeleton.
[01:55 - 02:06] In this particular example, and what we tend to do out of preference is use the star symbol to simply re export everything that exists within these files. You could specify a named export as well.
[02:07 - 02:18] But the takeaway here is that these index files will hold the responsibility to essentially be interfaces that export the functionality within this directory. Why are we doing this?
[02:19 - 02:28] There's one primary reason why we generally really prefer this approach. And that comes down to how we import this functionality in other components.
[02:29 - 02:45] For example, assume we needed a component in our react application that needed to import the four separate lib components within the lib components folder. With the public explicit index file, we can simply import everything directly from the lib components folder.
[02:46 - 03:00] As opposed to importing each component one by one by one. This will become especially important as we proceed through the course and we 'll notice how many different exports and imports we're going to do in our server project and especially in our client project.
[03:01 - 03:07] So this will keep things a lot more neat and a lot more concise. Now there is caveats or trade offs with this approach.
[03:08 - 03:26] The trade off that we can consider is the fact that we're going to have a lot of things grouped within folders. So instead of having these components simply on the highest root level, we're going to create a lib folder, a components folder and then have these components be created that then have an index file and then these components will have index files as well , et cetera, et cetera.
[03:27 - 03:30] We prefer this approach personally. However, it's up to you at the end of the day.
[03:31 - 03:44] If you generally don't want to follow this approach, as you proceed through the course, you can define your exports and imports the way you please. But we tend to stick with a particular structure and this is one very important asset to that structure.
[03:45 - 03:56] The next thing we're going to talk about is routing and URL parameters. If you've used web applications before, you're already probably familiar with routes and URLs.
[03:57 - 04:10] Routing is essentially the capability to direct us from a single URL to another URL where we're able to then see information that pertains to that particular URL. Why is routing helpful or useful?
[04:11 - 04:23] Routing allows us to keep context to where we are in our application. It allows us to use the browser back for history functionality and it even allows us to bookmark URLs and share it with others.
[04:24 - 04:33] We're going to use routes and the capability to have URL parameters to get specific information in certain routes. Let's see an example of this.
[04:34 - 04:58] Assume we wanted to have an area or location that we can sort of surface or show to another person that will allow them to get all the listings for the region of Los Angeles, California, United States. We're going to have this area or this region be a route that has a URL parameter at the very end and this URL parameter will dictate the information we serve in our application.
[04:59 - 05:07] How would that happen? We're going to go into details later on but the summary of this is we're simply going to retrieve the value of this URL parameter from the client.
[05:08 - 05:37] The client will then send this value as a variable to the server through the GraphQL API. The GraphQL API would serve that information back and the client will then surface that information and for many different areas in our application whether we're getting listings, depending on a certain listing, information about a specific user, we're going to employ this very similar pattern and you'll get more understanding of this when we start to build this out in our application.
[05:38 - 06:01] Now the next thing we're going to talk about sort of ties a little bit into what we mentioned right now when it comes to queries, we're also going to address the mutations that are going to be conducted in our app and some of the patterns around queries and mutations. And the very first pattern that you're going to get accustomed with very quickly is that queries are usually run on a page level while mutations are usually going to be run on user action.
[06:02 - 06:12] Here's an example. For example, let's look at the user page and assume you want to show information about a certain user and this certain user right now is myself, Hassan Jirdi.
[06:13 - 06:32] We're going to run a page level query, the minute this page loads to get this user information. However, the additional actions that can be done in this page, such as connecting with Stripe, this connecting with Stripe will be done based on a user action and this will then conduct a mutation.
[06:33 - 06:35] This is a usual pattern. It isn't always going to happen.
[06:36 - 06:57] There's going to be certain cases in our app where we're going to do things a little differently, but for the, I'll say majority of the cases or 90% of the time, this is the pattern we're going to notice. Similarly, following that pattern, query errors are often going to be a page level error while mutation errors would oftentimes be a notification.
[06:58 - 07:13] If we stick with that same user page example, assuming that query ever fails completely, there's going to be no information to show to the user. So we'll resort to still showing the loading indicator and showing a banner that tells the user, hey, something went wrong.
[07:14 - 07:23] We couldn't surface this user information. However, if a mutation error occurs, like you can't disconnect from Stripe or you couldn't connect from Stripe, the page data is still available.
[07:24 - 07:32] So we're not going to remove this page data. We'll simply surface an error message or a notification that just says, I'm sorry, we weren't able to do this action.
[07:33 - 07:53] Try again later. Following this same pattern again, and when it comes to building these queries and mutations in our React application, we'll notice that we're going to call queries on the parent React components to get all the data that's necessary and then use props to pass that data down to the child components that need it.
[07:54 - 08:15] And for specific mutations, these mutations will be executed on the specific React child components. So when it comes to that same user page example, this user index, essentially this user component that's going to be rendered at the user route of our application would make the page level query to get the information that's needed.
[08:16 - 08:28] And we'll use props to pass that data down to these child components. And when it comes to a specific mutation that's going to be triggered, we'll have this mutation be triggered in the specific child component that it actually occurs.
[08:29 - 08:34] Now another very important thing to again mention, this is a general pattern. This is not a must.
[08:35 - 08:38] There's going to be a few small caveats. Let me do things a little differently.
[08:39 - 08:44] And that's usually when we handle a walk in our application. However, we enjoy using that pattern.
[08:45 - 08:56] So as much as we can, we'll employ that pattern to structure the way our react application is established. The last thing we'll talk about is how we intend to build new features through part two of the course.
[08:57 - 09:13] And regardless of what we intend to do, we're going to try and follow a series of steps to get to where we want to. The very beginning, we're always going to try and understand the scope and what we intend to accomplish.
[09:14 - 09:19] Or stripe payments. We're going to explain the concept of what we intend to do before we begin coding.
[09:20 - 09:31] Then we're going to try and establish the graph QL fields we'll need in our API . At this stage, and sometimes we may not even talk about the type definitions or the resolver function.
[09:32 - 09:46] You may just simply set up simple dummy fields to explain the flow of how we want the client to interact with the server. Once these fields are understood, we'll then look to implement the type definitions and resolver functions for these new fields.
[09:47 - 09:53] This is where we need to think about, okay, what do we expect these fields to do? And what do we expect these fields to return to the client?
[09:54 - 10:13] Once that is successful and complete, this is where usually we'll look to try out and test our implementation in the graph QL Playground to verify that what we've done is correct. Once that works, we'll then move over to the client side to then build the UI and the components to consume these new graph QL fields.
[10:14 - 10:23] This is for the most part the pattern that we'll try to follow. At certain points in the course, we may realize that it's easier to just bypass one step or do both steps together.
[10:24 - 10:51] We'll move around, but for the most part, we'll try to conform to this pattern of first understanding what we need to do, building the graph QL fields, implementing the type definitions and resolver functions, and then finally building the client side UI to consume what we've done. Once you begin to build the tiny house application and you start working through Part 2, you'll get a lot more detail and a lot more understanding of a lot of the different things we've talked about in this lesson and the previous lesson.