Access privateRuntimeConfig in express server - vue.js

I've installed the express-nuxt template and I was wondering how could I get access to the privateRuntimeConfig inside nuxt.config.js from express (API folder). One approach I thought about was to put the vars inside a .env file and then installing the dotenv package for the express server, but I think that using just Nuxt could be better.

We have done precisely this by importing the Nuxt config into the file that configures the Express app, and using defu to combine public and private runtime configs, as Nuxt itself does:
// nuxt.config.js
export default {
publicRuntimeConfig: {},
privateRuntimeConfig: { redis: { url: process.env['REDIS_URL'] } }
};
// api/index.js
import express from 'express';
import defu from 'defu';
import { createClient as createRedisClient } from 'redis';
const app = express();
import nuxtConfig from '../nuxt.config.js';
let runtimeConfig;
app.use((req, res, next) => {
if (!runtimeConfig) {
// Load Nuxt config once, at runtime
runtimeConfig = defu(nuxtConfig.privateRuntimeConfig, nuxtConfig.publicRuntimeConfig);
}
next();
});
// Subsequent middlewares will then be able to read from `runtimeConfig`
app.use((req, res, next) => {
const redisClient = createRedisClient({ url: runtimeConfig.redis.url });
next();
});
export default app;

Related

using Nuxt 3 with Nest

Currently we are able to build nuxt as following. But are unable to handle routes. We basically want to serve nuxt app from Nestjs.
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module.js';
import { loadNuxt } from 'nuxt3';
import { buildNuxt, Resolver } from '#nuxt/kit';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Check if we need to run Nuxt in development mode
const isDev = process.env.NODE_ENV !== 'production'
// Get a ready to use Nuxt instance
const nuxt = await loadNuxt({ rootDir: 'src/client-app/' })
// Enable live build & reloading on dev
if (isDev) {
buildNuxt(nuxt)
}
await app.listen(3001);
}
bootstrap();
Following is next (react) equivalent code which is working and trying to achieve in Nuxt 3.
https://github.com/hnviradiya/contact-list/blob/e38a72167d5710fcc9f3ed9718fa9bfe8ebb7d00/src/server/client-app/client-app.service.ts#L25
import { Injectable } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { IncomingMessage, ServerResponse } from 'http';
import createServer, { NextServer } from 'next/dist/server/next';
#Injectable()
export class ClientAppService {
private nextServer: NextServer;
constructor(private configService: ConfigService) {}
async onModuleInit(): Promise<void> {
try {
this.nextServer = createServer({
dev: this.configService.get<string>('NODE_ENV') !== 'production',
dir: './src/client',
});
await this.nextServer.prepare();
} catch (error) {
console.error(error);
}
}
handler(req: IncomingMessage, res: ServerResponse) {
return this.nextServer.getRequestHandler()(req, res);
}
}
In nuxt 2 there were nuxt.render(req, res) or nuxt.renderRoute(route, context). But these methods are not available in nuxt3.
https://nuxtjs.org/docs/internals-glossary/nuxt/
So how to serve nuxt app through NestJs.
Following is the repo where nuxt 3 code is there but it is not serving nuxt app.
https://github.com/hnviradiya/nest-nuxt
while Nestjs is a great server, it's angular style #decorators and modular setup is too unlike Nuxt3's scaffold simplicity.
This conception feels like a bad idea.

TypeError: bodyParser.json is not a function in nuxt.js

I have received an error message stating: TypeError: bodyParser.json is not a function. My nuxt.config.js file has the following details regarding bodyparser (I originally had const bodyParser = require('body-parser') but an error appeared telling me that I had to use 'import' instead of 'require' so I changed it to 'import('body-parser'):
const bodyParser = import('body-parser')
export default {
serverMiddleware: [
bodyParser.json(),
'~/api'
]
}
In my index.js file under the api folder, I have the following code:
const express = require('express')
const bodyParser = require('body-parser')
const router = express.Router()
const app = express()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
})
router.post('/track-data', (req, res) => {
console.log('Stored data!', req.body.data)
res.status(200).json({ message: 'Success!' })
})
module.exports = {
path: '/api',
handler: router
}
Does anyone know how to get this to run? Everytime I enter 'npm run dev' in the terminal, I get the error 'TypeError: bodyParser.json is not a function'.
I had a slightly similar issue and could solve it with this code, first i imported express in the config file, before i did it like you, with bodyParser, but got a deprecated warning, then i use it in the api folder, in the index file, like so:
// nuxt.config.js
import express from 'express';
export default {
ssr: true,
.......,
.......,
serverMiddleware: [
express.json(),
// Api middleware
{ path: '/api', handler: '~/api/index.js' },
]
}
// ~/api/index.js
// Router setup for serverMiddleware
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request);
Object.setPrototypeOf(res, app.response);
req.res = res;
res.req = req;
next();
});
export default {
path: '/api',
handler: router
};
hope it helps ! 👍
I'm not familiar with nuxtjs specifically, but after looking at the nuxtjs module export docs it looks like your require inside your index.js should be a path to the file, rather than just the string body-parser which I have to imagine just gets processed something like an npm module if no path is supplied.
Additionally, at least in NodeJS and per the MDN docs on export and import, the syntax to import something that's exported with export default is import { destructuredModuleName } from 'string/representing/relative/or/absolute/path/to/module'
or
import * as whatYouWantToCallTheObject from 'string/representing/relative/or/absolute/path/to/module' would give you a more traditional module object with properties/methods matching properties on the exported module.
This should fix it:
import bodyParser from 'body-parser'
You actually used the import() as a dynamic import. It returned a promise which didn't have the json property. Therefore, the error was displayed.

