I wanted to create a public web interface for my semi-public journal. I want to do server side rendering so that new content can be shown without rebuilding the site. Inspired from Sam Selikoff’s video From Gatsby to Next.js: The Power of Incremental Static Regeneration.
I chose Nuxt because:
- I want to compile Markdown content into HTML-based component like VuePress to be able to Use Vue in Markdown.
- I want to try it.
Setting up Nuxt with SSR and API on Vercel: Vercel has a Nuxt template but it is SPA mode, not SSR (server-side rendering). Setting up SSR is pretty straightforward, but when it comes to deployment I hit a few roadblocks. I assumed this would take 10 minutes but I ended up spent over an hour trying to figure this out.
As a result I sent the PR #373 to improve the docs (outdated):
Original PR #373 contents (outdated)
A common use case in Nuxt is to use
serverMiddleware
put an API server on the same server as Nuxt. This requires some set up:
Set up
serverMiddleware
config innuxt.config.js
:serverMiddleware: [ { path: '/api', handler: '~/api/index.js' }, ],
Note: This assumes that your API will be served at
/api
and its source code is inapi/index.js
(same as Nuxt’s documentation).Go to your Project Settings → Environment Variables and add an environment variable
VERCEL_URL
to all environments. When you type in the name, Vercel will say that it is a system environment variable and its value will be automatically populated by the system.Set up
serverFiles
innow.json
:{ "version": 2, "builds": [ { "src": "nuxt.config.js", "use": "@nuxtjs/vercel-builder", "config": { "serverFiles": ["api/**"] } } ] }
Set up
@nuxtjs/axios
to use the base URL from Vercel.const baseUrl = process.env.baseUrl || (process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : undefined) export default { // ... modules: [['@nuxtjs/axios', withoutNullishEntries({ baseURL: baseUrl })]], env: withoutNullishEntries({ baseUrl, }), } function withoutNullishEntries(x) { return Object.fromEntries(Object.entries(x).filter(([k, v]) => v != null)) }
I later spent some more time fiddling with the builder and submitted PR #375 which makes this use case "just work" on Vercel. It has been merged.
Authentication: Setting up Auth0 + Nuxt was easy. By following the documentation, I got to the point where I can display the user’s info in Nuxt webpage. However the docs does not say anything about what to do afterwards. Same goes for all the tutorials I looked at.
You can now log in and log out! You can see your own name!! Congratulations!!! Now your app has a authentication system!!!!
What now? How do I protect my API endpoints to allow only authenticated users??
After one more hour of trial-and-error, I figured it out: I need to set up 2 separate applications in Auth0.
Set up a web application and put the Client ID in
@nuxtjs/auth
config.Set up an API (machine-to-machine) and put the Identifier as
audience
in@nuxtjs/auth
config. Use RS256.The Auth module will automatically configure Axios to send the
Authorization:
header with a proper JWT.In API, use
express-jwt
andjwks-rsa
to authenticate the incoming request. Thankfully, Auth0’s API dashboard has a Quick Start tab with code you can copy and use with Express app right away.
Handling invalid and expired JWTs in express-jwt
: Normally, if JWT is not provided the middleware throws an error. This can be fixed by adding credentialsRequired: false
in express-jwt
options.
However, soon I start getting 500 errors due to expired JWT. There are no settings to prevent this (issue) but a workaround is to put an “on error resume next” handler for invalid_token
case:
app.use(jwtCheck, function (err, req, res, next) {
if (err.code === 'invalid_token') return next()
return next(err)
})
After this, use req.user
to check for presence of a valid JWT token.