Playing Next Lesson In
seconds

Transcript

  1. Okay, so let's go ahead and install and configure Redis within our project. So first let's install it.

  2. So we'll run npm i @adonisjs/redis. This will install it within our project. Okay, cool, it's installed. So we can clear this out. Now, this package also requires us

  3. to configure it within our project. The configuration process is essentially going to inform our project about the particular package

  4. and it will allow the package itself to run the necessary hooks that it needs to instantiate various things that it needs in order for us to successfully use the package itself.

  5. So to configure a package, we'll wanna use the ACLI. So that'll be node ace and there's a configure command. And to this configure command, we just need to provide the package names.

  6. That's adamusjs/redis. Once we have that run, we'll see that it's done four things for us. First, it's created a configuration

  7. within our config directory called redis.ts. Within this configuration is where we'll set up our connection to Redis itself. Then it's also updated our ENV file.

  8. Since that connection information is environment specific, this is where we're storing the connection information that our config is reading from. Then it's updated our start_env.ts file,

  9. requiring those new ENV variables that have been added into our .env file. And then lastly, it's updated our adonisrc.ts file, which we've yet to talk about.

  10. So now the adonisrc.ts file is essentially in charge of configuration for our project. It is where the package has added its provider into our providers array.

  11. Providers essentially allow for the package to hook into our application's lifecycle to instantiate instances that it needs to

  12. in order for us to properly work with the package. It's also within that package's provider that it's going to read from our configuration to actually grab our connection details. Before we leave here,

  13. we can go ahead and boot our server back up, so empty and run dev, and let's dive back into our project. So we left off within our .env file, and within this file, we see three new variables.

  14. We have our redis_host, our redis_port, and our redis_password. Now by default, this package has set us up with a redis_host and redis_port value. These two values are actually the default values

  15. for Redis itself. So if you haven't changed anything within your Redis configuration, you're most likely okay to leave these values as is. The same goes for the password.

  16. By default, it's not going to utilize a password. So unless you've explicitly set up a password for your Redis configuration, you won't need to provide one in here either. Since we're on our local machine,

  17. we're not really worried about passwords at this point in time. Now within the env.ts file, that's where we're specifying that our host and our port are required. So if we dive into here, we'll see two new additions for our redis_host

  18. and our redis_port. The redis_host is specifying that it should be a string, and that string should be a host format. A host format's basically going to look like an IP address.

  19. It's going to be that 127.0.0.1 value that we have within our .env file. And then the port should be a number. Remember that the port kind of serves as like the street address for the server

  20. that we want to reach to on the specific host. So we're A-okay here. I'll go ahead and give that file a save so that it fixes our missing comma. All right, the next thing that the configuration step changed

  21. was our adonisrc.ts file. So let's go ahead and dive into there. And we'll see that we're calling define_config and providing that some form of an object. This object contains commands, service providers.

  22. There's that providers array that we had mentioned. And specifically, here's our redis_provider right here. So it's within this redis_provider

  23. that the redis_package is reading from our configuration, setting up the connection, and then providing us back a redis_package to actually be able to work with. There's some other things going on

  24. within this adonisrc.ts file, but we don't need to care about them at this point in time. We'll come across them as they become important to the series topic.

  25. So we'll go ahead and close this out for now. And the last thing that was added to our project was a new configuration file specifically for Redis with connection details. By default, our redis_config

  26. is going to have a default connection called main, and then we'll have a connections object that will allow us to define multiple connections if we need to. Out of the box though,

  27. we'll just have that single main default connection. And this main default connection is going to use the host and the port, and if needed, the password that we have set within our environment variables.

  28. It's also got some additional configuration going on down here as well. You might notice that we're reading from env.get to actually read from our environment variables.

  29. If we scroll up to see where that's coming from, we'll see it's being imported from our start directory and specifically that env.ts file. So one nice thing about that file

  30. is not only is it providing type safety whenever we boot our server up, but it's also providing type safety whenever we actually try to read from it as well. So if we scroll back down to where we call those gets

  31. and we remove, let's say our redis_host altogether, let's remove the strings as well, and we type strings back in, we're going to get an auto-complete

  32. of the variables that we have defined within our env.ts so that we know what we have within our environment variables and we can say, okay, I want the redis_host, click on it, and there we go.

  33. Now, for me particularly, I need to change one more thing in here. You most likely don't need to worry about this whatsoever. Redis allows you to have multiple different databases

  34. specified by this db0, which is the default database. I'm already using db0 for the Atacast project, so I need to switch to a different index.

  35. For this one, I'll switch it to one. Again, you most likely don't need to change this whatsoever. So I'm going to give that a save, collapse our config back down, scroll up, and now we're ready to replace

  36. our cache service implementation with redis. So let's dive into here. First things first, let's go ahead and import redis. So at the top of the file,

  37. let's call import redis from at adonisjs/redis/services/main. This is going to give us a redis service to be able to work with.

  38. All right, so let's go ahead and just roll through the different methods that we have here. So first, we have a has method, checking whether or not the key exists within our store.

  39. For this, for redis, what we'll want to do is return. We can read from redis. And there's an exists method that we can utilize to check this. Now, this method here is overloaded,

  40. meaning that there's a couple of different argument sets that we could provide into it. The various options for this essentially mean that we can either spread in an array of redis keys, or we could provide a redis key array.

  41. There's an additional callback option for a couple of these here that we could provide in as well, but we don't really care about that at this point. We just want to check whether or not the key exists. So within here, we can provide our key. Now, if we wanted to,

  42. we could mimic their spread behavior by spreading our key, turning this into keys, and now the string will be a string array. We can still provide a singular key

  43. because our arguments here are spread, but if we needed to, we could provide multiple arguments as an array. And now we can just provide our keys directly into here, and we're good to go.

  44. So now we can get rid of our prior implementation reading directly from our store. And before we move on, let's check the exists method. So this is returning back a promise of number.

  45. So since it's returning back a promise, that means that this call is asynchronous. We currently have our methods asynchronous, so we do want to switch them to async. Before I forget,

  46. I'm just going to do that for all of them. So we'll paste it in here, paste it in there, and paste it in for our delete as well. So now we're ready to take care of our get method, where we're reading a value from the store. So for this one,

  47. we're actually going to want to hold the value here momentarily before we return it. So let's do const value equals await or read from Redis dot,

  48. and there's a get method and provide in the key that we want to get. Now, before we return our value, we need to understand how Redis is actually going to store our information.

  49. So Redis is going to store it as a string, meaning that if we pass it in an object or an array, we're going to need to stringify it before we actually put it into the Redis database.

  50. So since we'll need to set it as a string, whenever we get it, we'll be getting it back as a string, meaning we'll want to parse it before we return it back. So for our return,

  51. what we'll want to do is JSON dot parse and provide the value in. Now you'll notice a red squiggly here. That's because the value could be null and parse does not like that.

  52. It requires a value. So what we'll do is check whether or not the value exists by doing value and, and. This is a shorthand kind of ternary check using the fact that JavaScript will return back

  53. the result of the if check. So essentially, if we have a value, it will move on to the and, and check, parsing our value. And since this is the last thing that is running,

  54. it will return that value back for our method. If our value does not exist, it will stop there and it will return back most likely undefined since our value does not exist.

  55. Okay, so now we have our get method. We're ready to move on to our set method where we're setting the value onto the store. So let's go ahead and get rid of this

  56. and we can return and we'll call redis.set. We'll set for our key and we'll set the value. Now we do need to stringify it. So we'll do json.stringify value.

  57. And there we go. Lastly, we have our delete. So we'll go ahead and get rid of our store deletion and we'll return redis. And they have a method called del that will serve as the deletion. So similar to our has check,

  58. this one also accepts a spread in array of arguments or a redis key array. So we can do the exact same thing here. We can spread in our keys, change that to keys, and this will now be a string array.

  59. And then we can provide the keys into the delete call. So now we're no longer using our store at all. So we can go ahead and get rid of the private store key that we had. And there's actually one additional method that we can add into the service

  60. to make clearing out our database a little bit easier. Previously, all that we had to do was stop and start our server back up. And that would clear out the in-memory cache that we had going on.

  61. But since Redis is running on a completely different server, that complicates things a little bit. So let's dive back into the service, scroll down to the end where we have a delete,

  62. and let's add an additional method. This will be async, and we'll call this flushDB. We don't need to take anything in for this one. And we'll just return back Redis.

  63. And they have a method themselves called flushDB. So we'll call that. And what this will do is it will flush out all of the data that we have stored within the particular database that we're using inside of Redis.

  64. We'll see exactly what that's doing here in a little bit whenever we actually clear our store. So we'll give this a save, and now we need to utilize this cache. So let's dive into our movie model. You'll see immediately our cache call is not happy

  65. since promise number will always return true. So what we wanna do is await that, and now we're getting back a number as our has check. If it doesn't find anything matching the slug,

  66. it will return back zero, which will be falsy, which will allow us to move on to actually reading our information. If it finds any results for our slug, it will return back one or higher,

  67. allowing us to dive into the if check where we can return our cache.get. Since this method itself is asynchronous, we're a-okay to just leave that as is.

  68. Let's scroll up to make sure the rest of the file's happy, and it looks like it. So that's really the only change that we needed to make there. So one thing that I missed here is that set is actually asynchronous as well. So if we hover over this,

  69. we're gonna see promise okay is the expected return for set. So we'll also want to await here as well. During my initial recording, I did miss this await,

  70. so you might see it disappear here in a second, but do note that you do actually want it to be there. Let's go ahead and give this a save, dive back into our browser, and we saw it do a refresh there, so if we check our terminal,

  71. we should see we didn't get any cache hits because nothing's logged into our terminal here. If we give the page a refresh, open our terminal back up, there's our cache hits.

  72. So our page is successfully utilizing our cache since everything worked A-okay. We can dive into our individual movies, go back home, and everything's still working.