Why Morgan Logger server middleware doesn't work in separate file?

I am following this tutorial in my nextjs app for adding a new middleware for logging so I have the following code in my server.js:
// create a write stream (in append mode)
const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' })
// setup the logger
server.use(morgan('combined', { stream: accessLogStream }))
And it is working without a problem.
But I want to have the logic in a separate file in the middleware directory so I have :
middlewares/logger/index.js
import morgan from "morgan";
import fs from 'fs';
import path from 'path';
export default (req, res, next) => {
var accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' })
return morgan('combined', { stream: accessLogStream });
};
and in my server.js I will have: (after importing and initializing stuff)
server.use(logger);
server.use(othermiddlewareone);
server.use(othermiddlewaretwo);
The other middlewares are working fine but this one breaks. Do you know why is that?
I changed the middleware like this and now it is working fine :)
import type { Request, Response, Next } from './types';
import morgan from "morgan";
import fs from 'fs';
import path from 'path';
const accessLogStream = fs.createWriteStream(path.join(__dirname, 'server.log'), { flags: 'a' })
const logger = morgan('combined', { stream: accessLogStream })
export default async (req: Request, res: Response, next: Next) => {
logger(req, res, function (err) {
return next();
})
};

After Nuxt build api returns 404

I'm having this weird issue and I seriously need your help. In development my code works perfectly fine but after doing nuxt build and all the apis are returning 404 error. Please help
When I'm building the api and running it, it works. In development api and nuxt both works but building nuxt and the api, doesn't work.
Following are my configuration
Server configuration
import convert from 'koa-convert';
import cors from 'kcors';
import bodyParser from 'koa-body';
import session from 'koa-session';
import helmet from 'koa-helmet';
import config from 'config';
import serve from 'koa-static';
import mount from 'koa-mount';
import { cModules, cMiddleware } from '../app';
import { catchErr, statusMessage } from './errorConfig';
import nuxtConfig from './nuxtConfig';
function baseConfig(app, io) {
app.keys = config.get('secret');
app.proxy = true;
app.use(mount('/static', serve(config.get('paths.static'))));
app.use(convert.compose(
catchErr,
cors({
credentials: true,
origin: true
}),
bodyParser({
multipart: true,
formLimit: '200mb'
}),
session({
maxAge: 21600000
}, app),
helmet(),
statusMessage
));
cModules(app, io);
app.use(cMiddleware());
if (config.get('nuxtBuild')) {
nuxtConfig(app);
}
}
export default baseConfig;
And my Nuxt
import { Nuxt, Builder } from 'nuxt';
import koaConnect from 'koa-connect';
import isDev from 'isdev';
import config from '../../../nuxt.config';
async function nuxtConfig(app) {
const nuxt = new Nuxt(config);
if (isDev) {
await new Builder(nuxt).build();
}
const nuxtRender = koaConnect(nuxt.render);
app.use(async (ctx, next) => {
await next();
ctx.status = 200;
ctx.req.session = ctx.session;
await nuxtRender(ctx);
});
}
export default nuxtConfig;

Is there a way to bootstrap an Express app?

I'm building an app in Express but I'd like it to call out to S3 to retrieve some keys before the server actually starts up. Is this possible in Express? If I google bootstrap Express I get hits for setting up Express with twitter Bootstrap.
I have used Sails.js before and you could specify bootstrap configurations in a bootstrap.js file so I guess I'm looking for something similar. Otherwise are there alternatives?
I have a index.js file and a separate bin/www file which calls the index.js file. I'd like the bootstrapping done in index.js so that it's included as part of the tests. Right now I 'initialize' the bootstrap but as it's asynchronous the server is already up and running before the bootstrap has complete (or errored out) i.e.
import express from 'express';
import {initializeFromS3} from './services/initializerService';
import healthCheckRouter from './routes/healthCheckRouter';
import bodyParser from 'body-parser';
initializeFromS3(); // Calls out to S3 and does some bootstrapping of configurations
const app = express();
app.use(bodyParser.json()); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
// ---------------------------- Routes ----------------------------
app.use('/', express.static('dist/client/'));
app.use('/health-check', healthCheckRouter);
export default app;
Posting my solution for anyone who comes across the same and has a mind blank. I kept the bin/www and index.js files separately but had the express object returned from index.js via a method. Solution below thanks to the friendly people of Github.
Index.js file:
import express from 'express';
import {initialize} from './services/appService';
import healthCheckRouter from './routes/healthCheckRouter';
import loginRouter from './routes/loginRouter';
export function getExpress() {
return initialize()
.then(() => {
const app = express();
// ---------------------------- Routes ----------------------------
app.use('/', express.static('dist/client/'));
app.use('/login', loginRouter);
app.use('/health-check', healthCheckRouter);
return app;
})
}
bin/www file:
import winston from 'winston';
import bodyParser from 'body-parser';
import {getExpress} from '../index';
getExpress()
.then(app => {
app.use(bodyParser.json()); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
const port = 3002;
app.listen(port, () => {
winston.info(`Server listening on port ${port}!`);
});
})
.catch(err => {
winston.error('Error starting server', err);
});
Integration tests:
import request from 'supertest';
import {getExpress} from '../../index'
describe('/login integration test', () => {
let app = null;
beforeEach(done => {
getExpress()
.then(res => {
app = res;
done();
});
});
describe('GET /login', () => {
it('should return 400 error if \'app\' is not provided as a query string', done => {
request(app)
.get('/login')
.expect(400, done);
});
});
});