Tom Gobich
@tomgobich
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
- Member Since
- Jan 10, 2021
- Lessons Completed
- 54
- Comments Contributed
- 159
- Hours Watched
- 3.73
Recent Activity
Here's what @tomgobich has been up to this past year
-
Published lesson Validating Query String Filter Values
-
Published lesson How To Paginate Filtered Query Results
-
Published lesson Pagination First, Last, Next, and Previous Buttons
-
Replied to Hello Master.When I try to make a request via the api, the message...
Hi Gabriel! Unfortunately, there isn't a straightforward answer here with the given information. Are your API requests being sent from inside your application or via a REST client? It sounds like your application works a-okay, your environment is just having issues connecting to it. This could be due to firewall settings or even WSL vs Non-WSL, if you're on Windows. Or, you may just need to use
0.0.0.0
instead oflocalhost
if you're using a REST client. -
Upvoted comment This is a great walkthrough on session based auth. One of the...
-
Replied to This is a great walkthrough on session based auth. One of the...
Thank you, Mark!! We haven't discussed social authentication with AdonisJS 6 in any of our lessons quite yet. But, once you get the user details from Google, you'll want to determine if you have a matching user already in your database, and if not, create that user. Once you've either found the matching user or created the new user, you can log them in using AdonisJS Auth.
router.get('/google/callback', async ({ ally, auth }) => { const google = ally.use('google') // ... validity checks const googleUser = await google.user() const appUser = await User.updateOrCreate({ // attempt to find a user with the matched Google Id googleId: googleUser.id }, { // no match found? merge googleId with this data and create the user // add any other data your user needs here email: googleUser.email, token: googleUser.token.token, refreshToken: googleUser.token.refreshToken }) // once we have the user, log them in await auth.use('web').login(user) })
Copied!Hope this helps!
-
Published lesson Filtering Our List by Movie Status
-
Published lesson How To Apply A Dynamic Sort Filter To Your Query
-
Published lesson Joining SQL Tables To Order By A Related Column
-
Published lesson Protecting Routes with Auth, Guest, and Admin Middleware
-
Published lesson Creating A Movie List Page
-
Published lesson Filtering A Query By Pattern Likeness
-
Published lesson Logging Out An Authenticated User
-
Published lesson Logging In An Existing User
-
Published lesson Remembering A User's Authenticated Session
-
Replied to After implementing slugify hook, I got an error, when refreshing...
Hi e4ma! I'm not all that familiar with SQLite, but if its default limit is a single connection or two, then that very well could've been the actual cause of your issue. This type of error can also occur due to a lingering SQL operation or a transaction that was left open.
-
Upvoted comment Many thanks for the effort you made to provide us with a very...
-
Replied to Many thanks for the effort you made to provide us with a very...
Thank you for watching, rnam!!
-
Published lesson The Flow of Middleware
-
Published lesson Authenticating A Newly Registered User
-
Published lesson Checking For and Populating an Authenticated User
-
Published lesson AdonisJS 6 Session Authentication in 15 Minutes
-
Replied to Nice listening with those Stripe web-hooks. I use those myself...
Adocasts has been my first time working with Stripe, we do more direct B2B sales where I work, so it's been a fun learning experience lol. :D
-
Upvoted comment It looks like the edge extension got merged into the AdonisJS...
-
Replied to It looks like the edge extension got merged into the AdonisJS...
Looks like it's still separated, though there is an AdonisJS Extensions Pack that includes all the AdonisJS-based extensions in one install
-
Upvoted comment Good content so far. Thanks for putting it together. Keep up...
-
Replied to Good content so far. Thanks for putting it together. Keep up...
Thank you, redeemefy!! I'm happy to hear it's helping, and best of luck with starting your business! You got this, just take it one line at a time!! :D
Also - apologies your Adocasts Plus badge was missing! Looks like it was a concurrency issue with the Stripe Webhook, I'll have to get that fixed up.
-
Published lesson Creating An EdgeJS Form Input Component
-
Published lesson Creating A Login Form and Validator
-
Published lesson How To Create A Custom VineJS Validation Rule
-
Replied to Interesting…. I'm coming from writing APIs with NestJS and this...
I'm not very familiar with NestJS so I, unfortunately, can't provide any direct comparisons, but under the hood, AdonisJS' query builder, for both the models and the database module, is a wrapping of the KnexJS query builder.
So, you do have access to an unrestricted query builder as well via the database query builder should you need it:
import db from '@adonisjs/lucid/services/db' await db.from('movies').select('slug', 'title').whereNotNull('released_at')
Copied!Just note that, unlike the model query builder, since the database query builder isn't going through models, naming strategies won't go into effect so you'll need to use the column names as they're defined inside the database.
Then, you also have the static model query methods as well for more straightforward queries.
-
Upvoted comment My assumption to this lesson is that the models.movies.query...
-
Replied to My assumption to this lesson is that the models.movies.query...
Yes, the model query builder will always provide a query scoped specifically to the model's table. If you don't specify a
select
within the query you're building, it will default to selecting all columns, which can be denoted by*
. So, some examples:await Movie.query() // Result: SELECT * FROM movies await Cineast.query().select('id') // Result: SELECT id FROM cineasts await Movie.query().select('slug').where('title', 'Interstellar') // Result: SELECT slug FROM movies WHERE title = 'Interstellar'
Copied! -
Published lesson Validating Form Data with VineJS
-
Published lesson Displaying Validation Errors and Validating from our Request
-
Published lesson Reusing Old Form Values After A Validation Error
-
Upvoted comment Thanks Tom.
-
-
Replied to Hello @tomgobich ,I am currently working with multi-level hierarchical...
Hi Bruce!
I'm going to preface this by saying, I'm not familiar with the closure table approach, though I did give the link a read-through.
That said, I'm not sure exactly how well this will translate to Lucid. You'll likely need to perform the actual insert as a raw query, and at that point, it may be easier to just define the entire thing as a raw query as well.
import db from '@adonisjs/lucid/services/db' await db.rawQuery(` INSERT INTO referrals(referrer, referee, level) SELECT p.referrer, c.referee, p.level + c.level + 1 FROM referrals p, referrals c WHERE p.referee = :refereeId AND c.referrer = :referrerId; `, { refereeId: 2, referrerId: 1 })
Copied!However, something like the below might work, at least for the select portion, if you're looking to keep with the query builder.
const records = await Referral.query() .join('referrals as child', 'referrals.referee', '=', 'child.referrer') .where('referrals.referee', 2) .where('child.referrer', 1) .select('referrals.referrer', 'child.referee', db.raw('referrals.level + child.level + 1 as level'))
Copied!I hope this helps!
-
Upvoted comment Hello Tom,Are you planning to cover automated testing in this...
-
Replied to Hello Tom,Are you planning to cover automated testing in this...
Hi tmuco!
Not specifically within this series, but we are planning to use the completed code from this series within another series to cover testing with Japa!
You can find what's on our radar at the page below, though series planned isn't in any particular order.
https://adocasts.com/schedule -
Upvoted comment Hi Tom,I've been porting what you did here to Adonis 6, but ...
-
Replied to Hi Tom,I've been porting what you did here to Adonis 6, but ...
Hi cbernard!!
Yeah, assuming you're using MySQL, it appears it doesn't accept zone information and that's the default way Luxon is serialized. It seems like AdonisJS takes care of this when the create/update flow goes through the model, but the model query builder looks to just be passing the update value directly through to KnexJS's update; hence why that particular attempt fails.
So, you should be able to fix your query builder update call by providing a valid MySQL date time format into the
expiresAt
value.await user.related('tokens').query().update({ // provide date time as string without timestamp offset expiresAt: DateTime.now().toSQL({ includeOffset: false }), })
Copied!Hope this helps!!
-
Published lesson How To Create Factory Relationships from a Pool of Data
-
Published lesson How To Query, Sort, and Filter by Pivot Table Data
-
Published lesson Accepting Form Data
-
Upvoted lesson request Managing Files
-
Commented on request Managing Files
Hi Bill! This sounds like a great topic to cover, thank you for the request!
We'll try to squeeze it in somewhere in between some of the Let's Learn AdonisJS 6 lessons.
-
Upvoted comment ou puis-je avoir les cours d'adocasts en français svp
-
Replied to ou puis-je avoir les cours d'adocasts en français svp
Salut! Bien qu’il n’y ait pas d’Adocasts officiels en français, comme je ne parle pas français, jetez un coup d’œil à Romain Lanz si vous ne l’avez pas déjà fait. Il a fait des diffusions en direct couvrant AdonisJS en français et est également un membre essentiel du framework.
Vous pouvez le retrouver sur YouTube et Twich.
— Translated with Bing Translate, apologies for any inaccuracies
Hi! While there is no official Adocasts in French, as I don't speak French, check out Romain Lanz if you haven't already. He's been doing live streams covering AdonisJS in French and is a core member of the framework as well.
-
Published lesson Defining Many-To-Many Relationships and Pivot Columns
-
Published lesson Many-To-Many Model Factory Relationships
-
Published lesson A Deep Dive Into Relationship CRUD with Models
-
Replied to thanks, by api resource i mean like laravel api resource https...
Ah - no, unfortunately, AdonisJS doesn't have anything that I'm aware of at present like that. Closest at the moment is the serialization options provided via Lucid, described here: https://lucid.adonisjs.com/docs/serializing-models
-
Replied to Thanks for this course. In the documentation of adonisJs 6 their...
Thanks for watching!
Yep, it sure does! You can find them in the controller section of the documentation.
router.resource('posts', PostsController).apiOnly()
Copied! -
Replied to When i call sendMail route or api i want to send the user password...
If it's sending to one inbox but not another, it's likely not a code issue but rather something invalid with how your email provider is set up. You could use a test service, like MailTrap, to verify locally that everything is okay with your code.
Once you verify that, you should be able to log in to your account with your email provider and check the logs to get more details on why it failed to send. It could be a DNS issue or even a requirements issue. For example, Yahoo and Google, have DMARC requirements.
Also, I'm not sure what your reasons are so just as a heads-up, sending user passwords via email isn't very secure as emails can be intercepted, and should their email become compromised, that password is also compromised.
-
Upvoted comment Hi, I want to send email from an email like this mail@domain...
-
Replied to Hi, I want to send email from an email like this mail@domain...
Hi Mohammad! If I'm following you correctly, you're looking to have emails sent to
[email protected]
also be received at[email protected]
, is that correct?If so, this is something you'd need to configure within your email registrar, if they support it. For example, within Zoho Mail, you would set up
[email protected]
as a group email, then add any/all[email protected]
emails you want to receive the group's emails. -
Started discussion Video Changes & Chapters
-
Published lesson Listing A Director's Movies with Relationship Existence Queries
-
Published lesson Listing and Counting a Writer's Movies
-
Published lesson Using Eager and Lazy Loading to Load A Movie's Writer and Director
-
Replied to Incase you have issues with writing to redis, head over to redis...
Thanks for sharing, Phillip!!
-
Replied to Upvote for a V6 version of this tutorial (now that the inertia...
Hey Eric! An updated version of this series is on the way, being planned now, and will begin shortly after we finish our Let's Learn AdonisJS 6 series :)
-
Published lesson Cascading and Deleting Model Relationships
-
Published lesson Defining One to Many Relationships with Lucid Models
-
Published lesson Seeding Movies with One to Many Model Factory Relationships
-
Published lesson Defining One to One Relationships Within Lucid Models
-
Published lesson Model Factory Relationships
-
Published lesson Querying Relationships and Eager Vs Lazy Loading
-
Completed lesson Generating A Unique Movie Slug With Model Hooks
-
Completed lesson Querying Recently Released and Coming Soon Movies
-
Published lesson Tapping into Model Factory States
-
Published lesson Querying Recently Released and Coming Soon Movies
-
Published lesson Generating A Unique Movie Slug With Model Hooks
-
Completed lesson Tapping into Model Factory States
-
Upvoted comment In 6, it looks like HTTPContext is a type, not an interface,...
-
Replied to In 6, it looks like HTTPContext is a type, not an interface,...
Yep, you can still extend it using the same approach, the only difference is where the type's namespace is and that it's now called just
HttpContext
instead ofHttpContextContract
. The contracts directly also no longer exists, but you can add the below anywhere you'd like, including the same file you're extending theHttpContext
within :)declare module '@adonisjs/core/http' { export interface HttpContext { organization: Organization } }
Copied! -
Replied to The adonis 6 docs talk about extending httpcontext through macros...
Yeah, the approach is still relatively the same. The only change is the type's name and namespace. In v6 you can continue just plopping additional properties directly on the HttpContext object, v5 actually had macros and getters as well :)
The types namespace is now
@adonisjs/core/http
and the name isHttpContext
, so you can extend it's type like so:declare module '@adonisjs/core/http' { export interface HttpContext { organization: Organization } }
Copied!Where you put the types doesn't matter much, you can put it in the same file you're existing the context within if you'd like or you can create a specific
types
directory off the root of your project.Here's an example of adding an
organization
property on to the HttpContext within a middleware:import type { HttpContext } from '@adonisjs/core/http' import type { NextFn } from '@adonisjs/core/types/http' import { inject } from '@adonisjs/core' import OrganizationService from '#services/organization_service' import Organization from '#models/organization' @inject() export default class InertiaMiddleware { constructor(protected organizationService: OrganizationService) {} async handle(ctx: HttpContext, next: NextFn) { const organization = await this.organizationService.active() // adding the organization onto the HttpContext ctx.organization = organization return next() } } // adding the organization onto the HttpContext interface declare module '@adonisjs/core/http' { export interface HttpContext { organization: Organization } }
Copied!Hope this helps!! :)
-
Replied to thanks for the course, really intersted to start again AdonisJS...
My Pleasure!! Thanks for watching, Ahmed! :)
-
Upvoted comment Thanks Tom for the speedy reply. I had originally tried that...
-
Replied to Thanks Tom for the speedy reply. I had originally tried that...
Anytime! Ah - yes, preloads need registered within the preloads array inside the
adonisrc.ts
file to be used; happy you were able to get your issue figured out!If you create your preload file using the Ace CLI's
node ace make:preload
command, it'll automatically register it within the preloads array for you as well. -
Replied to Ok, so the return earlier is just passing on to the next validator...
Yep, exactly!! :) Anytime!!
-
Upvoted comment Are you going to do anything with form validation? I'm trying...
-
Upvoted comment Hi Tom, really enjoying the videos. I have a question on this...
-
Replied to Hi Tom, really enjoying the videos. I have a question on this...
Hey cbernard! Thank you, I'm happy to hear that! Yeah, so the EdgeJS documentation takes a framework-agnostic approach to describing things. Within AdonisJS, the
edge
instance has already been created and you'll want to reference the already existing instance when adding your icons.Small change, but this should get things working for ya:
import edge from 'edge.js' // <-- import default export as edge instance import { edgeIconify, addCollection } from 'edge-iconify' import { icons as heroIcons } from '@iconify-json/heroicons' addCollection(heroIcons) edge.use(edgeIconify) // <-- add to that pre-existing instance
Copied! -
Replied to Are you going to do anything with form validation? I'm trying...
Yeah, validation will come with module 7, which focuses on form flow. The database modules are the last large modules, so things should start flowing quickly thereafter :)
Anything that doesn't report an error is considered valid. So, to walk through the example within Vine's documentation:
import { FieldContext } from '@vinejs/vine/types' /** * Options accepted by the unique rule */ type Options = { table: string column: string } /** * Implementation */ async function unique( value: unknown, options: Options, field: FieldContext ) { /** * 1. If the value isn't a valid string, we'll bail out here * The "string" rule will handle this particular validation * vine.string().use(unique({...})) */ if (typeof value !== 'string') { return } // 2. Otherwise, we'll continue validating uniqueness by checking the db const row = await db .select(options.column) .from(options.table) .where(options.column, value) .first() // 3. If value is NOT unique, we'll report the error if (row) { field.report( 'The {{ field }} field is not unique', 'unique', field ) } // If no error was reported by the end of the method // we'll assume everything was valid } export const uniqueRule = vine.createRule(unique)
Copied! -
Replied to Ok thanks. That might do what I want just as well.
Anytime! Yeah, most of the time, I like to offload most of my business logic into services, keeping controllers clean to just handle the request flow.
-
Replied to Hi Tom!I'm trying to inject the context into a controller, and...
Hey frp! When a route calls a controller's method as its handler the
HttpContext
is automatically provided as their first parameter, so you shouldn't need to inject the context into a controller.That said, somewhere Harminder Virk explained this well, but I can't seem to find it. The error though is because the HttpContext can't be bound in this context. If you were to bind it to a service, and then bind the service to your controller, all would work.
export default class MoviesController { async index(ctx: HttpContext) { // <-- return ctx.view.render('pages/home') } }
Copied!Here's an example of binding the HttpContext to a service and then the service to a controller.
import { HttpContext } from '@adonisjs/core/http' import { inject } from '@adonisjs/core' @inject() export default class MovieService { constructor(public ctx: HttpContext) {} }
Copied!- app
- services
- movie_service.ts
import type { HttpContext } from '@adonisjs/core/http' import Movie from '#models/movie' import { inject } from '@adonisjs/core' import MovieService from '../services/movie_service.js' @inject() export default class MoviesController { constructor(protected movieService: MovieService) {} async index({ request, view }: HttpContext) { console.log(request.url()) console.log(this.movieService.ctx.request.url()) return view.render('pages/home') } }
Copied!- app
- controllers
- movies_controller.ts
-
Published lesson Adding A Profile Model, Migration, Factory, and Controller
-
Published lesson SQL Parameters and Injection Protection
-
Published lesson Reusable Query Statements with Model Query Scopes
-
Published lesson Querying Our Movies with the Query Builder
-
Published lesson Unmapped and Computed Model Properties
-
Published lesson Altering Tables with Migrations
-
Replied to I sent you a request on linked in. You can also get in touch...
Alrighty cool, sounds good! :)
-
Published lesson The Basics of CRUD
-
Published lesson Defining Required Data with Seeders
-
Published lesson Stubbing Fake Data with Model Factories
-
Replied to Tom, I live in OTR. Would be great to grab a beer sometime!
Oh hey, small world! Yeah, that would be cool! :)
-
Published lesson Introducing Lucid Models
-
Published lesson Defining Our Models
-
Upvoted comment thank you so much for wonderful tutorial! I have all changes...
-
Replied to thank you so much for wonderful tutorial! I have all changes...
Thank you for watching!!
I believe this is the Tailwind CSS IntelliSense extension. It should immediately start working once the extension is installed :)
-
Replied to I followed exactly same steps in VSCode for eslint and prettier...
Hey there! A few things that come to mind that may turn off or disable ESLint are:
Ensure the ESLint extension is enabled in your VSCode workspace
Ensure the ESLint extension doesn't need a VSCode reload (would say "Reload VS Code" on the extension)
Ensure you have ESLint enabled within VS Code, you can find this within VS Code's settings by typing "eslint" within the search bar.
-
Replied to The URL builder in Adonis works fine, so I generate the route...
Oh good, happy to hear you got everything working! :)
-
-
-
Replied to Thanks, I will check it out. I thought I had found the answer...
Ah - gotcha, so you're using one server to serve multiple sites? If the routes are shared by all site, then you could most likely omit the
.domain()
usage as that essentially just namespaces the routes.Anytime! Hope you were able to get things working! :)
-
Published lesson Introducing and Defining Database Migrations
-
Published lesson The Flow of Migrations
-
Replied to So this still is not working. I tried it just like that, console...
I just pushed up a working example of subdomain usage you can find at the link below. If you're linking into your subdomain, you'll want to prefix the generated url with your subdomain and include the port if you're local.
https://github.com/adocasts/lets-learn-adonisjs-6/blob/subdomain_exampleThe specific files of interest here are the route definition and the url generation inside of EdgeJS. Within the route definition I also added examples for both the URL builder and the
makeUrl
method AdonisJS provides. Theroute
method inside EdgeJS is the same as therouter.makeUrl
method, so the arguments should match . Apologies, my formatter went a little extreme with themakeUrl
usage lol.Hope this helps!! :)
-
Commented on request Splitting frontend and backend styles & scripts [AdonisJS 6]
Hey there, drummersi-3! This is something we have planned within the last module of our Let's Learn AdonisJS 6 series. That module will be specific to create an admin section.
However, to get you going before then here's some steps on how to approach this. Typically, you'd have a layout that loads in assets required for your pages. In this case, you'd have two layouts one for you base application and one for your admin section.
Your application layout, we'll call this our index layout, can use the default JavaScript and CSS files provided when creating a new AdonisJS 6 web app.
/resources/js/app.js
/resources/css/app.css
We can duplicate these and alter them as needed for our admin assets
/resources/js/admin.js
/resources/css/admin.css
We'll need to inform Vite about the new JavaScript entrypoint so that it bundles it within our
vite.config.js
fileexport default defineConfig({ plugins: [ adonisjs({ /** * Entrypoints of your application. Each entrypoint will * result in a separate bundle. */ entrypoints: [ 'resources/js/app.js', ++ 'resources/js/admin.js', ], /** * Paths to watch and reload the browser on file change */ reload: ['resources/views/**/*.edge'], }), ], // ... })
Copied!For our application pages, we can then use our index layout which will use our
app.js
andapp.css
assets.<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title> {{ title || "App Layout" }} </title> @if ($slots.meta) {{{ await $slots.meta() }}} @endif {{-- here is our asset for our index layout --}} @vite(['resources/js/app.js']) </head> <body> <div class="max-w-3xl mx-auto mt-6"> @include('partials/nav') {{{ await $slots.main() }}} </div> </body>
Copied!- resources
- views
- components
- layouts
- index.edge
Then, we can duplicate this, changing it as needed, and update our Vite asset from
app.js
toadmin.js
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title> {{ title || "Admin Layout" }} </title> @if ($slots.meta) {{{ await $slots.meta() }}} @endif {{-- here is our asset for our admin layout --}} @vite(['resources/js/admin.js']) </head> <body> <div class="max-w-3xl mx-auto mt-6"> @include('partials/nav') {{{ await $slots.main() }}} </div> </body>
Copied!- resources
- views
- components
- layouts
- admin.edge
On our application pages, we can use our app layout, like so
@layout() @slot('meta') <meta name="description" content="Our application home page" /> @endslot <h1> App page </h1> @end
Copied!- resources
- views
- pages
- home.edge
Then, for our admin pages, we can use the admin layout like so
@layout.admin() @slot('meta') <meta name="description" content="Our admin home page" /> @endslot <h1> Admin page </h1> @end
Copied!- resources
- views
- pages
- admin
- index.edge
Hope this helps!
-
Published lesson Easy SVG Icons with Edge Iconify
-
Published lesson Configuring Lucid and our Database Connection
-
Published lesson Understanding our Database Schema
-
Replied to Wow thanks. I did not get that from the documentation anywhere...
Anytime!! This site is public on GitHub and uses AdonisJS 6
https://github.com/adocasts/adocastsBeyond that, I'm not aware of any that are currently public.
-
Replied to I should probably add that the 'moms' at the start of that route...
I haven't yet worked with subdomains in AdonisJS 6, but I believe they work the same as in v5. When building the route's url using the
route()
method, the third argument accepts the domain.<a href="{{ route('moms.join', {}, { domain: 'moms.test' }) }}"> My Link </a>
Copied!The subdomain behavior is siloed, so you can have the same route defined outside the subdomain as well, and they can also be dynamic. Due to these reasons, that's why the
domain
will need specified when you're specifically looking to build a route for that domain.Hope this helps!
-
Replied to Thanks! I followed the link to the source code and it looks ...
Anytime! Yep, that should do it! 😊
-
Upvoted comment Hi Tom, can you tell me why when I inspect the request object...
-
Replied to Hi Tom, can you tell me why when I inspect the request object...
It looks like AdonisJS is using the
request.url
to parse these details out using theparse
method fromnode:url
and it looks like therequest.url
only contains the path for the request. So, it's being built without those details included and since theparsedUrl
object is just an instance ofURL
that'd be why they're still included despite being null.You can get a complete parse of the URL by doing:
router.get('/my-endpoint', (ctx) => { // passing true to completeUrl will include the query string const completeUrl = new URL(ctx.request.completeUrl(true)) return completeUrl })
Copied!Host and hostname will mostly be the same. The difference is, if the URL contains a port, host will include it and hostname will not.
REQUEST: https://test.com:3333/endpoint HOST: test.com:3333 HOSTNAME: test.com
-
Completed lesson Setting Up Tailwind CSS
-
Replied to Yup, that was it. I commented out the domain() and it works....
Oh, lol! No worries at all, I'm happy you were able to get everything figured out and working! 😄
-
Replied to Thanks again. It's weird that the routes show up when you run...
Anytime!! Yeah, I honestly don't fully understand how it could work okay in one but not the other, could be a bug or maybe just a difference between the server and console applications.
Yeah, you could absolutely hard-code the imports if you're looking for something simple, but exporting as functions seems to be the only way it's completely happy for some reason. 😊
-
Replied to So I am getting "Exception Cannot GET:" in the browser on all...
Ah - yeah I see! It works fine if you have it at the top-level but once put inside a group I see the same error as you. It almost seems like the dynamic import is hoisted or something as this is the same error you guy when AdonisJS can't find a route matching the request.
It's not pretty, and can probably be cleaned up by using macros, but exporting as functions, importing at the top-level, then calling the functions within the group does seem to get things working.
So, the base router would look like:
import app from '@adonisjs/core/services/app' import router from '@adonisjs/core/services/router' import fs from 'node:fs/promises' const files = await fs.readdir(app.startPath('routes')) const definitionFunctions: Function[] = [] for (const filename of files) { const { default: definition } = await import(app.startPath(`routes/${filename.replace('.ts', '.js')}`)) definitionFunctions.push(definition) } router.group(async () => definitionFunctions.map((fn) => fn()))
Copied!- start
- routes.ts
Then, the sub-files being imported would export as functions.
import router from '@adonisjs/core/services/router' export default () => { router.get('/test-1', () => 'working 1').as('test.1') }
Copied!- start
- routes
- test.ts
There's gotta be a cleaner way, but that's the best I can figure at the moment.
Also, yeah sorry about the formatting, I really need to add a legend/guide on what all is available. Basic Markdown is supported so you can enter a code block using three backticks. You can also open a palette by typing
/
-
Replied to I guess it didn't work. Server did not throw an error but list...
Oh rats!! I think something like this might work:
import fs from 'node:fs/promises' import app from '@adonisjs/core/services/app' import router from '@adonisjs/core/services/router' const files = await fs.readdir(app.startPath('routes')) for (const filename of files) { await import(app.startPath(`routes/${filename.replace('.ts', '.js')}`)) }
Copied!- start
- routes.ts
import router from '@adonisjs/core/services/router' router.get('/test-1', () => {}).as('test.1')
Copied!- start
- routes
- test.ts
import router from '@adonisjs/core/services/router' router.get('/test-2', () => {}).as('test.2')
Copied!- start
- routes
- another.ts
-
Replied to Yeah, I used the functions on an earlier test and it worked ...
Yeah, if you're looking for something that'll dynamically pick up new route files as you add them, that looks like a valid solution! Hopefully that'll continue to work for you! 😊
-
Replied to Yeah, absolutely, I can imagine. I'm sure you are trying to ...
Yeah lol, that's the goal it to get as much out as possible! Definitely! A lot of the principals translate pretty well between 5 and 6, so the majority of v5 content is still usable, but the module change and some API changes can cause some confusion.
-
Replied to If we were to modify this to Adonis 6, we would need to change...
Yeah, the primary thing that matters is where the route definition is run. If it's run within the group, it'll be defined within the group. For example, you could also use functions!
const postRoutes = () => { router.get('/posts').as('posts.index') router.post('/posts').as('posts.store') router.patch('/posts/:id').as('posts.update') router.delete('/posts/:id').as('posts.destroy') } router.group(() => { postRoutes() }).as('api') /* Our final post routes would be named: - api.posts.index - api.posts.store - api.posts.update - api.posts.destroy */
Copied!Since the route definitions are run inside the
api
group, they'll be defined as a portion of that group. So, in essence that's the same behavior as requiring/importing those routes shown in this lesson. -
Upvoted comment I noticed on some lessons, you have the text with screenshots...
-
Replied to I noticed on some lessons, you have the text with screenshots...
Yeah, I used to provide both written and video formats for every lesson, but ended up dropping the written portion as it's a decent time suck to produce.
For example, back when both formats were being produced I was only able to create one, maybe two, lessons per week. Now, with just the video format, we're trending at around 5-9 lessons per week.
-
Published lesson State vs Share Data Flow
-
Published lesson Share vs Global Data Flow
-
Published lesson Form Basics and CSRF Protection
-
Published lesson HTTP Method Spoofing HTML Forms
-
Upvoted comment I love how you say Laravel is "kinda similar" to Adonis. That...
-
Replied to I love how you say Laravel is "kinda similar" to Adonis. That...
Yeah lol! The core team definitely used Laravel as inspiration to shape the foundations of AdonisJS! I came from Laravel as well, JavaScript has always been my primary language so I was super happy when I learned about it 😄
-
Upvoted comment this looks really cool. I had never heard of htmx. With htmx...
-
Replied to this looks really cool. I had never heard of htmx. With htmx...
Yeah, absolutely! This site is actually built using Unpoly, which is in the same hypermedia category as HTMX. All our pages are rendered using EdgeJS despite our pages loading like a SPA and our player sticking with you throughout the site while it's playing.
-
Upvoted comment Thanks Tom. I imagine you also will be updating a lot of your...
-
Replied to Thanks Tom. I imagine you also will be updating a lot of your...
Yeah, I've got quite a bit planned including additional focus on project-based series that'll cover updated topics we covered in v5! I'll be putting 100% of my focus on this series until we wrap it up, then we'll move onward from there 😄
Also, thank you very much that's very kind!! 😊
-
Upvoted comment It would be cool to get some tutorials on using other output...
-
Replied to It would be cool to get some tutorials on using other output...
Agreed, and thank you for the suggestion! It'd be good to cover those things, and we will most certainly get to them! It won't be within this series though, as we want this series to be a good entry point for everyone coming to AdonisJS 6, and using EdgeJS is the most inclusive way to do that as it's a part of the framework.
We'll plan and add series for these into our schedule!
-
Upvoted comment Would be cool to see how to debug in VS Code!
-
Replied to Would be cool to see how to debug in VS Code!
At present, I'm not entirely sure myself how to get it working, but if/when I figure it out I'll be sure to share!! 😊
-
Published lesson Component Tags, State, and Props
-
Published lesson Use Slots To Make A Button Component
-
Published lesson Extracting A Layout Component
-
Published lesson EdgeJS Templating Basics
-
Published lesson HTML Attribute and Class Utilities
-
Published lesson Making A Reusable Movie Card Component
-
Published snippet Disable Tailwind CSS Hover States on Tap Devices
-
-
Upvoted comment thanks for this course very interesting 👍👍
-
Published lesson Quick Start Apps with Custom Starter Kits
-
Published lesson Easy Imports with NodeJS Subpath Imports
-
Replied to For v6, looks like serializeOnly and serializeExcept have been...
Yeah, I need to add a way to tag these series to specific versions! Though the process is still the same, the syntax for this series is using EdgeJS & AdonisJS 5. If you're curious, you can find the full list of differences between EdgeJS 5 vs 6 here:
-
Upvoted discussion Welcome To The Feed!
-
Published lesson Environment Variables and their Validation
-
Published lesson Improved Caching with Redis
-
Published lesson Deleting Items and Flushing our Redis Cache
-
Published snippet Simple AdonisJS 6 Layout Component
-
Completed lesson Environment Variables and their Validation
-
Upvoted comment For mystical reasons, I missed that part. Sorry :DBy the way...
-
Replied to For mystical reasons, I missed that part. Sorry :DBy the way...
No worries at all, Jean!! :D
Thank you, I really appreciate that!!
-
Upvoted comment Hi,When I save routes.ts file, imports remain identical. Do ...
-
Replied to Hi,When I save routes.ts file, imports remain identical. Do ...
Hi Jean!
The standard import for controllers will work just fine, for example
import MoviesController from '#controllers/movies_controller'
Copied!However, mine changed to lazy style imports on save within the video due to the ESLint I set up and configured code actions for within VS Code in lesson 1.4 of this series.
Below is an example of the ESLint rule error that's auto-fixed by my code action configuration when I save my
routes.ts
file.ESLint is completely optional! It essentially is a style guide for your code, and anything not matching the style guide will display an ESLint error.
If you'd like to use it, you can install the ESLint extension within VS Code. Then, you can have it auto-fix ESLint errors by adding the below within your VS Code
settings.json
"editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }
Copied! -
Started discussion Welcome To The Feed!
-
Published blog post What's New in Adocasts V3
-
Replied to Really happy to see you do the model and migration stuff separately...
Thank you, pat_toast!! Yeah, for sure, it can definitely be tricky to grasp where migrations stop and models start and what the separation of concern is between them.
-
Completed lesson Singleton Services and the Idea of Caching
-
Completed lesson Defining A Structure for our Movie using Models
-
Published lesson Cleaning Up Routes with Controllers
-
Published lesson Defining A Structure for our Movie using Models
-
Published lesson Singleton Services and the Idea of Caching
-
Completed lesson Cleaning Up Routes with Controllers
-
Completed lesson Extracting Reusable Code with Services
-
Published lesson Reading and Supporting Markdown Content
-
Published lesson Listing Movies from their Markdown Files
-
Published lesson Extracting Reusable Code with Services
-
Published news AdonisJS 6 Has Landed!
-
-
Upvoted comment Great Lesson!
-
Completed lesson What We'll Need Before We Begin
-
Completed lesson Introducing AdonisJS
-
Published lesson Introducing AdonisJS
-
Published lesson What We'll Need Before We Begin
-
Published lesson Creating A New AdonisJS 6 Project
-
Published lesson Project Structure
-
Published lesson VS Code Extensions and Configuration
-
Published lesson Routes and How To Create Them
-
Published lesson Rendering a View for a Route
-
Published lesson Linking Between Routes
-
Published lesson Loading A Movie Using Route Parameters
-
Published lesson Validating Route Parameters
-
Published lesson Vite and Our Assets
-
Published lesson Setting Up Tailwind CSS
-
Completed lesson VS Code Extensions and Configuration
-
Published lesson Form HTTP Method Components
-
Published lesson Form Component Method Spoofing
-
Published lesson Form Utility Component
-
Anniversary Thanks for being an Acocasts member for 3 years
-
Published lesson Bordered, Active Bordered, and Plain Variants
-
Published lesson Identifying Accordion Items
-
Published lesson Starting Our Accordion
-
Published news AdonisJS 6 Release Date Announced
-
Published lesson Model vs Database Query Builder
-
Published lesson A Deep Dive Into Mixins & Compose
-
Completed lesson A Deep Dive Into Mixins & Compose
-
Replied to Hi, One small update required: In your app.js the vue 3 inertia...
Thank you for the heads up there, Dylan!!
-
Completed lesson Inverse Alert Style & Cascading Slots
-
Published lesson Dismissible & Self Destructing Alerts
-
Published lesson Inverse Alert Style & Cascading Slots
-
Published lesson Limiting, Sorting, & Pagination with the Query Builder
-
Completed lesson Creating Our Base Alert
-
Published lesson Adding Conditional Icon, Headline, and Message Content
-
Published lesson Adding Alert Variants
-
Published lesson Creating Our Base Alert
-
Published lesson Intro to the Query Builder & It's Where Statements
-
Completed lesson Intro to the Query Builder & It's Where Statements
-
Upvoted comment got Unauthorized access when I use middleware auth
-
Replied to got Unauthorized access when I use middleware auth
That would mean that in the eyes of your AdonisJS application, you're not authenticated. Please double-check that you've properly implemented all the steps covered within the "Fetching Our Logged In User" section.
Most notably, this block here, as this is the block that will pass along the needed headers for both client-sent and server-sent requests from Nuxt.
const headers = useRequestHeaders(['cookie']) const { data: user } = await useFetch($api('/auth'), { headers, credentials: 'include' })
Copied! -
Replied to I had the same issue but managed to work it out by cross referencing...
Thank you for the heads-up, I really appreciate it!
I've got the code blocks above updated to use
<Link>
instead of<link>
. -
Published lesson Adding Button Inverse and Outline Styles
-
Published lesson Adding Button Variants
-
Published lesson Adding Button Size Options
-
Replied to Congratulations on the excellent example. In my case I'm using...
Thank you, Walison! Unfortunately, I haven't used the LucidSoftDeletes package, so I'm not familiar with it's inner workings at all.
My best guess would be to go back through the package's documentation and ensure everything is configured correctly.
-
Published lesson Adding Interactivity to our Button
-
Published lesson Creating Our Base Button
-
Published lesson Main Slot, Named Slots, and Slot Scopes
-
Published lesson Serializing Props as Element Attributes
-
Published lesson How To Get City and State Info from User’s IP Address using IP2Location
-
Completed lesson Component Props and Default State
-
Completed lesson Component State & Passing State from EdgeJS to ApineJS
-
Completed lesson A Look At Component Reactivity
-
Published lesson Component Props and Default State
-
Published lesson Component State & Passing State from EdgeJS to ApineJS
-
Published lesson A Look At Component Reactivity
-
Completed lesson Dynamic Demo Routes for Components
-
Completed lesson View Structure and Component-Based Layouts
-
Completed lesson Getting Started, Installing AlpineJS and TailwindCSS
-
Completed lesson Exploring EdgeJS' Component System
-
Published lesson Dynamic Demo Routes for Components
-
Published lesson View Structure and Component-Based Layouts
-
Published lesson Getting Started, Installing AlpineJS and TailwindCSS
-
Published lesson Exploring EdgeJS' Component System
-
Published snippet Get User IP Address when Server is Proxied by Cloudflare
-
Upvoted comment Great to see all the changes and upcoming changes along with...
-
Replied to Great to see all the changes and upcoming changes along with...
Thank you, Josh! I greatly appreciate that!! It's great to be back!
-
Published blog post Exciting Changes Coming with Adocasts+ Subscription
-
Commented on request CRON Jobs in Adonis
Hi ismailomodara, and thank you for the request!
This sounds like a good topic! I don't have a timeline estimate at the moment, but we'll add it to our list of topics to cover.
Thanks again for the request!
-
Commented on request Adonisjs as backend and flutter as a UI
Hi dartson-yosh, thanks for the request, and sorry for the delayed response! Unfortunately, I've never used Flutter and don't know much about it. I'd imagine integration would be very similar to using AdonisJS as an API.
This is something we can look into down the road. For now, I'll leave this request as-is and if more folks end up looking for this as well, we can take a deeper look.
Thanks again for the request!
-
Commented on request Adonis with mongoDB
Hi iFeras93, thanks for the request, and sorry for the delayed response! It's been a few years since I've used MongoDB, but give me some time and this is definitely something we can cover down the road.
Thanks again for the request!
-
Published blog post Creating Blog Posts with Whisper Transcription and ChatGPT
-
Completed lesson Creating Our Filter Query with AdonisJS
-
Replied to Hello!What about middleware?
The same requirements mentioned above should apply for Nuxt middleware. If you're making any call to your AdonisJS API where you need to appear authenticated, just be sure to pass along the cookie headers and include credentials.
You could also store your authentication state within a Pinia store and reference it directly inside any Nuxt middleware.
-
Replied to Hello @tomgobich. Thanks a lot for an amazing series. Really...
Thank you, ismailomodara! I haven't quite finished this series yet, but I would say if you aren't familiar with auth yet, our User Role Authentication in 15 Minutes lesson would be a good next step, followed by our Bouncer series.
-
Replied to I did all the steps and an error is shown. require() of ES Module...
Both Inertia and this provider have had a major release since this was posted. We recently released a lesson walking through setting up the latest version
https://adocasts.com/lessons/how-to-create-an-adonisjs-5-and-inertia-1-project-with-ssr
-
Completed lesson How To Create An AdonisJS 5 & Inertia 1 Project with SSR
-
Published lesson How To Create An AdonisJS 5 & Inertia 1 Project with SSR
-
Completed lesson Installing HTMX & Project Overview
-
Replied to Thank you very much for the class! It will help me a lot
That's great to hear, Leandro! Thanks again for the lesson request!
-
-
Published snippet Using Transaction Events To Defer Actions
-
Published snippet Require Route Parameter To Start With @ To Match
-
-
Replied to This is a very great tutorial video. I've always read about ...
Thank you very much, Zubs! That means a lot!!
-
Replied to I can confirm - but I was also a bit confused. I was wondering...
Sorry about that! Since I manage this site and create the lessons in my spare time, cutting out the written portion and just focusing on video production was a decision made to help reduce the amount of time it took to create each lesson.
-
Commented on request Example code logger user actions
Alrighty, this was released yesterday and can be found here:
https://adocasts.com/lessons/how-to-use-adonisjs-model-hooks-to-log-all-user-actions
Thanks again for your request!
-
Published lesson How To Use AdonisJS Model Hooks To Log All User Actions
-
Completed lesson How To Use AdonisJS Model Hooks To Log All User Actions
-
Replied to I noticed this and the last tutorial are missing the transcript...
T'was a bug. Should be all fixed up now, thank you, Raymond!
-
Replied to "Show Ful Transcript" is not working here.
Thank you, Raymond! The selectors for this were scoped to
.body-copy
and I guess I accidentally removed that class while refactoring. -
Published lesson AdonisJS 5 API & Nuxt 3 SSR Authentication in 15 Minutes
-
Commented on request SSR using Interia and adonis
Hi Mohammed, thank you for your request!
I frequently see folks asking about dynamic authorization, so I think a series dedicated to looking at how to structure and build this would be appropriate.
We can include use cases in this series, which will include your second component level point as well.
Inertia with SSR, however, sounds like a good addition to the AdonisJS + Inertia series.
I'll likely be able to get Inertia with SSR out in the coming weeks. For the authorization series, I'll need to plan and may start sometime in July.
-
Commented on request Example code logger user actions
Hi Leandro, thank you for your request! This sounds like a great lesson. This can be done globally for all models by hooking into the base model's event system.
I should have this lesson out on July 2nd or 9th.
-
Completed lesson Easy Querying with Static Model Query Methods
-
Replied to When we seeded our roles, is there a reason why created_at and...
The
autoCreate
andautoUpdate
functionality on thecreated_at
andupdated_at
columns are defined within the model instead of at the database level. They'll only be executed when we specifically use the model for CRUD operations.Here we're using the Database module, which acts as a direct layer to the database bypassing the model completely. As a result, any hooks or actions defined on the model will not run.
-
Replied to While following along, in "Using the Service Method", is this...
Thank you, Raymond! Yes, those code blocks should've been using the
dateTime
result from the line above. Here's the corrected usage:const dateTime = DateService.toDateTime() const date = this.dateService.toDate(dateTime) // 👈 use it
-
Commented on request Checking For An Expired Auth Session
Lesson is now live and can be found here https://adocasts.com/lessons/gracefully-checking-and-handling-an-expired-auth-session
-
Published lesson Gracefully Checking and Handling An Expired Auth Session
-
Commented on request Checking For An Expired Auth Session
This lesson has been recorded and is awaiting editing.
-
Requested lesson Checking For An Expired Auth Session