Fly.io is a platform that offers a unique way to deploy and manage web applications, and Next.js is a popular framework for building server-side rendered React applications.
Fly.io provides a managed platform for deploying Next.js applications, allowing developers to focus on writing code rather than managing infrastructure.
Fly.io's platform is designed to provide fast and secure web applications, with features such as automatic SSL encryption and built-in support for WebSockets.
Developers can deploy Next.js applications to Fly.io with ease, using the platform's simple and intuitive interface.
Project Setup
To set up a project on Fly.io with Next.js, you'll need to create a new Next.js project in your terminal.
Navigate to your workspace folder and run the command to create a new Next.js project.
Then, install the necessary dependencies, which will include those for Next-auth, since this blog uses Google for sign-in/login authentication.
Create an auth.jsx file in the root directory and enter the code provided.
Fly.io supports rate limiting to help prevent abuse, allowing 10 requests per minute.
Comment System
The comment system on your Fly.io Next.js app is a crucial feature for engaging with your users. We develop a comment system using Arcjet and the Arcjet/Auth.js integration.
We create an API route for handling comments on blog posts, including creating new comments and fetching existing comments. This is done in the app/api/comment directory, where we create a route.jsx file and enter the code.
The code configures Arcjet to implement rate limiting using a token bucket algorithm. This is a live mode that blocks requests if limits are exceeded. We also have a function that deducts tokens from the token bucket to enforce rate limiting.
If the request is denied due to rate limits, it returns a 429 Too Many Requests response. But if the user is authenticated and the rate limit is not exceeded, a new comment is created in the database using Prisma.
Using the useEffect hook, comments associated with the current blogid are fetched, updating the comment state with fetched data while handling potential errors. This demo showcases the comment system's limit feature using Arcjet, preventing spamming by limiting the number of comments a user can enter at a time.
Newsletter Signups
When working with Fly.io and Next.js, managing newsletter signups is a breeze. Fly.io will provide a DATABASE_URL to connect to the database at runtime.
This DATABASE_URL is used through the prisma/schema.prisma file in the project's root directory. The newsletter signups are stored using a Next.js database with Prisma.
Fly.io takes care of setting up the database connection, making it easy to focus on building your application.
Deploying to Fly.io
To deploy your Next.js app to Fly.io, you'll need to install the Fly CLI. This command-line utility lets you interact with Fly.io and deploy your application, so install the version for your operating system.
Using Windows, run the PowerShell install script, and make sure you have the latest version of PowerShell installed.
Log in to your Fly account and add a payment method. You can also create an account using the command fly auth signup.
To deploy your application, run the command fly launch in the project root directory. This will redirect you to the browser to confirm the launch settings.
Your secure Next.js blog is now deployed on Fly.io with Arcjet protection!
Static vs Dynamic
Separating static and dynamic content is crucial for performance and scalability. I implemented this setup to separate static pages like the home page, privacy policy, and dynamic content from the dynamic author dashboard.
Static pages can be reliably cached and don't require server resources, making them ideal for pages like the home page and privacy policy. Every small change to static pages doesn't pollute the main application repo and doesn't need a server restart.
Statically generating static pages with NextJS can provide the speed of static sites, while still serving dynamic pages from the Phoenix app. This setup is particularly useful for SEO reasons, such as serving a blog as a subdirectory.
Standalone Builds (Recommended)
Standalone builds can significantly reduce your total image size by ~400mb, making them a highly recommended approach.
To take advantage of standalone builds, you can either use the "standalone" value in your Next config when running fly launch or using the Dockerfile generator. This will automatically update your Dockerfile to include only the files necessary for a production environment.
Alternatively, you can manually update your Dockerfile by replacing the final COPY instruction with a new one. After running the build script (npm run build), you should see a final COPY instruction towards the end of your Dockerfile.
Updating this instruction is crucial to achieving the benefits of standalone builds.
Lastly, update your CMD to the following:
Static Site Generation
Static site generation is a game-changer for websites that require both speed and interactivity. It allows you to separate static pages like home pages, privacy policies, and dynamic content, which can be served from a NextJS app that can be statically generated.
This approach can be particularly useful if you want to add a blog as a subdirectory for SEO reasons, as it can help reduce server resources and improve caching. By moving static pages to a NextJS app, you can get the speed of static sites and the interactivity of Phoenix LiveView.
However, you may need to adjust your Dockerfile and deployment setup to accommodate static site generation. For example, you may need to replace the entry point in your Dockerfile with a script that runs npm build prior to starting your server.
Here are some things to consider when setting up static site generation:
- Your deployment Machines will need enough memory to run a build.
- You may need to adjust the grace period for any HTTP service checks.
- If you are only running one Machine, there will be a period of time where your server is inaccessible while the site is being statically generated.
- If you run multiple Machines, the static site generation will be run on each, increasing the total time before any changes are fully deployed.
Domain Setup
To set up your domain, you'll want to start by pointing your domain nameservers to Vercel. This ensures all requests pass directly to Vercel, improving performance.
First, you'll need to deploy your NextJS app to the root domain on Vercel. Use the root domain https://yourdomain.com for deployment, and make sure to redirect www.yourdomain.com to it.
You'll also need to deploy your Phoenix LiveView app to a subdomain on Fly.io. Use fly.io and deploy the LiveView app to subdomain `app.yourdomain.com`. This will involve pointing the subdomain to the fly.io IP address and generating a certificate.
Here's a summary of the steps:
- Point domain nameservers to Vercel.
- Deploy NextJS app to root domain on Vercel.
- Deploy Phoenix LiveView to app subdomain on Fly.io.
Make sure to set the PHX_HOST variable as the root domain https://yourdomain.com when deploying the LiveView app.
Environment and Security
As a Next.js user, you might know that it supports exposing environment variables to the browser using variables with names starting with NEXT_PUBLIC_. These variables are fixed at build time.
In order to make these NEXT_PUBLIC_ variables available, you can add them as ARG variables in your Dockerfile, which should come after any actual build steps.
You can also use the Dockerfile generator to achieve this, but you'll need to run a specific command to do so.
If you've used the Dockerfile generator or the Dockerfile created from fly launch, you'll need to add the NEXT_PUBLIC_ variables after the line FROM base as build.
By following these steps, you can make your NEXT_PUBLIC_ variables available to the browser, allowing you to store sensitive information securely.
Rendering Content
Rendering content with Fly.io and Next.js is a breeze. You can create a new page at app/post/[id]/page.tsx to display individual blog posts.
This page is a dynamic route that includes an id URL parameter, which you'll use to fetch the corresponding post from the database.
To fetch the post, you'll use a Server Component, specifically the filter_single method, to filter the BlogPost type by its id.
You can deploy an EdgeDB instance on the EdgeDB Cloud or your preferred cloud provider, which will be covered in the tutorial.
The id parameter is converted to a UUID using the uuid function from the query builder, making it easy to retrieve the correct post.
By using a Server Component, you'll be able to fetch the post from the database efficiently, making your application more scalable and performant.
Featured Images: pexels.com