Playing Next Lesson In
seconds

Transcript

  1. So, so far we have everything working out great. We have our homepage that lists out all of the movies that we have within our movies directory. And then we can click on each one of those movies to see its inner details.

  2. However, you might have been thinking, boy, this routes file is getting a little clunky and there's some things happening twice already within it. And you're absolutely right.

  3. So today we're gonna focus on cleaning this file up here a little bit by extracting our reusable code out into services.

  4. At a high level services don't really have a specific designated purpose. They're kind of like a catch all solution to, hey, I have this reusable bit of code,

  5. let's toss it in the service so that we can use it wherever we need to, right? So one of the things that we are using multiple times is our file service to

  6. read the file, convert it for markdown, and pass it into the markdown file. So one option we have is we can create a movie service to help us read from our

  7. movies directory and get our information out from each of those files. Now the app directory is where most of our business logic for our application is gonna go.

  8. So that's where our service will want to reside. Inside of app, we'll have a designated folder specifically for services, just like we do for each one of these different topics so far.

  9. So we can manually create a services folder within here and in a file within that folder, or we can use the ACLI. Now we haven't taken a look at the ACLI quite yet, but we've actually already used it.

  10. So let's get our text editor out of the way so that we can get back to our terminal. The ACLI is actually what we're using underneath the hood to run that npm run dev command.

  11. It's running a node-ace serve command, which is an ACLI command that we could use manually to boot up our server.

  12. For example, npm run dev would be the exact same as if we ran node-ace serve-watch. This is running an ACLI command called serve, and

  13. it's telling it to watch our files for changes. If we run this, you'll see that we get back the exact same thing. Our server just booted up, as if we had run npm run dev.

  14. If we stop our server, let's clean our terminal up and just run node-ace list. This will list out all of the available commands that we have from the ACLI.

  15. Now at this point in time, we don't need to worry about the vast majority of these. But as you can see, we do have a lot of different options. For right now, the one in particular that we care about is within the make section,

  16. and it's specifically make service. We can use this command to make our new service for us, and it will just plop a new file within our app services directory.

  17. So to run this command, we could just do node-ace make service, and then we could just pass in the service name that we want. So we'll call this movies. Hit Enter, and there we go.

  18. It created a file for us at app services, movie_service.ts. We can boot our server back up before we leave, and now head back into our text editor.

  19. Once we do, we'll see our new services directory with our movie service inside of it. Within the service, let's go ahead and export, default, a new class called

  20. movie service, and let's create three different methods inside of the service. We're gonna make each of these static so that we don't need to instantiate a new instance of our movie service in order to use them.

  21. We just do movie service dot whatever the method name is. This one will be async. Let's call get slug URL. We'll pass a slug in, and this will be a type string.

  22. And what we'll wanna do in here is call app.makeURL to get the underlying file URL pointed to the passed in slug.

  23. So we're gonna want our file extension on the end of our slug if it's not already there. So first we'll wanna check and see whether or not it does have that. So if not, slug ends with .md.

  24. So if the slug doesn't end with md, we'll wanna add it on. So slug plus equals .md. Now we're ready to go ahead and call app.makeURL. So let's go ahead and return a result of that.

  25. Call app.makeURL, point the path to resources/movies/ and then inject in our slug, which is really at this point our file name.

  26. So that's gonna be our first method. Our second method will also be static and async, and we'll call this get slugs. It won't take anything in. And within here, all that we're gonna do is get our files.

  27. So const files equals await. And then we're gonna wanna use the readdir method from the file system, specifically the one from promises so that we can use it async.

  28. Go ahead and hit tab to auto import that. And then let's pass an app. let's call makeURL and point it to resources/movies.

  29. Now what we wanna return from this method is just our slugs. And at this point, we have our file names. So let's loop over each of our files.

  30. So return files.map, take that file, and let's replace .md with an empty string. Okay, so lastly, let's do another static async method.

  31. This one will be called read, and it will take in a slug that is a string. Within here, we can use our get slug URL method to get our URL.

  32. So const URL equals this, get slug URL, and pass in our slug. Then we wanna get our file. So we can do const file equals await read file.

  33. We can import that from FS promises, pass in our URL, and set the encoding to UTF-8. We have a red squiggly on our URL. Let's see, it looks like it's still returning.

  34. Okay, we have it returning back a promise. So we just wanna await that. There we go. Actually, I don't think we're doing anything async within this method, so we probably don't need the promise there. So let's go and remove our await, scroll up a little bit.

  35. Make URL here is not asynchronous, so we can just get rid of the async here. Okay, let's scroll back down. So next, we're gonna wanna pass our file into our markdown file, so let's go and import that.

  36. So import markdown file from dimer app/markdown, scroll back down, and let's do const MD equals new markdown file, and pass our file in.

  37. We'll go ahead and await MD process, and then return back MD. Cool, so we have our movie service ready to go. We'll give that a save. We can jump back into our routes.

  38. In this portion right here, we can actually get rid of and replace with our new service method. So const MD equals await movie service, hit tab to auto import it, dot read.

  39. Now all that we need to do is provide in the slug, which we can get directly from our HTTP context. So ctx params slug, and there we go. Meaning, we can also get rid of our URL up here as well.

  40. Now we can do one additional thing here as well. We can move our try catch, determining whether or not a particular movie exists, into our read method as well.

  41. So I'm gonna go ahead and copy the bulk of this, which is just the catch block, and let's cut that out, and then let's get rid of the try. Cool. So here's our new route handler for our movie show path.

  42. Let's give that a save, and now let's jump back into our read method. We have the catch copied, so we'll go ahead and paste this in, and then we'll start this method with a try, and let's just indent everything in between.

  43. We need to import our exception, so I'm just gonna type out exception, and hit tab to auto import that. We no longer have our HTTP context, so this will just go away and

  44. turn into slug, and we can give this a save. Jump back into our routes, and everything's still happy. Let's verify that everything still works by jumping into our browser. Looks like the page just refreshed, but we'll do it once more time for sanity's sake.

  45. And everything's still working there, A-OK. Let's check another movie. There we go, still working. We can hide this back away, and let's go ahead and scroll up to our homepage now.

  46. Again, this portion here is all being taken care of by our read method, so let's go ahead and get rid of all of that, and let's do const md = awaitMovieService.read. We need to pass in the slug.

  47. However, we've written it in a way where it will also accept the file name, so we can just pass the file name in, and that should be happy there. Additionally, we've also added a method for this portion here within our service as well.

  48. So we can call this const, and this method returns back our slugs, md = awaitMovieService.getSlugs.

  49. Now this array no longer represents our file names, but instead our file slugs. So we can call slugs there, and this is no longer our file name, but it's our actual slug.

  50. So we can replace those there as well, and simplify this down here too. Cool, this is looking a lot more readable now. So let's give this a save, jump back into our browser, and make sure that everything still works.

  51. Still good on this page, still good on our homepage, and this movie works too. So everything is looking A-OK.

