Want to dig further into NodeJS Subpath Imports? Checkout the documentation referenced within this lesson here:
Easy Imports with NodeJS Subpath Imports
In this lesson, we'll learn about NodeJs Subpath Imports and how AdonisJS leverages them to help simplify our import paths throughout our application.
- Author
- Tom Gobich
- Published
- Feb 06
- Duration
- 8m 40s
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
Easy Imports with NodeJS Subpath Imports
-
[MUSIC]
-
So far, we have been making use of
-
Visual Studio Code's auto-import feature to import
-
the various files and packages that we've been
-
using throughout our application so far.
-
If we take a brief moment here to actually
-
inspect some of the imports that we have,
-
for example, I'm within our movie model right now,
-
we'll see something that we might not be familiar with,
-
this hash services/cache service.
-
Particularly, it's the hash services
-
portion that you may not be familiar with.
-
However, as we hover over this particular import,
-
we see that Visual Studio Code is successfully able to map
-
this back to a module at our code,
-
let's learn AdonisJS 6 at services/cache service file,
-
which is absolutely correct.
-
We go at services/cache service,
-
and that is indeed the file that we're importing here.
-
So how exactly is that mapping happening?
-
That's what we want to discuss here today.
-
So this is using a feature called Node.js subpath imports.
-
If we dive into the documentation here,
-
so let's open up our browser and head to
-
nodejs.org/api/packages.html/subpathimports.
-
There's a whole section here talking about what these are.
-
If we go ahead and scroll down here a little bit further,
-
we'll see subpath patterns.
-
This is the particular feature that AdonisJS
-
is making use of to allow us to specify that hash services
-
to then import from our app services directory.
-
Within the example here,
-
we see that we have an imports object,
-
which has a mapping of hash internal/star.js.
-
The hash internal is defining a subpath import namespace.
-
The star is allowing us to specify any file
-
within the internal directory
-
that we're in turn pointing to right here.
-
And then the .js is required
-
because we're working with ECMAScript modules,
-
so we need to specify the file extension.
-
The right-hand side of that is then pointing
-
to the underlying directory and the files therein
-
that our import namespace should resolve to.
-
So if we were to specify an import for hash internals/test.js,
-
the underlying file would wanna be within
-
source internal/test.js.
-
If we scroll down just slightly further,
-
Node.js will describe this within their documentation.
-
So you have an import for various features
-
from es-module-package/features/x.js,
-
and then they'll have the underlying resolved package
-
from that import right here.
-
If we scroll up a little bit further,
-
they also describe why the hash is there.
-
So right here, entries in the imports field
-
must always start with a hash
-
to ensure that they are disambiguated
-
from external package specifiers.
-
Essentially meaning it's a special character
-
that allows them to know specifically
-
that you wanna use a subpath import
-
to import the particular file that you're working with,
-
as opposed to like the at or ./ designation
-
that you could be doing to import from Node.js
-
or a relative path file.
-
Cool, so if you wanna dig into this further,
-
there's the documentation path right there.
-
Let's go and switch over to our application now
-
and hide our browser back away.
-
So we know that these are defined within our package.json,
-
so let's scroll down and take a look at our package.json.
-
You wanna scroll down just a little bit
-
until you see this imports object right here,
-
where we see a number of different
-
subpath namespaces defined.
-
We have one for our controllers, exceptions, models,
-
mails, services, listeners, et cetera.
-
With every fresh AdonisJS 6 project,
-
Adonis has set these up for us automatically
-
so that we don't need to go in and manually define them
-
whenever we get to the point where we're working with them.
-
They know that these are most likely
-
going to be directories that we need to work with
-
at some point through our applications development.
-
So they've gone ahead and defined them for us here
-
so that we can make easy use of them.
-
However, there may come a time
-
where you need to define your own
-
because you're using something to your own taste
-
or specifications.
-
For example, a lot of people like to use DTOs or ViewModels,
-
so you could add those into your project
-
and then define the subpath alias
-
for those various import paths.
-
So we're gonna end up getting rid of the work
-
that we do within this lesson here.
-
I just want to highlight how you can add
-
your own subpath imports in should you need to.
-
So let's roll with the ViewModel example.
-
So within our app directory, let's create a new file
-
and let's create a new directory called view_models.
-
And within here, we'll create a new file called movie.ts.
-
So now we have both a movie model and a movie view model.
-
Now, as the name suggests, the movie model
-
defines what a movie is per our database.
-
The movie view model then defines
-
what a movie is to our views.
-
So they're just models with two different purposes
-
within this particular example.
-
So that we don't spend too much time on this example,
-
I'm just going to go ahead and export default class,
-
movie VM, and I'm just going to extend our movie model
-
just like so.
-
If we wanted to make it obvious
-
which one we were working with,
-
we can go ahead and add an additional property on here.
-
So public type equals ViewModel, something like that.
-
So now this type property only exists on our ViewModel
-
and not on our movie model.
-
Okay, next, let's go ahead and jump
-
into our movies controller and let's make use of it.
-
So instead of using our movie
-
to query all of our different movies,
-
let's go ahead and switch this over to our movie model.
-
So we'll go ahead and just use automatic imports here
-
and we'll scroll down to our movie VM right here
-
and go and give that an import.
-
And the first thing you'll notice
-
is that this is a relative path import that it has added.
-
And that's because we haven't defined
-
a sub path import quite yet for our ViewModels directory.
-
So it's telling Node.js to look for this file at ./
-
which goes back a directory from where we're at.
-
So out of controllers and into app, into ViewModels
-
and then into movie.js.
-
Now the file extension is here as we described earlier
-
because we're working with ECMAScript modules
-
and the file extension is needed
-
for relative imports for those.
-
Now you might notice that this is a .js file
-
despite the actual file being movie.ts.
-
The reason behind that to my understanding
-
is that it was a TypeScript decision
-
because at the end of the day TypeScript
-
compiles down to JavaScript
-
and they didn't want to mix and match the file extensions.
-
So that's why that is there to my understanding.
-
Okay, so before we switch this over to a sub path import,
-
let's go and give it a test
-
just to make sure everything's still working A-okay.
-
So let's dive back into our browser
-
and give our homepage a refresh where we swap this in.
-
Okay, good, everything's still working.
-
We can hide that back away.
-
And now we know that we need to define our sub path import
-
within our package.json file.
-
So let's dive down into there.
-
And just after our models,
-
let's go ahead and add in our view models.
-
So we'll do a string hash view_models/*
-
designating that the view models namespace
-
will work for any file within here, colon.
-
And now we can add in the value for this,
-
which should be a relative path to the files.
-
So add in a string ./,
-
which is the start of a relative path
-
coming out of our applications root.
-
So if we scroll up a little bit,
-
we'll want to go into app view_models/*.js
-
to support any file within our view models.
-
So we'll go app view_models/*.js.
-
Give this a save.
-
And now we can jump back into our movies controller
-
and let's switch this relative import
-
for our sub path import.
-
So we can get rid of the ./ and replace it with a hash.
-
And now we can just get rid of the file extension
-
and voila, everything is happy so far.
-
However, there is one more step that we need to take.
-
Sub path imports are one area where JavaScript
-
and TypeScript don't communicate too well with one another
-
and TypeScript isn't actually going to read these imports
-
from our package.json file.
-
So we need to inform TypeScript about them separately.
-
So we'll have kind of a one-to-one mapping
-
in two different places
-
within both of our nodejs package.json configurations
-
and our tsconfig.json file.
-
So if we scroll down to our tsconfig.json file,
-
we will see a paths object like so
-
with all of the same mappings that we have
-
within our package.json file,
-
except they're here now for TypeScript.
-
So in order to make use of the sub path alias,
-
we'll also want to define our view model sub path alias
-
within our TypeScript paths as well.
-
Everything here will look pretty much the exact same.
-
The only difference is the value will be an array
-
rather than just a string.
-
And now we should be able to jump back into our browser,
-
give our homepage a refresh and everything still works A-OK
-
despite us now importing using sub path aliases
-
for our movie view model.
-
Okay, so within this series,
-
we're not actually going to be working with view models.
-
So we'll go ahead and get rid of what we've just done.
-
But before we do,
-
let's go ahead and summarize
-
what exactly we covered in this lesson.
-
So first, hashes within your imports
-
designate sub path imports,
-
and you can define your own,
-
but you need to do so within both the tsconfig.json file
-
and the package.json file.
-
Within the tsconfig.json, it will be within paths.
-
I'm gonna go ahead and get rid of our view model import here.
-
And this paths property is specifically
-
within the compiler options.
-
Within our package.json file,
-
it's gonna be within imports.
-
So we'll go ahead and get rid of the view models there
-
as well.
-
If we scroll up,
-
imports is just a baseline property
-
within our package.json file.
-
The package.json import is needed for node.js,
-
whereas the tsconfig.json import is needed for TypeScript.
-
You can do relative path imports.
-
So if we go back a couple of steps here,
-
this works A-okay,
-
but you will need to add in .js
-
because we're working with ECMAScript modules.
-
And you'll need to use .js
-
regardless if it's a TypeScript file.
-
So we'll give that a save.
-
And now we're ready to go ahead
-
and just delete out of our view model.
-
So we'll go into here,
-
right click it and delete.
-
And now it's gone.
-
And then we'll need to remove the import
-
and switch this back to just movie.all.
-
Give it a save.
-
Let's jump back into our browser one more time just to make sure everything's working A-OK.
-
And there we go.
-
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
-
The Flow of Middleware7m 49s
-
Authenticating A Newly Registered User4m 14s
-
Checking For and Populating an Authenticated User2m 10s
-
Logging Out An Authenticated User2m 24s
-
Logging In An Existing User6m 54s
-
Remembering A User's Authenticated Session6m 55s
-
Protecting Routes with Auth, Guest, and Admin Middleware5m 36s
-
-
Filtering and Paginating Queries
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!