Documented Full-Stack Sample Web App Using My Favorite Tools
NotesApp is a project to document my current favorite stack. It is kind of the full-stack equivalent of TodoMVC, but with support for having different pages, a database, CRUDs, server-side rendering, and authentication. It's basically a minimal website with a full-featured architecture to serve as a reference and boilerplate for me when starting a new project.
The app itself is very simple. There is a landing page to sign up and log in, an about page, you can write basic notes for yourself, and single notes can also be accessible by unauthenticated users if they have its URL (kind of like "unlisted" videos on YouTube).
Every file of the project is mirrored into a documentation in the docs
folder. For instance, for src/pages/_app.tsx
, there is a docs/src/pages/_app.tsx.md
to comment on specific concepts without bloating the source code with comments.
Here is a diagram of the stack and tools used:
In the following section I explain why I chose those specific tools instead of others.
I program in JavaScript over other languages, because I value isomorphic code (using the same code on the client and the server), being able to implement server-side rendering more easily, and programming in only one language in general. Considering that JavaScript is pretty much mandatory for client-side code, there is not much of a choice to make here.
In terms of flavor, I prefer TypeScipt over vanilla ES6, because static typing catches a lot of bugs before they happen. I find TypeScript more reliable than Flow, and it has a much bigger community and support.
I would recommend Python to someone learning the fundamentals of programming though.
I am very satisfied with using React as my front-end library. Its one-way data flow is very intuitive, the ecosystem is rich, server-side rendering is on point, and it is very widely adopted which means there is a lot of support available, as well as work opportunities.
I know Vue is great, but I haven't tried it yet, because it would involve replacing a significant portion of my stack if I were to replace React by it. I would recommend giving it a shot to people who are just getting started with front-end development, or need a lightweight solution though.
Angular might work for some people, but overall it has a very low developer satisfaction according to the State of JS 2019 (38%, versus 89% for React and 87% for Vue), so I don't think I am missing out much.
Server-Side Rendering (SSR) might be a hard requirement if your website needs to be accessible to programs. Those can be bots from Google for SEO, Facebook for its Open Graph, or Twitter for Cards for instance. If the only pages that need SSR are static pages, such as a landing page, an about page, or articles ("static SSR") you can use Gatsby, which has a rich plugins ecosystem, particularly for CMSes, and render all the pages at build-time. If you want to expose pages that are user-generated or dynamic in general (dynamic SSR), you cannot create those pages at build-time, you need a server to render them on the fly. That's where Next.js steps in. Next might not have all the plugins Gatsby has, but it can do both static SSR and dynamic SSR, which makes it a better (and only) choice for this kind of larger project. Just like other Vercel products, it is very elegantly conceived and is a delight to use.
Combining a hybrid static SPA with SSR and with authentication can be quite complicated. I made a spreadsheet to help figuring out what to do in those different scenarios.
Vercel are the creators of Next and other great projects and a deployment platform for Jamstack apps and serverless functions. Everything from them is honestly top-notch quality and well-crafted. As a deployment platform, the Github integration makes deploying websites a breeze, and their dashboard is stellar. For Next projects in particular, it is the platform of choice. For Jamstack projects, Netlify is also a good similar alternative. For serverless functions, Vercel supports JavaScript, TypeScript, Go, Python, and Ruby, whereas Netlify only supports JavaScript and Go.
Both Vercel and Netlify offer a ridiculously generous free tier.
Hasura combines an ORM for CRUD operations via GraphQL on a PostgreSQL database, a DB GUI, DB migrations, roles and permissions, and acts as the single entrypoint for all your API calls, with the ability to call remote GraphQL services under the hood. Its competitor is PostGraphile which I haven't tried yet.
I use Apollo Server hosted on a Vercel function for custom logic that cannot be handled by Hasura's CRUDs. The only consumer of this server is the Hasura server, which can seemlessly make calls to the Apollo Server and return results to the user. This server handles authentication and setting cookies as well.
Most simple operations will be done directly via Hasura's CRUDs, and Hasura also takes care of database migrations via auto-generated raw SQL. For specific custom-logic database calls, you might want an ORM or a query builder to help not write SQL by hand. For this I use Prisma, which can introspect the database schema and provide amazing TypeScript types specifically tailored to your data.
I also really like Knex, a simple query builder that can handle migrations too. If you need to modify data during migrations, it will be easier to do with Knex than with Hasura's raw SQL. I might swap out Hasura's migrations entirely in the future, for Knex's, or for Prisma's when it's out of its experimental state.
I tried TypeORM (the "TypeScript" ORM) multiple times, but the fact that some parts of it (such as migrations) cannot interpret TypeScript and rely on compiling your files into JavaScript causes some complicated hybrid TS/JS configuration. It also requires some tweaks with Babel to get decorators to work, which was not as simple as they make it seem. And the connection system has always been a source of headaches to me, particularly in a serverless environment. In comparison, everything worked immediately as expected with Prisma. It is night and day to me but your mileage may vary.
I am not considering Sequelize or Bookshelf because they do not support TypeScript natively.
I think for common use-cases, relational databases are better-suited than NoSQL. Unless you have specific needs, such as very high performance or schema flexibility, a relational database will ensure your data is more consistent than NoSQL databases. Now regarding what system to use, to be honest I am not well-versed enough in databases to argue in favor of PostgreSQL over MySQL or MariaDB. I'm just using Postgres because it's the default and only option Heroku offers, and I trust Heroku to make the right choice for me. Hasura also only supports Postgres currently.
JWTs make it possible to store session data on the client instead of the server (in a Redis for instance), avoiding one database round-trip to validate the identity of the user. They must be stored and transported securely though. The current consensus is to store them in an HttpOnly, Secure, SameSite cookie.
The JWT authentication mechanism of Hasura requires the JWT to be sent in the Authorization
header of requests, which is easy to do for server-side requests, but impossible to do for the client since the cookie is inaccessible via JavaScript. This is why I have a serverless endpoint to convert client requests containing a cookie into server requests containing the Authorization
header. It won't be necessary if Hasura supports reading JWTs from cookies.
I am a fan of passwordless authentication, particularly for bootstrapping projects to production quickly and getting users to sign up with no friction. Magic is very easy to use, it just opens a popup to tell the user to click on a link in the email that has been sent, and returns a token to confirm the authentication. It has a free tier but it's too expensive at scale. It is also a very recent project, so it could be unstable or disappear. I would use Auth0 to do the same thing, but they require the user to use the same browser to request the email and validate the email, which will fail for many users, particularly on mobile with email apps using a webview different than the user's regular browser. That's a big no-no to me. I also had bad experiences with Auth0 every time I tried using it, because I find it very complex. An alternative is to implement magic links yourself, which is not very complicated, or using social logins.
I am Jonathan Verrecchia, aka @verekia, a French freelance web developer who created JavaScript Stack From Scratch and Initializr, and ex-Yelp developer. I love improving my toolset and helping other developers. I am open to developer relations jobs or remote freelance work. My resume is available on my website.