Extracting Reusable Code with Services

In This Lesson

We'll learn about services and how we can use them to extract reusable code in a way that makes it super simple to use throughout your project.

Created by
@tomgobich
Published

Join the Discussion 2 comments

Create a free account to join in on the discussion
  1. @luiz-alves

    Is a service just a controller?

    0
    1. Responding to luiz-alves
      @tomgobich

      Hi Luiz! Though they look similar and are traditionally both classes, they aren't the same as one another from a logical standpoint as they're in charge of different things.

      Controllers are used to handle routes. AdonisJS will directly instantiate a new instance of a controller and call the appropriate method bound to the requested route.

      Services generally aid controllers and events to help them complete their objectives. This could be something as simple as a helper, like normalizing text, or as complex as the main action of an operation, like storing a new blog post.

      To put it in a real-world example, let's say you go to a dine-in restaurant. You sit at a table and the server comes over to take your order (determining which of the app's routes you need). They take your order (request) to the chef (our controller). The chef then is in charge of completing your order which may consist of:

      1. Making a burger

      2. Fetching fries

      3. Grabbing that pickle slice

      4. etc

      Any one of those little things could come from a service, but the controller (the chef) coordinates and puts it all together to complete the request (order).

      export default class DinerController {
        async order({ request, view }: HttpContext) {
          const data = await request.validateUsing(orderValidator)
      
          const burger = await BurgerService.getCheeseBurger()
          const fries = await FriesService.getMediumFry()
          const pickle = await CondimentService.getPickleSlice()
      
          return view.render('plate', {
            burger,
            fries,
            pickle
          })
        }
      }
      Copied!
      2