I have been following component based strucutre for my projects and I have clone the repo for this purpose listed here
Everything is fine and testable except services.
So I decided to use awilix to inject dependencies in services.
This is what I tried.
I created a container.js file in the loaders.
const awilix = require('awilix');
const db = require('../db');
const AuthService = require('../components/auth/auth.service');
const container = awilix.createContainer({
injectionMode: awilix.InjectionMode.PROXY,
});
function setup() {
container.register({
db: awilix.asValue(db),
doGetAllService: awilix.asValue(AuthService.doRegister),
});
}
module.exports = {
container,
setup,
};
I invoked this container in app.js as below.
const express = require('express');
const app = express();
const cors = require('cors');
// load environment config variables
require('dotenv').config();
require('./loaders/container').setup();
...
In the doc it says, the first argument will be injected dependencies. But when I do console.log I still get undefined.
const doRegister = async (opts, { username, password }) => {
console.log(opts);
const user = await User.create({
username,
password,
role_id: 1, // assign role id here
});
return user;
};
For entire folder structure and source conde please go through the repo.
In order to have awilix resolve any dependencies something must consume from or bootstrap the dependency container. One way is container.resolve(...); (not permanent) and another is by binding the cradle to the request pipeline.
For awilix-express one can bind the cradle to requests like scopePerRequest(container):
Add this to your server middleware
app.use(scopePerRequest(container));
In case you are in need of a complete source, you can check this repository:
https://github.com/fdm-monster/fdm-monster/blob/fb0fc8075cfcd840ae7b50519fe6dd1525b03034/server/app-core.js#L63
I recommend you take a look at awilix-express for a complete server setup with controllers as this truly is cleaner than just function based API routes:
https://www.npmjs.com/package/awilix-express
Related
I have created a helper.js file that will contain helper functions. In order to use the helper functions, I have to import the file in all the locations where I want it to be used. Is there any way I can load/import the helper.js file globally?
helper.js
module.exports = {
responseObject: function (status, message, data, error) {
return {
status: status,
message: message,
data: data,
error: error
}
}
}
index.js
// load mongoose
require('./db/mongoose')
const express = require('express')
const app = express()
const port = process.env.PORT || 3000
app.use(helper)
// load routers
const userRouter = require('./routers/user')
app.use(express.json()) // automatically converts incoming requests to json
app.use(userRouter)
app.listen(port)
UserController.js
const User = require("../models/user")
const helper = require("../helper")
exports.createUser = async (req, res) => {
const user = new User(req.body)
try {
await user.save()
res.status(201).send({
status: true,
message: 'Registration success',
data: user,
error: {}
})
} catch (error) {
const response = helper.responseObject(false, "Wrong", {}, error)
res.status(400).send(response);
}
}
From the documentation:
In browsers, the top-level scope is the global scope. This means that
within the browser var something will define a new global variable. In
Node.js this is different. The top-level scope is not the global
scope; var something inside a Node.js module will be local to that
module.
This means you can't just define your responseObject in the "global" scope, as it always refers to the scope local to the module and will not be available to other modules.
But there's a way to do it, although I would not recommend it (search Google or SO why that's the case, this has been discussed in detail already). You can add your function/variable to the global object (which is somewhat similar to the window keyword in browsers):
// in your app.js "add" the exported stuff from the helper-module to the global object
const express = require('express')
...
global.myHelper = require('./helper');
// in e.g. your user-controller you could then access it like
const response = global.myHelper.responseObject(false, "Wrong", {}, error)
I've been using Express for a while but suddenly I'm unsure about something pretty basic --
I'm trying to add custom middleware to a KeystoneJS application -- specifically I'm adding a JWT token endpoint to a TinyMCE custom field
The custom field is
export let Wysiwyg = {
type: 'Wysiwyg',
implementation: Text.implementation,
views: {
Controller: Text.views.Controller,
Field: importView('./views/Field'),
Filter: Text.views.Filter,
},
adapters: Text.adapters,
prepareMiddleware,
};
and prepareMiddleware is
function prepareMiddleware() {
const tinymcePath = dirname(require.resolve('tinymce'));
const app = express();
app.use(cors());
app.use('/tinymce-assets', express.static(tinymcePath));
app.post('/jwt', function (req, res) {
// NOTE: Before you proceed with the TOKEN, verify your users' session or access.
const payload = {
sub: '123', // Unique user id string
name: 'John Doe', // Full name of user
// Optional custom user root path
// 'https://claims.tiny.cloud/drive/root': '/johndoe',
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 60 minutes expiration
};
try {
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256'});
res.set('content-type', 'application/json');
res.status(200);
res.send(JSON.stringify({
token: token
}));
} catch (e) {
res.status(500);
res.send(e.message);
}
});
return app;
}
This is all called from a KeystoneJS app that has its own ExpressJS server running. What exactly is the call to express() above doing? The ExpressJS API docs say
**express()**
Creates an Express application. The express() function is a top-level function exported by the express module.
var express = require('express')
var app = express()
I always understood this to be creating a new HTTP server. Surely you don't want to do that twice in a single app unless you're serving on different ports (which I'm not trying to do)?
Similarly, the KeystoneJS docs say
If you need to provide your own custom middleware for your system you
can create a custom App and include it in your exported apps.
class CustomApp {
prepareMiddleware({ keystone, dev, distDir }) {
const middleware = express();
// ...
return middleware;
}
}
Here, again, they're calling express().
What exactly happens when you callexpress()? It starts a new Express app, according to the docs, but I always thought this was equivalent to starting a new server. Surely, I thought, you can't start two servers on the same port.
Thanks for helping clear up my confusion -- I'm obviously not seeing the forest for the trees.
express() basically just creates a stack of middleware functions. It's not a server on its own.
Because it's just a stack of middleware, an Express app can be 'mounted' into another app. An example is shown here (edited for brevity):
var sub2 = express();
sub2.get("/", (req, res) => { res.json({}); });
var app = express();
app.use("/foo", sub2);
Defining and use()ing a new Express instance is really no different from loading any other middleware stack, such as express.Router().
As for binding to ports, usually, you'll only call the listen() helper function on the upper-most Express app instance. All this does is set up a basic HTTP server (so you don't have to) and registers your Express instance as the callback. It's little different from doing http.createServer(options, myUpperMostExpressApp);.
I am using express along with sequelize to create a REST API. I want to keep just one instance of my database and use it in all the routes I may create. My first thought was to create the instance as soon as the app runs. Something like this.
const express = require('express')
const databaseService = require( './services/database')
const config = require( './config')
const userRoute = require( './routes/user')
const app = express()
databaseService.connect(config.db_options).then(
connectionInstance => {
app.use('user', userRoute(connectionInstance))
app.listen(3000)
}
)
I have not seen something like that so I believe it's not a good approach.
Any ideas ?
A strategy that I use extensively in several production applications to great success is to define the database connection logic in a single file similar to the following:
database.js
const dbClient = require('some-db-client')
let db
module.exports.connectDB = async () => {
const client = await dbClient.connect(<DB_URL>)
db = client.db()
}
module.exports.getDB = () => {
return db
}
Obviously, the connection logic would need to be more complex then what I have defined above, but you can get the idea that you would connect to your database using whichever database client library that you choose and save a reference to that single instance of your database. Then, wherever you need to interface with your database, you can call the getDB function. But first, you would need to make a call to your connectDB function from your server application entry file.
server.js
async function run() {
const db = require('./database')
await db.connectDB()
const app = require('express')()
const routes = require('./routes')
app.use('/', routes)
app.listen(3000, () => {
console.log('App server listening to port 3000')
})
}
You would make the call to initialize your database connection in the server application entry file before initializing express. Afterwards, any place in your application that needs access to the database can simple call the getDB() function and use the returned database object as necessary from there. This does not require overly complicated connection open and close logic and saves on the overhead of constantly opening and closing connections whenever you want to access the database by maintaining just a single database connection that can be used anywhere.
Is there a way in nodejs to define a custom object type in it's own file, then instantiate it anywhere else in the codebase without needing to require in the file?
For example, every time I create a new map somewhere, I don't have to require in the map definition. I want to be able to do
var user = new User();
from anywhere without requiring in the definition.
If you don't want to require a dependency everywhere then you could use Dependency Injection. Dependency Injection is a pattern for passing dependencies or common information from one context to another.
The power of Dependency Injection is decoupling the Dependency itself with resolving and/or initializing that Dependency. In JavaScript, closures can be used to hide configuration/initialization details of a Dependency by locking in their context to the scope that created them. This means that your files implementing the Dependency don't need to be concerned with how to get it initialized (ie. a config) which, probably also means more thing it needs to require.
Below is an example of how a User can be required in one file, injected into a separate file and then be used as if it were required within the file it was injected to. I chose to use an Express Server and Dependency Injection into a Router as the example because a popular reason to use Node.js is to build a Web API. For another example of Dependency Injection, see this blog post from RisingStack Engineering (you'll need to scroll down to the Dependency Injection section).
The example has 3 files
lib/models/User.js
This is the module that is Injected into our UserRouter
/routes/user.js
This is the UserRouter and it returns a function that accepts a models object which, contains all Models the UserRouter needs. Once the router is created it is returned for use within an Express Server
server.js.
The Express Server that is going to be responsible for injecting the User Model into the UserRouter so it can create new users in a cache.
lib/models/User
class User {
constructor(name) {
this.name = name
}
}
module.exports = User
routes/user.js
const Router = require('express').Router
const UserRouter = models => {
// Use Destructuring to get the User model from the models object passed into the Router
// You can use regular ES5 for this as well: var User = models.User
const {User} = models
const router = new Router()
const users = []
// Return all users in cache
router.get('/', (req, res) => res.status(200).json({users}))
router.post('/', (req, res) => {
const {name} = (req.body || {})
if (!name) {
return res.status(400).send('A User must have a name')
}
// Create the new User
let newUser = new User(name)
// add it to the users cache
users.push(newUser)
// return 201 CREATED and send the user as the message body
return res.status(201).json(newUser)
})
// Return the router instance
return router
}
module.exports = UserRouter
server.js
const express = require('express')
const port = process.env.PORT || 1337
// require our UserRouter
const UserRouter = require('./routes/user')
// import the User model
const User = require('./lib/models/User')
// create an object called Models with the User now as a property
const Models = {User}
const server = express()
// Inject the Models directly into the UserRouter
server.use('/users', UserRouter(Models))
server.listen(port, () => console.log(`Listening on ${port}`))
How could I split the routing into different files?
This what I tried, but did not work:
// file 'index.js' as main in manifest.json
const createRouter = require('#arangodb/foxx/router');
const router = createRouter();
const entries = require('./routes/entries')
entries.init(router);
module.context.use("", router);
and the entries file is working as a function:
// file './routes/entries.js'
const db = require('#arangodb').db;
// [...] more const
module.exports = {
init: function(router) {
router.post('/entries', function(req, res) {
// [...] post handle
}
}
}
1) How could I modify the router in a js file and reuse in?
module.context.use(router)
2) Any idea how to handle all js files in the folder 'routes' to define the router and so minimize the definition for route files?
you can use the funtion router.use([path], middleware, [name]): Endpoint for that.
module.context.use('/entries', require('./routes/entries'), 'entries');
For more information take a look into the docs here or in the newest Foxx tutorial here which also use child routers.