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.
- Author
- Tom Gobich
- Published
- Jan 27
- Duration
- 7m 4s
Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.
Adocasts
Burlington, KY
Transcript
Extracting Reusable Code with Services
-
[MUSIC]
-
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.
-
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.
-
So today we're gonna focus on cleaning this file up here a little bit
-
by extracting our reusable code out into services.
-
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,
-
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
-
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
-
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.
-
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.
-
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.
-
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.
-
It's running a node-ace serve command,
-
which is an ACLI command that we could use manually to boot up our server.
-
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
-
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.
-
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.
-
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,
-
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.
-
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.
-
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.
-
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
-
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.
-
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.
-
And what we'll wanna do in here is call app.makeURL to get the underlying
-
file URL pointed to the passed in slug.
-
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.
-
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.
-
Call app.makeURL, point the path to resources/movies/ and then inject in our slug,
-
which is really at this point our file name.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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
-
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.
-
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.
-
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.
-
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.
-
So we can call this const, and this method returns back our slugs,
-
md = awaitMovieService.getSlugs.
-
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.
-
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.
-
Still good on this page, still good on our homepage, and this movie works too.
-
So everything is looking A-OK.
-
Introduction
-
Fundamentals
-
2.0Routes and How To Create Them5m 23s
-
2.1Rendering a View for a Route6m 29s
-
2.2Linking Between Routes7m 51s
-
2.3Loading A Movie Using Route Parameters9m 17s
-
2.4Validating Route Parameters6m 6s
-
2.5Vite and Our Assets6m 38s
-
2.6Setting Up Tailwind CSS9m 5s
-
2.7Reading and Supporting Markdown Content4m 32s
-
2.8Listing Movies from their Markdown Files8m 51s
-
2.9Extracting Reusable Code with Services7m 4s
-
2.10Cleaning Up Routes with Controllers4m 52s
-
2.11Defining A Structure for our Movie using Models9m 38s
-
2.12Singleton Services and the Idea of Caching6m 11s
-
2.13Environment Variables and their Validation4m 16s
-
2.14Improved Caching with Redis10m 44s
-
2.15Deleting Items and Flushing our Redis Cache6m 46s
-
2.16Quick Start Apps with Custom Starter Kits6m 28s
-
2.17Easy Imports with NodeJS Subpath Imports8m 40s
-
-
Building Views with EdgeJS
-
3.0EdgeJS Templating Basics8m 49s
-
3.1HTML Attribute and Class Utilities6m 9s
-
3.2Making A Reusable Movie Card Component10m 24s
-
3.3Component Tags, State, and Props4m 53s
-
3.4Use Slots To Make A Button Component6m 56s
-
3.5Extracting A Layout Component5m 13s
-
3.6State vs Share Data Flow2m 59s
-
3.7Share vs Global Data Flow6m 7s
-
3.8Form Basics and CSRF Protection6m 13s
-
3.9HTTP Method Spoofing HTML Forms3m 3s
-
3.10Easy SVG Icons with Edge Iconify7m 57s
-
-
Database and Lucid ORM Basics
-
4.0Configuring Lucid and our Database Connection4m 3s
-
4.1Understanding our Database Schema9m 35s
-
4.2Introducing and Defining Database Migrations18m 35s
-
4.3The Flow of Migrations8m 28s
-
4.4Introducing Lucid Models5m 43s
-
4.5Defining Our Models6m 49s
-
4.6The Basics of CRUD11m 56s
-
4.7Defining Required Data with Seeders11m 11s
-
4.8Stubbing Fake Data with Model Factories13m 48s
-
4.9Querying Our Movies with the Query Builder15m 30s
-
4.10Unmapped and Computed Model Properties3m 24s
-
4.11Altering Tables with Migrations7m 6s
-
4.12Adding A Profile Model, Migration, Factory, and Controller2m 57s
-
4.13SQL Parameters and Injection Protection9m 19s
-
4.14Reusable Query Statements with Model Query Scopes8m 11s
-
4.15Tapping into Model Factory States9m 15s
-
4.16Querying Recently Released and Coming Soon Movies4m 59s
-
4.17Generating A Unique Movie Slug With Model Hooks7m 59s
-
-
Lucid ORM Relationships
-
5.0Defining One to One Relationships Within Lucid Models5m 49s
-
5.1Model Factory Relationships2m 54s
-
5.2Querying Relationships and Eager Vs Lazy Loading5m 17s
-
5.3Cascading and Deleting Model Relationships5m 16s
-
5.4Defining One to Many Relationships with Lucid Models6m 56s
-
5.5Seeding Movies with One to Many Model Factory Relationships5m 24s
-
5.6Listing A Director's Movies with Relationship Existence Queries8m 41s
-
5.7Listing and Counting a Writer's Movies8m 41s
-
5.8Using Eager and Lazy Loading to Load A Movie's Writer and Director5m 18s
-
5.9Defining Many-To-Many Relationships and Pivot Columns9m 48s
-
5.10Many-To-Many Model Factory Relationships4m 50s
-
5.11A Deep Dive Into Relationship CRUD with Models18m 5s
-
5.12How To Create Factory Relationships from a Pool of Data13m 55s
-
5.13How To Query, Sort, and Filter by Pivot Table Data9m 47s
-
-
Working With Forms
-
6.0Accepting Form Data12m 15s
-
6.1Validating Form Data with VineJS9m 29s
-
6.2Displaying Validation Errors and Validating from our Request7m 16s
-
6.3Reusing Old Form Values After A Validation Error2m 3s
-
6.4Creating An EdgeJS Form Input Component5m 28s
-
6.5Creating A Login Form and Validator5m 1s
-
6.6How To Create A Custom VineJS Validation Rule9m 7s
-
-
Authentication & Middleware
-
7.0The Flow of Middleware7m 49s
-
7.1Authenticating A Newly Registered User4m 14s
-
7.2Checking For and Populating an Authenticated User2m 10s
-
7.3Logging Out An Authenticated User2m 24s
-
7.4Logging In An Existing User6m 54s
-
7.5Remembering A User's Authenticated Session6m 55s
-
7.6Protecting Routes with Auth, Guest, and Admin Middleware5m 36s
-
-
Filtering and Paginating Queries
-
8.0Creating A Movie List Page3m 43s
-
8.1Filtering A Query By Pattern Likeness7m 9s
-
8.2Filtering Our List by Movie Status5m 47s
-
8.3How To Apply A Dynamic Sort Filter To Your Query7m 12s
-
8.4Joining SQL Tables To Order By A Related Column4m 49s
-
Validating Query String Filter Values7m 23s
-
How To Paginate Filtered Query Results9m 15s
-
Pagination First, Last, Next, and Previous Buttons4m 2s
-
-
User Watchlist
-
An Alternative Approach to Many-To-Many Relationships4m 56s
-
Toggling A Movie in an Authenticated User's Watchlist9m 56s
-
Listing and Filtering User Watchlist Items7m 34s
-
Allowing Users To Toggle A Movie As Watched4m 44s
-
Filtering By User's Watched Status6m 7s
-
Defining A Composite Unique Constraint4m 46s
-
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!