React.js is a great front-end frameworks and I have been combining it a lot with .NET API written in C#.
It is one of my favourite duos.
So far we have used react to build internal application such as dashboards, admin panels etc. for which things like SEO don't matter at all.
However for anything publicly available, SEO is important specially if you are building a SaaS and you want it to be findable by Google and others.
I started working on a small project which required server side rendering and so Next.js had to step in.
The challenge was to set it all up with Azure Pipelines (aka Github Actions aka Team City) and deploy it on a Linux hosted with Droplets.
I got very confused about the process of building and deployment because whenever I reached out to the Next.js community I was told to run next build
on my production server which I found a bit crazy.
Additionally nobody seem to know the answer: https://stackoverflow.com/questions/76121837/how-can-i-run-next-js-with-self-hosted-node-js
Why I found it crazy? Well it is a first time I was told to build the project on the production server. This is not how it is supposed to be done. The way it is supposed to be done is that you commit the code to your repo, pipelines pulls it and builds the whole thing for you and all it has to be done is the transfer of the production build to the production server. Why on Earth I would build things on the production server?
After some research I discovered something called standalone output which also had its issues:
https://stackoverflow.com/questions/76294578/cannot-find-module-next-server-font-manifest-json
Finally I managed to figure this out and so I'm sharing it here for those who are lost as I was once:
- Set output to standalone
const nextConfig = {
reactStrictMode: true,
output: 'standalone'
}
module.exports = nextConfig
- Here is the pipeline **build **setup. I will use a sudo code to give an idea of what needs to be done. I add the complete file at the end of the this post.
# SM.Web is my root of the web project
yarn install
# overwrite your .env with environment specific stuff
echo "NEXT_PUBLIC_API_BASE_URL=https://api.yourawesomeapp.com" >> src/SM.Web/.env
yarn build
cp -R src/SM.Web/public "src/SM.Web/.next/standalone/public"
cp -R src/SM.Web/.next/static" src/SM.Web/.next/standalone/.next/static"
zip src/SM.Web/.next/standalone SM.Web.zip
# publish SM.Web.zip s artefact
Here is **deployment **pipeline:
- Download the artefact on to your Linux box using SSH key connection. I won't go here into a details.
- Replace current application with the artefact content
rm -rf /var/apps/sm/staging-app/*
unzip -o /var/apps/sm/staging-app-build/SM.Web.zip -d /var/apps/sm/staging-app
rm /var/apps/sm/staging-app-build/SM.Web.zip
# for this command to work you need to setup `sm-staging` first
# go to /var/apps/sm/staging-web/
# and run: pm2 start "node server.js" --name "sm-staging"
pm2 reload sm-staging
Here is the complete azure-pipelines.yaml
:
trigger:
- main
pool:
vmImage: ubuntu-latest
variables:
buildConfiguration: "Release"
steps:
- task: CmdLine@2
displayName: "[app] yarn install staging"
inputs:
script: "yarn install"
workingDirectory: "src/SM.Web/"
- task: Bash@3
displayName: "[app] add build version"
inputs:
targetType: "inline"
script: |
touch src/SM.Web/.env
echo "NEXT_PUBLIC_API_BASE_URL=https://api.yourawesomeapp.com" >> src/SM.Web/.env
cat src/SM.Web/.env
- task: CmdLine@2
displayName: "[app-staging] yarn build"
inputs:
script: "yarn build"
workingDirectory: "src/SM.Web/"
- task: CopyFiles@2
displayName: "[app-staging] copy public"
inputs:
SourceFolder: "src/SM.Web/public"
Contents: '**'
TargetFolder: "src/SM.Web/.next/standalone/public"
- task: CopyFiles@2
displayName: "[app-staging] copy static"
inputs:
SourceFolder: "src/SM.Web/.next/static"
Contents: '**'
TargetFolder: "src/SM.Web/.next/standalone/.next/static"
- task: ArchiveFiles@2
displayName: "[app-staging] create artifact"
inputs:
rootFolderOrFile: "src/SM.Web/.next/standalone"
includeRootFolder: false
archiveType: "zip"
archiveFile: "$(Build.ArtifactStagingDirectory)/sm-web/SM.Web.zip"
- task: UseDotNet@2
displayName: "[api] use dotnet 7"
inputs:
packageType: "sdk"
version: "7.0.x"
performMultiLevelLookup: true
- task: DotNetCoreCLI@2
displayName: "[api] restore"
inputs:
command: "restore"
projects: "src/SM.Api/SM.Api.csproj"
feedsToUse: "select"
- task: DotNetCoreCLI@2
displayName: "[api] build"
inputs:
command: "build"
projects: "src/SM.Api/SM.Api.csproj"
arguments: "--configuration $(BuildConfiguration)"
- task: DotNetCoreCLI@2
displayName: "[api] publish"
inputs:
command: "publish"
publishWebProjects: true
arguments: "-c $(buildConfiguration) -o $(Build.ArtifactStagingDirectory)/sm-api"
- task: PublishPipelineArtifact@1
displayName: "publish artifact"
inputs:
targetPath: "$(Build.ArtifactStagingDirectory)"
artifactName: "sm"
What does your Linux box need?
🚀 Good luck! 🚀
Top comments (0)