How To Fix NextJS 13's N+1 Problem

Sdílet
Vložit
  • čas přidán 2. 08. 2024
  • Code: github.com/jherr/njs13-n-plus...
    Survey: docs.google.com/forms/d/e/1FA...
    Oha: docs.rs/crate/oha/0.4.5
    👉 I'm a host on the React Round-Up podcast: devchat.tv/podcasts/react-rou...
    👉 Don't forget to subscribe to this channel for more updates: bit.ly/2E7drfJ
    👉 Discord server signup: / discord
    👉 VS Code theme and font? Night Wolf [black] and Operator Mono
    👉 Terminal Theme and font? oh-my-posh with atomic and SpaceMono NF
    0:00 Introduction
    0:44 Project Introduction
    2:05 Building The NextJS 13 Application
    7:26 Testing The N+1 Performance
    11:21 Trying React's New Cache
    13:34 Creating The Backend For Frontend
    18:36 Analyzing The Returned Page
    23:59 Doing Client Components Wrong
    27:03 Outroduction
  • Věda a technologie

Komentáře • 145

  • @ooogabooga5111
    @ooogabooga5111 Před rokem +37

    This is my hidden gem channel.

    • @b_two
      @b_two Před rokem +2

      Criminally underrated

  • @Dgiulian
    @Dgiulian Před rokem +8

    The idea, the preparation, the explanation and the editing...is so much work. I can't thank you enough for making these videos.

  • @ahmedjaber8595
    @ahmedjaber8595 Před rokem +1

    This is the first time i see someone talking about eager loading in js frameworks
    Keep going!

  • @carlosavila7573
    @carlosavila7573 Před rokem

    Hi Jack, I really loved this video
    I was really impressed by the way you presented the benchmark and explained the problem with such clarity and precision. Your expertise and attention to detail were evident and made a big impact on me. I would love to see more of this type of content from you in the future.

  • @ami6014
    @ami6014 Před rokem +8

    Maybe I am missing something a core concept but.. when you make a backend for your front end, you combine the “get all ideas” and “get iPhones with ids” into a singular request that your React (front end) can can request. Yes it turns 51 requests into 1 request but 51 requests are still being made in your application to the backend server to get the data. You say it only happens once when the route is accessed. So basically it’s the same as the cached version regular n-plus-one version? When the data is cached in n-plus-one-cached you no longer make those requests to the backend after going to that page the first time. You just return the cached promises correct? So is the performance boost here that in n-plus-one-cached you still return 51 cached promises on each page load after the first one, as opposed to “backend for frontend” where it’s just a singular cached promise that is returned on each page load after the first one?
    Love all your videos!

  • @grsevero
    @grsevero Před rokem +1

    Very nice, Jack. Thank you! I had the same problem using graphql one time and I've solved using a npm lib called dataloader to batch the operations. So we would have a list of IDs to be fetched instead of a list of iphone requests.

  • @ionelLupu_
    @ionelLupu_ Před rokem

    25:55 blew my mind. Really appreciate this video. Keep it up with such examples. Love the "Don't do" section. I think we need more of these

  • @TimKindberg
    @TimKindberg Před rokem +1

    An awesome video that was well worth the watch. Thank you for exploring this tech early and explaining to the rest of us!

  • @waltervanwoudenberg6876
    @waltervanwoudenberg6876 Před rokem +2

    Amazing breakdown! Great content as always!

  • @baybay1234
    @baybay1234 Před rokem +1

    Very informative video! Thanks Jack, this is really helpful!

  • @kuba6547
    @kuba6547 Před rokem +2

    big props Jack for all the explanations !

  • @dealloc
    @dealloc Před rokem +11

    15:24 could have used Promise.allSettled/Promise.all instead here to fetch concurrently. Using await in a loop will block execution until the Promise has resolved before continuing the loop. Since there's no dependencies between each async call, there's no need to wait around.
    Additionally, you should run benchmarks against a production build rather than development because dev environments skew the results on top of the already rather flawed benchmarking.

    • @dealloc
      @dealloc Před rokem +2

      Tested the above with Promise.all and it seems to be negligent with the amount of data used in the example. Would love to do a more realistic example of hitting a remote server instead.

  • @mattd5419
    @mattd5419 Před rokem +6

    underrated channel, never a trivial tutorial!!

  • @let77044
    @let77044 Před rokem +35

    Hey Jack,
    I'm having a hard time understanding what the benefits of server components are when solutions like React Query exist, and how/why fetching data on the server is so much more performant than just fetching on the client and showing a loading spinner
    If you could make a video about either or both of these topics I'd appreciate it a ton. Filling out the survey now, thank you for the great content as always

    • @dealloc
      @dealloc Před rokem +6

      "More performant" is the wrong takeaway, either can be performant in their own way. There's tradeoffs between the two based on the kind of UX and requirements you have. While spinners aren't the worst UI to display users (there are plenty of worse things, like flashing styles), in some cases it's not desirable, at least for initial rendering.
      When able to, you should provide the UX that makes sense for your use-case. For simple listings of products, spinners are just noise as they aren't triggered by user actions and it makes things seem slower.
      It also depends on how you consume said API. Some things may not be necessary, or takes additional time to fetch upfront, so will be required to load lazily in order to not stop the entire page from rendering with the most important content.
      If your most important content happen to be slow to fetch, then that's where you should focus on optimizations.

    • @dynatle5450
      @dynatle5450 Před rokem +4

      one more reason is that you cant control client speed and location, while your server speed and location is a constant.
      what I meant by this is if you fetch on a slow client, it will be much slower than if you just fetch html once (all data fetched from your server which should be fast)

    • @bryanngen5572
      @bryanngen5572 Před rokem +2

      Smaller client side bundle. All the code lives on the server. Client gets hydrated with essentially just markup.

    • @dealloc
      @dealloc Před rokem +1

      @@dynatle5450 That depends what you're sending. If the HTML sent is larger than the what the payload would be for individual data fragments, that you could fetch lazily then it would cost more time for both server and client.
      This is especially true for things like pagination (infinite scrolling, et al.) where there's no need to fetch entire HTML for each request, only the necessary fragments of data required to render a component.

    • @hakooplayplay3212
      @hakooplayplay3212 Před rokem

      Also, server is close to Database, so if you need to fetch a lot different thing, better do it on server and then send to the client whole page

  • @sobrevivendo-no-front

    Thank you, Jack! That's why I never miss any of your videos.

  • @raymondmichael4987
    @raymondmichael4987 Před rokem

    Now I should really take the survey 😊, you guys are doing such a good job.
    🙏🏾

  • @adimardev1550
    @adimardev1550 Před rokem

    this is by far very useful content. you're making us a great developer just like you Jack! you're awesome!

  • @AaronBentley24
    @AaronBentley24 Před rokem +2

    That shell is very pretty 🤩 I’d love to see a video on your terminal/shell configs

  • @CFXTBogard
    @CFXTBogard Před rokem +6

    Excellent chapter of "the more you find out on your client components, the more you F around your server payload"

  • @ooogabooga5111
    @ooogabooga5111 Před 5 měsíci

    I think its important to discuss why n+1 problem araised in the first place, the performance boost having page load way faster was because components only fetched data after they got rendered in server/ client. So moving the data to a point where the component (parent) is redered first will give you fastest fetch of data possible which is also required to finish the redering of the individual components.

  • @dimaandoniev544
    @dimaandoniev544 Před rokem +6

    Hello Jack! I want to express my sincere appreciation for your hard work and dedication in creating each video. Your generosity in sharing them for free and with such exceptional quality is deeply appreciated. Thank you so much.

  • @DenisMitiliuc
    @DenisMitiliuc Před rokem +1

    Awesome video 🎉thank you
    Waiting for more

  • @ibrahimmohammed3484
    @ibrahimmohammed3484 Před rokem +1

    wow that's very informative, i haven't heard of that cache in react thing before !

  • @shivamjhaa
    @shivamjhaa Před rokem

    That is really good🎉🎉
    This channel has helped me a ton

  • @ThEldeRS
    @ThEldeRS Před rokem

    Extremely interesting!!! Thanks for the video!

  • @akosbalint3485
    @akosbalint3485 Před rokem +2

    Great tutorial, thank you.

  • @_singledev
    @_singledev Před rokem +1

    thank you sir, you produce very good content

  • @Dev-Siri
    @Dev-Siri Před rokem +1

    it doesnt matter if phone or pokemon.
    just make sure there's a stringMander.

  • @_hugo_cruz
    @_hugo_cruz Před rokem

    Thanks for so much Jack. Excellent video. Perfect for improving preformance. When you can make some video about render types for Nextjs, please!

  • @odoenet
    @odoenet Před rokem

    Nice! You know what, video worth it just to learn about oha, thanks!

  • @laurentblondy2592
    @laurentblondy2592 Před rokem +2

    You might simply pass options to the useMutation wrappers and use onSuccess in order to remove useEffect 😊

  • @ijbarratt_
    @ijbarratt_ Před rokem

    Great work

  • @sanzhar.danybayev
    @sanzhar.danybayev Před rokem +1

    Thank you!

  • @griffadev
    @griffadev Před rokem

    Great video as usual! I do feel some of this might be a little deceptive, by generating all the iPhones up front in the backend for frontend you essentially turned the API requests into something like what a static site generator would do. This works nice for this static API but if the API needs to be fetched with user data that approach wouldn't work well

  • @cameroon360
    @cameroon360 Před rokem +1

    Great video - thank you. Btw, what font is that?

  • @case6339
    @case6339 Před rokem

    That's nice to know that the boundary point of going from SC to CC should be thought carefully.

  • @kain1638
    @kain1638 Před rokem

    in my project I left the mapping on the server component and made a client component only on the leaf, passing down the object as a prop, here instead in aggregated-client you cut at Phones [20:50], I found this interesting and I would be curios to check the benchmarks for this case, essentially leaving the use client only on single phones

  • @16ranand
    @16ranand Před rokem +1

    nice one!

  • @ericechemane3335
    @ericechemane3335 Před rokem

    you deserve a million subs

  • @aritark
    @aritark Před rokem +1

    the last bad example reminds me of a beautifully stupid faux pas i had a couple of months ago where i had a getCategory() function that took an id and gave back information and then i used it to get the metadata from category ids in a navigation, but i forgot that i built that getCategory function for actual category pages, so every item in the navigation included the data for the first page of products

  • @spiceydev
    @spiceydev Před rokem +2

    Awesome video as always, what terminal theme is that?

    • @spiceydev
      @spiceydev Před rokem

      It's OK, just read it above

  • @hugodsa89
    @hugodsa89 Před rokem

    Survey done! ❤

  • @charliesta.abc123
    @charliesta.abc123 Před rokem +1

    Survey done

  • @nastyideas
    @nastyideas Před rokem +1

    What would be the optimal way to share that same data in another page that also needs it?
    If it was only client and react, we could have 2 useQueries, one for each page, and whichever got visited first would download it and the other one when it got visited would use it from the cache.

  • @tiagocunhafernandes6607

    Vera good!!!! Thanks alot

  • @sereyvathanakkhorn760

    With the introduction of Next 13 to use FETCH to get data, if I have a mysql database which I will have my utility functions using 'mysql2' to query the data, which of the following method is best to get to the data for the ServerComponent page or the ClientComponent page?
    1. Create an api route, and then use Fetch
    2. ServerCompoent page await the import of the utility function

  • @Inspirational48
    @Inspirational48 Před rokem

    Thanks!

  • @PhilipAlexanderHassialis

    By the way Jack, couldn't we use the aggregate pattern in the original "n-plus-one" where instead of doing n+1 calls you could do the aggregate call in the main top level component and then pass data down the line? Personally I believe that it wouldn't be that much of a difference from the "api aggregate" you demonstrate.
    As an aside, how would an RSC-with-aggregate-fetch compare in terms of caching and memory performance versus an RSC-with-internal-API-call? Isn't the second case a bit of an overkill in terms of system resources use when scaling it up, if you have to both call an api route AND do a server render?

    • @jherr
      @jherr  Před rokem +2

      You're right, I should have. This adds a network call that isn't required. I think I just got carried away on how cool the API stuff is.

  • @pmioduszewski
    @pmioduszewski Před rokem

    thx!

  • @alwaysgrowww
    @alwaysgrowww Před rokem

    If you make a full video about production ready app and e2e testing in nextjs 13, it will help me in interveiws

  • @joseandkris
    @joseandkris Před rokem

    Very well done video. Tho in theory, this is a backend problem :D

  • @mohithguptakorangi1766

    Do you have a video of your vscode setup by any chance?

  • @elamandeep
    @elamandeep Před rokem +3

    I really like ur pokemon api

  • @HungNguyen-tl9kg
    @HungNguyen-tl9kg Před rokem +1

    please correct me if i'm wrong. The point is instead of making n+1 requests on 'frontend' side, we turn into 1 request on 'backend of frontend' side, and due to Nextjs server component caching, the performance is improved, except for the first request?

    • @jherr
      @jherr  Před rokem +1

      Yeah, we get rid of the hidden N+1 pattern that NextJS allows us to make.

  • @webdev_telugu
    @webdev_telugu Před rokem +1

    In the localhost:3000/iphones can't we make parallel requests to get each Iphone data rather sequentially looping it with a for loop?

    • @jherr
      @jherr  Před rokem

      You could, but it's only run once on startup so it's not a huge overhead in this case.

  • @johnaroj
    @johnaroj Před rokem +1

    Hi jack, thank you for this tutorial. What if you want to fetch a list of 10000 ihpones in the server. Do we have a limit?

    • @jherr
      @jherr  Před rokem

      There are no framework specific limits. But the more data you get the more tags you'll make and data you'll transfer and all that. Nothing is free.

  • @DanDelarge
    @DanDelarge Před rokem +2

    I miss the Pokemons already

    • @jherr
      @jherr  Před rokem

      But the phones are so cool! And I'm getting a new one!

  • @VladisXL
    @VladisXL Před rokem

    Hi! thanks for the video!
    Does this new fetching syntax work only with experimental Next 13? Not with stable next 13?

    • @jherr
      @jherr  Před rokem +1

      It only works with the App Router, which is experimental. It does not work with the NextJS 13 pages directory, which is stable.

  • @Guergeiro
    @Guergeiro Před rokem

    Basically this is applying the Fundamental theorem of Software Engineering. The "aggregate" is nothing less than a glorified cache. Still really good info.
    One thing I like about these meta frameworks is how easy it is to add this "extra level of indirection" by having a backend for frontend.

    • @jherr
      @jherr  Před rokem

      I find myself re-treading along of old SE wisdom, but hey, it works.

  • @VilClavicus
    @VilClavicus Před rokem +2

    Hey Jack,
    It would have been nice to do one more example with request batching, basically what dataloaders do. They are most used with graphql where the resolvers exhibit the same N+1 problem as RSCs.
    That way we keep the data fetching in the components but while only a single request is actually made to the API.
    Another topic I would have liked you to tackle is component streaming: making a single big request might actually make the page load feel slower because no individual Card component can start streaming to the client before the request is done.
    I believe that a slightly longer total page load but where it is progressively streamed to the client is preferable to aggregating everything in a request.

    • @pookiepats
      @pookiepats Před rokem

      sounds like you know what you're looking for

  • @boot-strapper
    @boot-strapper Před rokem

    graphql (apollo with caching) along with ent and boom, your request is translated to sql in the service to do the most optimal sql and minimal data transfer.

    • @jherr
      @jherr  Před rokem

      No doubt. Sadly we don’t always have the option to replace the backend API.

    • @boot-strapper
      @boot-strapper Před rokem

      @@jherr I'm a big fan of building it correctly from the start :P

  • @somebodyyouusedtoknow8600

    Love his voice xd

  • @claus4tw
    @claus4tw Před rokem

    Is there a benefit introducing the network split for Comp? Or was it just to showcase how to use `use client` correctly. Because if we don't have interactivity, why bother with client comps?

    • @jherr
      @jherr  Před rokem

      It was just to demonstrate a client split if it wanted to make it interactive.

  • @snowsnowy677
    @snowsnowy677 Před rokem

    Server components are fine, but is there any way we can work with refresh tokens like in client components ???

  • @gmoniava
    @gmoniava Před rokem

    I have question about caching. All the requests are made to phones with different identifiers so what is cached here? Doesn't caching make sense when you are making say several requests to phone with same ID?

  • @leojohn6702
    @leojohn6702 Před rokem

    Do you have any totorial for nodejs with typescript?

  • @mageshyt2550
    @mageshyt2550 Před rokem

    dude your terminal look so good can i get know about your terminal setup

  • @webdev_telugu
    @webdev_telugu Před rokem

    I might sound dumb, but why don't we change the express server code itself to provide all the Iphone's data at once? Is it assumed in this case that the backend API definition is written that way and we can't change that?

    • @chungweileong
      @chungweileong Před rokem +4

      In some cases, you might not have access to the API codebase, if you have access to the codebase, then sure, you should improve the API instead, the video that shown is just a demonstration.

    • @jherr
      @jherr  Před rokem +2

      Exactly, the idea is to imitate a real world scenario where the backend team is like "this is good enough".

  • @axios7776
    @axios7776 Před rokem

    The biggest thing I would like to know is how to deploy a real-world nextjs application (with the API route).
    It feels like a naive implementation to just deploy this as an EC2.

  • @cacup7
    @cacup7 Před rokem

    Omg, react server components can quickly become a footgun. People say that this is a step towards PHP and i don't know how to feel about that, but I feel like we'll have to create a new category of FE dev due to the tight couppling with backend with react server components. Maybe, It's a good thing from full stack dev's perspective, but for me, as a front end dev, just seems wrong hahahaha.

  • @mostafamohammadi9919
    @mostafamohammadi9919 Před rokem

    there is no cache function on react .. How did you import that ? import {cahce} from 'react'

  • @hasanaliyev5231
    @hasanaliyev5231 Před rokem

    I really liked your zsh theme/configurations, could you please share them?

  • @rajatpratapsingh3123
    @rajatpratapsingh3123 Před rokem +1

    Great

  • @bryanmora4996
    @bryanmora4996 Před rokem

    how would you store a session token in next 13? there is no more localStorage!

  • @titouanv
    @titouanv Před rokem

    I may not understand but in the part "Creating the backend for frontend" what is the point of defining a API route (route handler in app directory) instead of "just" and just calling an async function ?

    • @chungweileong
      @chungweileong Před rokem

      You definitely can, but the only difference is the cache strategy.

    • @jherr
      @jherr  Před rokem +1

      Yeah, that's true, you could get away without the BFF and just have an async function that is cached that does all the work.

  • @emilfjellstrom2299
    @emilfjellstrom2299 Před rokem +1

    I might be blind, but I don't see the link for the survey. Could you please share?

  • @dylanmoore6414
    @dylanmoore6414 Před rokem +1

    The example of Client Iphone Bad component doesn't make sense to me. What use-case would there ever be to pass the entire list? You have access to the current iphone object within the map, which should be the only required json payload to client, for each render of that component. I cant imagine a scenario where anyone would pass the total list and an ID - seems like an unrealistic example. Just passing the current iphone should reduce that payload and performance issue, i would expect similar to the Phones client component implementation.

    • @jherr
      @jherr  Před rokem +1

      There is no practical use, it's just really important to know that everything you send across to the first level of a client component is going to be serialized.

    • @smakosh
      @smakosh Před rokem

      @@jherr Worth highlighting this in the video or in the title, otherwise most beginners who developers who didn't read the official docs would try that which in my opinion is a bad approach

  • @maksimsturs6666
    @maksimsturs6666 Před rokem

    What for theme you use in your youtube videos?

    • @jherr
      @jherr  Před rokem +1

      Night Wolf [black]

  • @moh6823
    @moh6823 Před rokem +1

    I swear this is the same problem i had yesterday,

  • @srsajjad7460
    @srsajjad7460 Před rokem

    okay awesome demo but with next 13 and server components things look more confusing and wrong paths opening up -_-

  • @comadeycluster
    @comadeycluster Před rokem

    How to customize zsh terminal

  • @okumuoriaro4994
    @okumuoriaro4994 Před 2 měsíci +1

    How am I only coming across this now??? #HiddenGems

  • @moinulislammoin
    @moinulislammoin Před rokem

    what's that terminal theme?

  • @webdev_telugu
    @webdev_telugu Před rokem

    Do we run into the same problem in next 12? If no, why?

    • @jherr
      @jherr  Před rokem

      Yes, but it was more obvious because you'd see the N+1 pattern right in getServerSideProps. Where with NextJS 13, where you can have any RSC make a request, you could end up running into this in a complex application and not know it.

  • @mickdelaney
    @mickdelaney Před rokem

    is it not obvious that we're going to go around in circles and just end up back at graphql + relay ?

  • @albinopepegas8391
    @albinopepegas8391 Před rokem

    What theme do you use?

    • @jherr
      @jherr  Před rokem

      Night Wolf [black] (theme and font are always in the description)

  • @NaourassDerouichi
    @NaourassDerouichi Před rokem

    After thinking about it, I see that with iPhones I can understand better than with Pokemons :D

    • @jherr
      @jherr  Před rokem +1

      Hey, at least it's looking different.

    • @NaourassDerouichi
      @NaourassDerouichi Před rokem

      @@jherr I mean, seriously, it seems for me that things are easier to understand with everyday things like iPhones compared to using Pokémons

  • @alsherifkhalaf7385
    @alsherifkhalaf7385 Před rokem +1

    I responded to survey

  • @Imjoshnewton
    @Imjoshnewton Před rokem

    Couldn’t you just move the code that you put in your endpoint into the very first server component?
    This is what I have been doing with a lot of my data fetching in next 13. I typically have a server component for the page, do my data fetching there, and then pass that data into other server components, or client components as needed.

  • @Stoney_Eagle
    @Stoney_Eagle Před rokem

    But using Pokémon is your thing 😮 it brings some consistency to the ramdomnes.

  • @boot-strapper
    @boot-strapper Před rokem

    With your solution arent you just moving the problem from front end to backend? its still a problem, its just a little better because it would be the same cache shared for all users, but its still an issue when the cache invalidates.

  • @usmanmarkaz
    @usmanmarkaz Před rokem +1

    sir plz start fully functional series on advance React or Next js from scratch plz sir

  • @Thorax232
    @Thorax232 Před rokem

    If you were working on NextJS 12 and not thinking about these sorts of problems you were doing it wrong then too. This is not a Next problem.

  • @emmanuel_medina_garcia

    You are still making a lot of requests to the original api

    • @jherr
      @jherr  Před rokem

      Not after the first request, no.

  • @nashishere
    @nashishere Před rokem +3

    So basically, you are suggesting to change all backend infrastructure to get a bit more speed and create another level of complexity which you or someone else should maintain and support, while the business people changing things constantly like they always do. so, you need to maintain one more endpoint :) i dunno, this seems like another n + 1 in the context of endpoints you need to support. If you are an architect on your project, this can be feasible, but what if you are just another mere developer who stuck between sprints :) dunno man

    • @jherr
      @jherr  Před rokem +1

      Well, as a few folks have pointed out I could have just avoided the BFF altogether and done a cached version of the N+1 query up at the front and you'd pay the price once on page load or whenever the route revalidates.

    • @nashishere
      @nashishere Před rokem +1

      @@jherr That is just another way to achieve that ofc, with it's pros and cons. What i was trying to say is, this kind of approaches generally getting a lot of pushback from other devs and business people, and sometimes you just need more than an amount of speed increase to persuade them. i struggle with these kind of people all day everyday, it is exhausting for real.
      on the technical aspect, this is a good content imo, really educative. kudos from me

  • @AndrewErwin73
    @AndrewErwin73 Před rokem +1

    Just being honest here... it is a little sad that you have to even teach this considering the fact that if you did this same process in vanilla javascript you wouldn't even run into this problem... things like React and Next were supposed to make front developers into actual programmers! But all they actually did was make EVERYTHING more complicated.

  • @sanchownik
    @sanchownik Před rokem

    Jesus, this looks like such an unnecessary way to go. Absolutely awful. Then again i haven't touched the server components concept yet so I guess I'm in the complete dark here

  • @mrwho2513
    @mrwho2513 Před rokem

    Man, I'm so bored of iPhone examples...

    • @jherr
      @jherr  Před rokem +2

      🤣 Lists of Doctors in the Dr. Who franchise?

    • @mrwho2513
      @mrwho2513 Před rokem

      @@jherr 🤣

  • @PapaVikingCodes
    @PapaVikingCodes Před rokem +1

    I go files left and diffs/git right column...heathens the lot of us..