Improved Caching with Redis

@tomgobich
Published by
@tomgobich
In This Lesson

We'll install and configure the AdonisJS Redis package. We'll then swap out our singleton in-memory cache service with a Redis cache implementation.

Join the Discussion 8 comments

Create a free account to join in on the discussion
  1. Incase you have issues with writing to redis, head over to redis-cli on the terminal and run this

    `config set stop-writes-on-bgsave-error no`

    Restart your redis server and all will be good.

    1
    1. Responding to PhillipMwaniki
      @tomgobich

      Thanks for sharing, Phillip!!

      1
  2. @n2zb

    Hi Tom, why do you define an overlay on redis with the CacheService class? I don't understand why you don't use the methods provided by redis directly. Is it an abstraction layer that preserves the name of the methods independently of the service used? A bit like what you explained with the aliases for routes?

    1
    1. Responding to n2zb
      @tomgobich

      Hi n2zb! We started our CacheService without using Redis, but rather an in-memory key/value store. We kept using the CacheService even with Redis for a couple reasons.

      1. I felt it would be easier to understand and follow by refactoring our preexisting service to use Redis rather than directly replacing the service with Redis. That way the before/after is all in one file.

      2. To keep the JSON parse & stringify calls abstracted to the service's get & set methods.

      3. When we have things like this that may be swapped out at some point, I like to keep them abstracted in my code to simplify any updates down the road. For example, if this lesson were released today we would've used Bentocache rather than the AdonisJS Redis package directly as Bentocache is specially attuned for caching with Redis - and will be used for the official AdonisJS Cache package coming soon.

      So yes, you're pretty spot on! 😊

      1
  3. @holodev

    I believe this redis module needs an active Redis server deployed locally? Developing on Windows, it looks like Windows Subsystem for Linux would be required to get that working. Perhaps that should be mentioned in the video?

    1
    1. Responding to holodev
      @tomgobich

      Hi holodevelop! First, thank you for your feedback! I'm always looking to improve my lesson making skills, and feedback like this definitely helps, I appreciate it!

      To answer your question, any Redis connection would do. You can even use a small free tiered one from Redis Cloud. However, we only use Redis in this lesson and the next within this series, so just skipping over is also a valid option.

      There's many things about this series I would add, remove, or do differently now if I were to go back and do them again. A heads up blurb like the below in this lesson and our before we begin lesson would definitely be one of those things.

      As a heads up, we'll only be utilizing Redis in this lesson and the next as a way to demonstrate installing, configuring, and using the package. Again, as we covered in lesson 1.1, you'll need a Redis connection to work with. If you're following along and don't see yourself using Redis for anything beyond this series, feel free to skip this and the next lesson and pick back up in lesson 2.16.

      👆 I'll circle back and add that to this lesson and another blurb to lesson 1.1, I have some updates to do in module 11 as well.

      0
      1. Responding to tomgobich
        @holodev

        Ah thanks for the response, I think I may have just skimmed lesson 1.1, which was quite a number of lessons ago. Someone who did everything in order would likely have no issue, but for people who skip around (like me), that new blurb you mentioned would indeed be quite useful!

        1
        1. Responding to holodev
          @tomgobich

          Yeah, I completely agree!! Thanks again for the helpful feedback!!

          0