Product
My journey building a GPT-powered assistant using Next, GPT and Pocketbase [Part 2]
6 min
Ica, PerĂş
Next.js App best practices
Disclaimer: This intends to be a series of posts describing my journey through this process. This is an academic focus, and all of the code is available on GitHub. This doesn't aim to be a step-by-step tutorial. Most of the resources used to build will be shared here.
You can visit the current version here: https://notes.juanmurillo.co/en
In my previous article (Motivation), I implemented an ugly POC (Proof of Concept), and in the end, I listed a couple of things I would like to tackle. I wanted to build something using my previous experience; the focus of the current article would be adding the “best practices” in there.
The more I code, the more I get familiar with the Next framework and OpenAI. I’ve looked through the docs and it’s quite impressive and smooth the way you are able to add best practices to your project. This week I focused on adding these key features to my app. You might wonder, why are these considered best practices. Let me get started.
What are “best practices”?
According to Oxford’s Dictionary, a best practice is:
Commercial or professional procedures that are accepted or prescribed as being correct or most effective.
The “best practices” definition could be ambiguous in the software industry and it could be a little bit relative but for the sake of this side project, let’s scope that into tests, routing, internationalization, analytics, performance metrics, CI/CD, environments (dev, staging, production), among others. These best practices help us keep high quality in our digital products or services. There might be other best practices such as standardizing components, internal processes (communication between product and engineering, communication between designers and developers, etc.), and managing logs, but these will be addressed later.
Why are they considered best practices?
In my experience, there are different reasons for considering these practices as the “best”. This usually depends on the type of product/service but so far I will mention which I consider critical for my current scenario one by one:
- Tests: Mitigate errors in production, help developers identify edge cases, and tackle them before reaching real customers. Also, they allow us to mitigate regressions while implementing new changes or feature flags to avoid breaking existing behavior.
- Routing: An MVP could be easily one page. But by the time you start adding more features, you will need different pages/views to allow customers to visualize those features. In addition, most of these routes will be dynamic and created based on customer data.
- Internationalization (i18n): You might want to reach a single-language audience, but even if your target is LATAM, Brazil is a huge market that you should consider. On the other side, if your goal is North America. There are plenty of Spanish speakers in there. Also, internationalization is not limited only to the language, think about the currency. If you sell a product or service, pricing should consider the currency as well (đź’µ, đź’¶, đź’´).
- Analytics: You can’t improve what you can’t measure. How do you know if your copy changes or new features are being adopted? Analytics are crucial to understanding your user’s flow and their journey using your products.
- Performance metrics: Same here. You can’t improve what you can’t measure. It’s easy to use your product on your laptop, but think about how many people use it from different devices, limited networks (3G), etc.
- CI/CD: You should know if a new pull request is breaking an existing behavior, and otherwise if it’s working as expected, deliver it as soon as possible to your customers. (There’s also something called progressive rollout which is something we should consider. It basically makes releases gradually so you can mitigate possible errors).
- Environments: We don’t want to mess around with customers’ data. Also, there could be PII or sensitive data that must be handled accordingly.
How to implement them?
- Tests: There’s something called the testing pyramid. https://learn.cypress.io/testing-foundations/the-testing-pyramid. Jest and React Testing Library for unit tests, Cypress/Playwright for E2E tests. https://nextjs.org/docs/pages/building-your-application/optimizing/testing
- Routing: For our particular case it’s built-in using Next. https://nextjs.org/docs/pages/building-your-application/routing
- Internationalization (i18n): It looks like there are BE-side i18n (Internationalization Routing) and FE-side i18n. There are many popular libraries to handle like react-intl, react-i18next, lingui, rosetta, next-intl, next-translate, next-multilingual, typesafe-i18n, tolgee. https://nextjs.org/docs/pages/building-your-application/routing/internationalization. I haven’t figured out how to combine both yet.
- Analytics: For our particular case, it’s built-in with Next. Google Analytics is also an option. https://vercel.com/docs/analytics
- Performance metrics: For our particular case, it’s built-in with Next. https://vercel.com/docs/speed-insights
- CI/CD: GitHub Actions. This also depends on where you are hosting your code. For instance, other alternatives might be Bitbucket pipelines.
- Environments: Vercel manages different environments. https://vercel.com/docs/deployments/environments
Conclusion
In conclusion, we should not limit best practices are the ones defined here. Also, best practices are not only limited to technology, there could be process-related best practices. In this particular context, we implemented best practices from a technical perspective and we took advantage of the Next.js framework because most of them were built-in. However, it's important to mention that there are other alternatives for each of the best practices mentioned here. It's just that Next, as a framework, makes our job easier.
Please, feel free to let me know about other practices that you consider the best and I should have mention here.
Work done in the previous week
- Update cache policy in assistant's resources
- Update styles. Tailwind rocks!
- Add analytics
- Add prettier
- Add linter
- Add husky
- Add lint-staged
- Setup jest
- Add spinners
- Add i18n and locale switcher component
- Add redirect for default locale
- Add dark mode
Lessons
- Debugging next auth on production. https://next-auth.js.org/configuration/options#debug
- The cache policy is strong on the Next app. Pocketbase SDK it’s not obvious how to manage caching. However, there’s a workaround: https://github.com/pocketbase/pocketbase/discussions/2695:
https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating
https://nextjs.org/docs/app/building-your-application/caching - Vercel AI API is really good including the `initialMessages` property, I was able to remove some unnecessary logic. https://github.com/jgmurillo10/notesapp/commit/b3337291b60af4c9b85706f7eebfc7a6fd668b03
- GPT function calling lets you describe functions and have the model intelligently choose to output a JSON object containing arguments to call those functions. Functions could execute your own logic, or use third-party libraries. For example, get the current temperature of a given location. https://openai.com/blog/function-calling-and-other-api-updates
- How to manage providers in Next using the App directory.
- Vercel has built-in analytics and performance.
- Vercel provides a way to measure the real performance of your app on your client’s computers. Not your fancy 🍎 computer with more RAM than needed to run a simple App.
- The dark mode is easy to implement with Tailwind.
Resources
- Implement dark mode in the Next 13 App directory: https://www.linkedin.com/pulse/implement-dark-mode-tailwindcss-nextjs13-app-5-simple-lucas-los-arcos/
- Suppress hydration: https://legacy.reactjs.org/docs/dom-elements.html#suppresshydrationwarning
- Prettier VS TSLint/ESLint: https://prettier.io/docs/en/comparison
- GPT | Function calling: https://openai.com/blog/function-calling-and-other-api-updates
- GPT | Fine-tuning updates: https://openai.com/blog/gpt-3-5-turbo-fine-tuning-and-api-updates
- Web Analytics: https://vercel.com/docs/analytics
- Speed Insights: https://vercel.com/docs/speed-insights
- GitHub actions: https://github.com/features/actions
- Next cache:
https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating
https://nextjs.org/docs/app/building-your-application/caching - E2E or Component tests using Cypress: https://docs.cypress.io/guides/core-concepts/testing-types
- TailwindUI: https://tailwindui.com/
Things that keep me humble
- Finding out that auth errors were due to missing ENV variables on production.
- Added support for jest but there’s only one dummy test written in the whole app.
Other best practices worth mentioning
(I might write about them in the short term)
- Optimize Core Web Vitals: https://web.dev/i18n/es/vitals/
- Optimize Bundle Size
- Feature flags managing
- Use Dependabot to Update Packages: https://github.com/dependabot
- Storybook: https://storybook.js.org/
- Depcheck: https://www.npmjs.com/package/depcheck
- Mono-repo structure where your component library resides with your Next.js application: https://blog.logrocket.com/build-monorepo-next-js/
- Maintain large NextJS App blogpost: https://www.smashingmagazine.com/2021/11/maintain-large-nextjs-application/