Context: I am migrating from Nuxt to Quasar framework, and initially I want to port my full-stack apps from Nuxt to Quasar SSR mode. When using Nuxt, apps are full-stack by default, we just need to place backend related files in the server-directory (see https://nuxt.com/docs/guide/directory-structure/server#server-directory).
Question: I have already created my first Quasar application and added the SSR mode, but I could not find anything similar to Nuxt server-directory :-( So the question is: Does Quasar offer something similar to Nuxt server-directory? How to generate a Quasar SSR full-stack application?
I don't think Quasar offers something similar to server-directory out-of-the-box, but Quasar is a quite flexible framework, and we can easily achieve "SSR full-stack capabilities" by using a Quasar SSR-middleware :-)
SSR-middleware is an additional layer where all browser-requests are supposed to come through. Therefore, all we need to do is to add an additional SSR-middleware to intercept browser-requests when trying to hit a specific route (in this case, /api/*
). Step-by-step:
Disclaimer: it works only when running Quasar in SSR mode, of course.
npm init quasar && cd my-app && quasar mode add ssr
), let's add an additional SSR-middleware "api" with the command:quasar new ssrmiddleware api
quasar.config.js
:ssr: {
...
middlewares: [
'api', // <= this is our SSR-middleware (keep this as first one)
'render' // keep this as last one
]
},
...
src-ssr/middlewares/api.ts
:export default ssrMiddleware(async ({ app, resolve }) => {
app.all(resolve.urlPath('*'),(req, res, next) => {
if (req.url.substring(0, 4) === '/api') {
// Here we can implement our backend NodeJS/Express related operations.
// See the example below "Real-life example", which provides
// something similar to Next/Nuxt server-directory functionality.
res.status(200).send(`Hi! req.method: ${req.method}, req.url: ${req.url}`);
} else {
next();
}
});
});
Now we can start our Quasar SSR application, and see our API in action by pointing the browser to http://localhost:9100/api/foo/bar/ Response => Hi! req.method: GET, req.url: /api/foo/bar/
updated according to @David's suggestion. Thanks :-)
src-ssr/middlewares/api.ts
. The example below maps all available API-Handler (see apiHandlers object) and select them to handle browser requests according to the request URL:import { ssrMiddleware } from 'quasar/wrappers';
const apiHandlers: { [key: string]: any } = {
'req-info': import('../../src/api/req-info'),
version: import('../../src/api/version'),
};
export default ssrMiddleware(async ({ app, resolve }) => {
app.all(resolve.urlPath('*'), async(req, res, next) => {
if (req.url.substring(0, 4) === '/api') {
try {
const path = req.url.split('?')[0].substring(5);
const apiHandler = await apiHandlers[path];
if (apiHandler) {
await apiHandler.default(req, res);
} else {
res.sendStatus(404);
}
} catch (error) {
console.error(error);
res.sendStatus(500);
}
} else {
next();
}
});
});
/src/api/
./src/api/version.ts
:import type { Request, Response } from 'express';
export default function (req: Request, res: Response) {
const version = require('../../package.json').version;
res.status(200).send(version);
}
/src/api/req-info.ts
:import type { Request, Response } from 'express';
export default function (req: Request, res: Response) {
res.setHeader('Content-Type', 'text/html');
res.status(200).send(`
<ul>
<li>req.url: ${req.url}</li>
<li>req.method: ${req.method}</li>
<li>req.query: ${JSON.stringify(req.query)}</li>
</ul>`);
}
I hope it helps, thanks! If so, please vote-up :-)
StackBlitz project: https://stackblitz.com/edit/quasarframework-uocha6