Move to Next.js?
Unanswered
Dwarf Crocodile posted this in #help-forum
Dwarf CrocodileOP
I am new to webD. I am currently using CRA, flask and sqlite DB. Hosted on AWS (ec2) with nginx server to serve my Frontend and proxy my requests to flask.
i have few questions:
- Can I move my current setup to Nextjs without changing my deployment steps?
- Advantages and disadvantages (if any) over the current setup?
i have few questions:
- Can I move my current setup to Nextjs without changing my deployment steps?
- Advantages and disadvantages (if any) over the current setup?
100 Replies
@Dwarf Crocodile I am new to webD. I am currently using CRA, flask and sqlite DB. Hosted on AWS (ec2) with nginx server to serve my Frontend and proxy my requests to flask.
i have few questions:
- Can I move my current setup to Nextjs without changing my deployment steps?
- Advantages and disadvantages (if any) over the current setup?
yes you can move. I think ec2 can be pretty expensive pretty fast with your nginx server, so you have at least the money advantage. And you also need to do less, so should be easier for you as well
@B33fb0n3 yes you can move. I think ec2 can be pretty expensive pretty fast with your nginx server, so you have at least the money advantage. And you also need to do less, so should be easier for you as well
Dwarf CrocodileOP
sorry, couldn't get ur point. how will nextjs have the
money advantage
and need to do less
?Well the big advantage is CRA is end of life and nextjs isnt!
@adam.birds Well the big advantage is CRA is end of life and nextjs isnt!
Dwarf CrocodileOP
π
is that why CRA was deprecated
xD
@Dwarf Crocodile is that why CRA was deprecated
Yeah it isnβt maintained any more! Your options are either NextJS or React+Vite! The latter is still great for your fully client side apps. Whereas if you want SEO benefits, speed etc for a website rather than a web app! NextJS is the way! NextJS can also do web apps too!
Dwarf CrocodileOP
Yes I need SEO benefits too
i hadto use react helmet and mimic SSR somehow on my current CRA setup
:/
Yeah NextJS is all built in! And you still run it behind nginx!
Dwarf CrocodileOP
so i dont have to do much deployment changes?
just migrate to next?
what about my flask APIs, (doing some user management stuff there)
Just call them from next
Dwarf CrocodileOP
will it support everything? that i am using on flask?
or for the time being can I keep my flask APIs ?
You could rewrite the backend in next if you wanted! But Iβll be honest I always use Django as my backend as I prefer Python for that stuff and you can call any api you want in nextjs
Dwarf CrocodileOP
yes, i prefer flask, so i am thinking if I move, i will keep my flask backend (for the time being)
You donβt need to make a backend change to use Next.js, Next comes with nice βbackend integrationβ but itβs not mandatory, I would say itβs even better to keep them separated in bigger projects
Dwarf CrocodileOP
and are there any good code repo's that i can have a look at?
which has like marketing pages (home, demo, technology) + then some login page and then some protected routes for user dashaboard?
cauz mine is similar to that
just to get idea
how things would look like
https://github.com/adb-software-solutions/debuglife
Iβm working on this at the moment. NextJS, Django backend. Mainly been doing client side stuff so far but feel free to take a look
Iβm working on this at the moment. NextJS, Django backend. Mainly been doing client side stuff so far but feel free to take a look
Iβve only got dashboard stuff so far!
Dwarf CrocodileOP
thanks, i will have a look π
if you have any other code examples, plz do share, kind of new to these things
Iβll see if I have any other public repo when I get to my PC!
Dwarf CrocodileOP
Hey, should I migrate my current CRA directly (which also has many unused dependencies in it, that i will need to remove)
or
Should I start with a new Nextjs project and simply copy-paste my CRA react components in it?
or
Should I start with a new Nextjs project and simply copy-paste my CRA react components in it?
Start from scratch, copy and update the components you need.
Dwarf CrocodileOP
Ok
and which one should I go for: app router or page router?
this is how it looks right now:
CRA
βββ public/
βββ src/
β βββ dashboard/ # admin-dashboard related components & pages
β βββ marketing/ # Marketing related components & pages (Home, features, pricing)
β βββ utils/
β βββ App.js
β βββ index.js
β βββ index.css
βββ .env
βββ .gitignore
βββ README.md
βββ package.json
βββ tailwind.config.js
app router, its the go-to going forward.
Dwarf CrocodileOP
Hey guys, still confused about how to do folder structuring with app router. Saw couple of videos on app router too, got to know about grouping, which I think will be helpful.
But I have 2 extra pages that I want to show only to admins and not to clients.
Also, when role == admin, I want the dashboard route to be
the current CRA strucutre:
But I have 2 extra pages that I want to show only to admins and not to clients.
Also, when role == admin, I want the dashboard route to be
/dashboard/admin-viewer
and for clients it will be /dashboard/viewer
.the current CRA strucutre:
CRA
βββ public/
βββ src/
β βββ marketing/ ( # Public-facing pages)
β β βββ components/
β β β βββ Cta.jsx
β β β βββ Footer.jsx
β β β βββ Navbar.jsx
β β β
β β βββ pages/
β β βββ Homepage.jsx
β β βββ Features.jsx
β β βββ Pricing.jsx
β β βββ Contact.jsx
β β βββ PrivacyPolicy.jsx
β β βββ TermsOfUse.jsx
β β
β βββ dashboard/ ( # Protected dashboard section)
β β βββ components/
β β β βββ AdminDashboard.jsx
β β β βββ ClientDashboard.jsx
β β β βββ AdminViewer.jsx
β β β βββ ClientViewer.jsx
β β β βββ AddClient.jsx
β β β βββ few more.....
β β βββ pages/
β β βββ Dashboard.jsx (render dashboard based on roles: admin, client)
β β βββ Viewer.jsx
β β βββ PasswordReset.jsx
β β βββ (2 more pages if role == admin)
β β
β βββ utils/
β βββ App.js
β βββ index.js
β βββ index.css
βββ .env
βββ .gitignore
βββ README.md
βββ package.json
βββ tailwind.config.js
Roseate Spoonbill
I think you need to clarify the URL structure you want to achieve. There are multiple ways to do something that resembles what you described and it's hard to tell which will match your use case without knowing details.
Off the top of my head, these are related mechanisms you can work with (in random order):
- Redirects and rewrites - e.g. upon login redirect to home page based on user role.
- Middleware - run code before entering any route and decide if user should be redirected etc - be careful though, this runs on every route in the whole app
- Nested layouts - run authentication checks in layout within folder. Redirect if needed, or show 404 page.
Off the top of my head, these are related mechanisms you can work with (in random order):
- Redirects and rewrites - e.g. upon login redirect to home page based on user role.
- Middleware - run code before entering any route and decide if user should be redirected etc - be careful though, this runs on every route in the whole app
- Nested layouts - run authentication checks in layout within folder. Redirect if needed, or show 404 page.
Dwarf CrocodileOP
Like, in CRA I used to do like this in my
I was using
- if admin, then
- if client, then
- if employee, then
app.js
:{isLoggedIn && (
<Sidebar handleLogout={handleLogout}>
<Routes>
<Route
path="/login"
element={
roleValue === "admin" ? (
<Navigate to="/admin-viewer" />
) : (
<Navigate to="/viewer" />
)
}
/>
<Route path="/admin-viewer" element={<AdminViewer />} />
<Route
path="/viewer"
element={
roleValue === "client" ? (
<ClientViewer />
) : roleValue === "employee" ? (
<EmployeeViewer />
) : (
<div>Unauthorized</div>
)
}
/>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/job-submit" element={<JobSubmit />} />
<Route path="/job-status" element={<JobStatus />} />
<Route path="/change-password" element={<ChangePassword />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Sidebar>
)}
I was using
isLoggedIn
to check if a user is logged-in and roleValue
to redirect users from /login
to respective paths: - if admin, then
/admin-viewer
which opens <AdminViewer/>
- if client, then
/viewer
which opens <ClientViewer/>
- if employee, then
/viewer
which opens <EmployeeViewer/>
Dwarf CrocodileOP
don't know if this will help
Roseate Spoonbill
So out of what I mentioned, I'd probably do one of the following:
- create
- use [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) to have all auth checks done in one place - there's bunch of examples for this, e.g. https://medium.com/@turingvang/nextjs-middleware-auth-56a2da4ea341
Layout/page level auth is more page-oriented. It will also make it easier to run only for pages that need actual auth.
Middleware method is more if you need fine-tuned response codes, or simply want to have all declared in one place. Downside is that middleware will run on all routes (including internal next routes and api handlers), so you need to be extra careful what is middleware configured to run with.
- create
(dashboard)/dashboard/(admin-only)/
directory and put a new layout inside. On the top of the layout, you can authenticate the user and perform something like if (!user.isAdmin) return notFound()
. You can also do it in your current structure, by putting this check in your admin-only pages.- use [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) to have all auth checks done in one place - there's bunch of examples for this, e.g. https://medium.com/@turingvang/nextjs-middleware-auth-56a2da4ea341
Layout/page level auth is more page-oriented. It will also make it easier to run only for pages that need actual auth.
Middleware method is more if you need fine-tuned response codes, or simply want to have all declared in one place. Downside is that middleware will run on all routes (including internal next routes and api handlers), so you need to be extra careful what is middleware configured to run with.
I use an Auth Guard added to my layout.
@adam.birds I use an Auth Guard added to my layout.
About that, if you delete the cookies manually and then navigate via Links in your app (within the same layout), do you get redirected because youβre not authenticated or keeps navigating ?
Since session is attached to Layout via a wrapper (AuthGuard) and layouts are maintained in navigations, what happens?
Since session is attached to Layout via a wrapper (AuthGuard) and layouts are maintained in navigations, what happens?
@Roseate Spoonbill So out of what I mentioned, I'd probably do one of the following:
- create `(dashboard)/dashboard/(admin-only)/` directory and put a new layout inside. On the top of the layout, you can authenticate the user and perform something like `if (!user.isAdmin) return notFound()`. You can also do it in your current structure, by putting this check in your admin-only pages.
- use [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) to have all auth checks done in one place - there's bunch of examples for this, e.g. https://medium.com/@turingvang/nextjs-middleware-auth-56a2da4ea341
Layout/page level auth is more page-oriented. It will also make it easier to run only for pages that need actual auth.
Middleware method is more if you need fine-tuned response codes, or simply want to have all declared in one place. Downside is that middleware will run on all routes (including internal next routes and api handlers), so you need to be extra careful what is middleware configured to run with.
Dwarf CrocodileOP
I liked the simplicity of your first idea. I will try with that for the time being (cauz I need the app running by tmrrw). And after getting comfortable with next, i will try to use middleware to do this stuff. Thank you for the docs, will give them a read.
Also, I thought I could only use a node-based server in Nextjs, but it also allow flask servers?
https://vercel.com/templates/next.js/nextjs-flask-starter
Does this mean, i can simply copy paste my backend APIs into Next's /api ?
Does this mean, i can simply copy paste my backend APIs into Next's /api ?
@Dwarf Crocodile https://vercel.com/templates/next.js/nextjs-flask-starter
Does this mean, i can simply copy paste my backend APIs into Next's /api ?
Roseate Spoonbill
If you are trying to get comfortable with Next, try to use it solo first. Next is JS-based and running it with flask will essentially run python and next in parallel. Not that it's a bad thing, but not necessarily where Next will shine. With Next you have the ability to call server functions from client-side code, which not only is type-safe, but also allows you to reduce number of API route handlers to bare minimum required by external clients.
Once you get comfortable with Next and learn the pros and cons of the solution, as well as how revalidation patterns work, then you'll be able to make more educated decision about incorporating other languages/frameworks into the stack.
Ofc. that is simply my subjective opinion, do whatever feels right for your learning path π
Once you get comfortable with Next and learn the pros and cons of the solution, as well as how revalidation patterns work, then you'll be able to make more educated decision about incorporating other languages/frameworks into the stack.
Ofc. that is simply my subjective opinion, do whatever feels right for your learning path π
@luis_llanes About that, if you delete the cookies manually and then navigate via Links in your app (within the same layout), do you get redirected because youβre not authenticated or keeps navigating ?
Since session is attached to Layout via a wrapper (AuthGuard) and layouts are maintained in navigations, what happens?
I would have to check tbh. It might be something I have to rethink for NextJS apps. The approach is seemless in react-router-dom but your logic does seem ti could be an issue with next.
Roseate Spoonbill
Don't forget to mark the topic as solved once you are happy with the response π It helps a lot in keeping the forum clean!
Dwarf CrocodileOP
I might have few more doubts, so keeping it open for a day or two. π
@Roseate Spoonbill So out of what I mentioned, I'd probably do one of the following:
- create `(dashboard)/dashboard/(admin-only)/` directory and put a new layout inside. On the top of the layout, you can authenticate the user and perform something like `if (!user.isAdmin) return notFound()`. You can also do it in your current structure, by putting this check in your admin-only pages.
- use [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) to have all auth checks done in one place - there's bunch of examples for this, e.g. https://medium.com/@turingvang/nextjs-middleware-auth-56a2da4ea341
Layout/page level auth is more page-oriented. It will also make it easier to run only for pages that need actual auth.
Middleware method is more if you need fine-tuned response codes, or simply want to have all declared in one place. Downside is that middleware will run on all routes (including internal next routes and api handlers), so you need to be extra careful what is middleware configured to run with.
Dwarf CrocodileOP
hey, in 1st approach, if someone with
client
role manually tries to access admin-only pages URL, will he see a glimpse of that page?cauz we performing checks on client-side?
Roseate Spoonbill
You should not make those checks client side - make sure the layout, or the page that preforms it are server-side. They wouldn't see a glimps of a page, but it's still a security risk to do any authentication on client. So if you need client side in those pages, wrap them in dedicated parent components and render them in the laout/page that runs on server.
Next will put client-side code in there only if you ask it to do so. All pages by default are server-side unless you tell them to be client side.
Dwarf CrocodileOP
can i perform those checks in
layout.jsx
or page.jsx
any one? (if both are server-side)or do i need to perform checks in subsequent sub-folders aswell?
Roseate Spoonbill
@Dwarf Crocodile It's not determined by file structure. For all I know all those pages may be client side or server side. As I said - pages and layouts are by default server-side. Only when you write
use client
at the top, your DOM tree will have client-side code from that point downward (it's a bit oversimplification from my side, but that's mostly the case anyway). If you have async
put in front of your component/page/layout then it is almost certainly server side (at least in current versions of Next). You can ensure that by using server-only
module. Once imported e.g. in the auth file, or on the page itself, it will throw an error if you try to import it on client: https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#keeping-server-only-code-out-of-the-client-environmentDwarf CrocodileOP
This is my ProtectedRoute.jsx component: https://codefile.io/f/QUM3gUI6mC
And i was thinking to do this in my admin's layout.tsx:
And i was thinking to do this in my admin's layout.tsx:
import "../../globals.css";
import Sidebar from "../../../components/dashbaord/sidebar";
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });
export default function DashboardLayout({ children }) {
return (
<html lang="en" className={inter.className}>
<body>
<ProtectedRoute allowedRoles={["admin"]}>
<div className="flex flex-row">
<Sidebar />
<div className="flex-grow">{children}</div>
</div>
</ProtectedRoute>
</body>
</html>
);
}
Roseate Spoonbill
Add
import "server-only"
at the top, and you'll be 100% sure that this is the server-side code πDwarf CrocodileOP
by default aren;'t they server-side?
Roseate Spoonbill
Yes, they are. But if you make a mistake and try to render them in client-component later on, this will ensure to throw an error at you before you make that change go public.
So call that being extra safe π
@Dwarf Crocodile This is my ProtectedRoute.jsx component: https://codefile.io/f/QUM3gUI6mC
And i was thinking to do this in my admin's layout.tsx:
import "../../globals.css";
import Sidebar from "../../../components/dashbaord/sidebar";
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });
export default function DashboardLayout({ children }) {
return (
<html lang="en" className={inter.className}>
<body>
<ProtectedRoute allowedRoles={["admin"]}>
<div className="flex flex-row">
<Sidebar />
<div className="flex-grow">{children}</div>
</div>
</ProtectedRoute>
</body>
</html>
);
}
Dwarf CrocodileOP
so i only need to perform this check in my
admin
folder's layout.jsx? and not in sub-folder of admin
? Also, all of the pages.jsx inside those sub-folders are using some useEffect and stuff, so i had to put "use client" in each pf them.Roseate Spoonbill
Yes. The server-side code will check auth etc. and once it passes, only then it will return client components to the user.
Dwarf CrocodileOP
if someone does:
/admin/change-password/
directly, still it will perform that check and throw an error right? cauz I am performing it in layout.jsx and all the rest pages are children of this layout.jsxRoseate Spoonbill
Yes. Layout runs first, before any of the pages inside.
@Roseate Spoonbill Yes. The server-side code will check auth etc. and once it passes, only then it will return client components to the user.
Dwarf CrocodileOP
also, i am storing role and jwt-token in localStorage, how can server read those local values?
and to use middleware i think I need to put those in a http cookie instead of savign them in local?
Roseate Spoonbill
Well, it cannot. You must find other ways to pass that info to server. Cookie is probably the easiest.
@Roseate Spoonbill Yes. The server-side code will check auth etc. and once it passes, only then it will return client components to the user.
Dwarf CrocodileOP
hey. my layout.jsx is server side.
But the imported component
Is it okay?
But the imported component
ProtectedRoute.jsx
is client-side.Is it okay?
Roseate Spoonbill
You do need server-side check as well. You cannot rely on client-side checks only. I'd put auth info in cookies and check those. Even if the cookie simply carries the JWT token.
Think of it that way: client-side auth code are helpers at best - show error messages when you are logged out, redirect after token invalidates and so on. Server-side code must do the check if you want to be safe.
Otherwise you risk exposing stuff.
Do not trust that having your auth check in your Layout makes your pages protected.
Layout.tsx isnβt a good place to do auth checks
Layout.tsx isnβt a good place to do auth checks
Dwarf CrocodileOP
:/
Roseate Spoonbill
Yeah, just wanted to write this - I just found some info about layouts being leaky when sent specific header. I cannot find any details or reports on next repo, so you may need to switch to Page-level check or to middlewares.
The docs say itβs not a Guarantee that Layouts are rendered before the page is rendered, yes theyβre nested visually when they reach the client but itβs not a guarantee when theyβre rendering
Also, Layouts maintain state and donβt re-render on navigations so if you make the check itβll only be made once for the first page rendering inside the layout
Also, Layouts maintain state and donβt re-render on navigations so if you make the check itβll only be made once for the first page rendering inside the layout
Make checks on the page level by abstracting an utility funcion and using it on every protected page at the very top of the page component (have in mind this will turn your page Dynamic if it wasnβt already, since youβre accessing dynamic APIs like cookies and headers), and for static pages the only way is doing it through middleware.
@luis_llanes The docs say itβs not a Guarantee that Layouts are rendered before the page is rendered, yes theyβre nested visually when they reach the client but itβs not a guarantee when theyβre rendering
Also, Layouts maintain state and donβt re-render on navigations so if you make the check itβll only be made once for the first page rendering inside the layout
Dwarf CrocodileOP
but If the 1st check was passed (meaning he was with an admin role), then its good right? he can then access other admin-pages as well
Roseate Spoonbill
Definitely do not rely on client-side checks only - if your
ProtectedRoute
is the only place you verify the session, it's not enough.Dwarf CrocodileOP
i am not using cookies right now. is there any way i can do server-side checks without storing my roles and jwt token to cookies?
Roseate Spoonbill
Cookies are the easiest way TBH. Simply set a http-only cookie upon login.
@Dwarf Crocodile but If the 1st check was passed (meaning he was with an admin role), then its good right? he can then access other admin-pages as well
Yes but if the session finishes either because it was timed out or you deleted cookies manually youβll still have access to it
Dwarf CrocodileOP
Also, inside my flask backend APIs, i am doing a role-check, if its admin, only then he can get details of clients, employees. otherwise i am returning a 403 unauthorized message
Either way doing auth checks in Layouts is not recommended, even the Next.js team discourages that practice
@Roseate Spoonbill Cookies are the easiest way TBH. Simply set a http-only cookie upon login.
Dwarf CrocodileOP
hmm..i will look in to. i was currently setting them in localstorage upon login
@Dwarf Crocodile Also, inside my flask backend APIs, i am doing a role-check, if its admin, only then he can get details of clients, employees. otherwise i am returning a 403 unauthorized message
Dwarf CrocodileOP
this is a server-side check? right? but just for the data and not the entire route
Roseate Spoonbill
You can do this if your data sources are always through API and by sending the token. But if next needs to be able to verify this, it must get this in the request - hence the cookies.
@Roseate Spoonbill You can do this if your data sources are always through API and by sending the token. But if next needs to be able to verify this, it must get this in the request - hence the cookies.
Dwarf CrocodileOP
yes, i am sending token in headers for all the admin APIs
and cheking if role matches
Roseate Spoonbill
Ok, let me sum it up for you as this is a bit long for how the context of the conversation grows:
- Out of the auth options I presented, the layout one is not good, so disregard this and forget I ever mentioned it, as I learned today. I'm sorry for mentioning this without proper check - that's my bad.
- Put auth checks on Page level, but only if page is server-side.
- Middleware checks are fine too if you start using cookies
- You can skip the auth check if you only get protected data through external API - in this case you still need to handle auth errors, but client-side is fine for this.
- Server-side checks require you to use cookies as
- Treat your
- Out of the auth options I presented, the layout one is not good, so disregard this and forget I ever mentioned it, as I learned today. I'm sorry for mentioning this without proper check - that's my bad.
- Put auth checks on Page level, but only if page is server-side.
- Middleware checks are fine too if you start using cookies
- You can skip the auth check if you only get protected data through external API - in this case you still need to handle auth errors, but client-side is fine for this.
- Server-side checks require you to use cookies as
localStorage
is not available to server- Treat your
ProtectedRoute
code as UX feature - anyone can set localStorage
to anything, so without server-check it's only something to put user in the right place, not something that will protect your data.Dwarf CrocodileOP
okay, cool π
I just changed my role from client to admin. So I was able to see the admin dashboard UI. But since my jwt token still has client role in it (when it was signed on server at the time of login), I am unable to perform anything and see anything.
i am considering this not-bad. But this can be improved by using cookies and middlewares. Then users wont be able to access those unauthorized routes.
Roseate Spoonbill
Sounds like you got it right.