I am currently trying to add a service layer to my NodeJS project, which is a simple API I build using express and sequelize. The project has a structure of the usual model, route, and controller (MRC) form; each has its own directory. Now all I need to do is add another directory that will contain the service files. The files will contain the business logic in the form of a class that I will export and include in my controller files.
Here I will display the controller and service files:
eruptionController.js
const EruptionService = require('../services/eruptionService.js');
let Eruption = new EruptionService()
module.exports = {
getEruptions: (req, res) => {
Eruption.get().then(result => {
return res.status(200).json({
message: "Get All Eruptions",
result
})
}).catch(err => {
return res.status(200).json({
message: "internal Server Error",
error: err.message
})
})
}
};
eruptionService.js
const { eruption } = require('../models');
class EruptionService {
constructor () {
this.eruptionModel = eruption;
}
get () {
this.eruptionModel.findAll()
.then(result => result)
.catch(err => err)
}
}
module.exports = EruptionService;
As we can see here, in the service file, I imported the eruption model from the model folder and include it in the constructor. Then I created a method named get, which purpose to retrieve all the eruption data from the database.
After that, I imported the EruptionService class into the controller file, initiated it as an instance, and include the class in the getEruptions function. The function will then be used in an express route in the form of an endpoint.
The issue is that when I tried to send a request to the endpoint, I got the following error
TypeError: Cannot read property 'then' of undefined
Based on the error, it seems that the EruptionService class was not exported properly since when I called the mothed get it's undefined.
I've been stuck with this for a while now, and I'm not really sure what's going on. Based on this post, I seem to be exporting the class properly.
I've also tried exporting in a way like below
module.exports = {EruptionService: EruptionService}
And I've also tried importing like
const EruptionService = require('../services/eruptionService.js');
But in the end, it either results in the same error or different one kind of error. I was wondering if anybody could point out what I was missing? Thanks in advance
You should return a promise function for getting the then function inside it. There is a issue in your service it should be like the below snippet
class EruptionService {
constructor () {
this.eruptionModel = eruption;
}
get () {
return this.eruptionModel.findAll()
}
}
Related
everyone. I've got a little node.js project with vue.js and I'd like to use a config packet (npm i config) in it. But when I add config to the vue.js component
import configData from config
and try to use one in the vue.js component
data: () => { config: configData }
I've got an exception when I make a request to the page. In the browser console appear:
Uncaught Error: Config file /config/runtime.json cannot be read. Error code is: undefined. Error message is: v.readFileSync is not a function
So I'd like to understand what's wrong with this config packet in vue.js. Is there a way to pass config data to the Vue component? Thanks a lot in advance
I created /config/default.json that contains the data I need, but when I try to pass it into my vue.js component the component page fall
data is a method that should return some variables. There is a mistake in your code. arrow functions works like this
if you want to return an empty object
data: () => ({})
If you want to call some method inside of it
func: () => {
someMethod();
anotherMethod();
}
And you can't write
data: () => { config: configData }
this is a mistake. You should put braces around your curly braces
I'm trying to build an express server.
In the app.js file I create the server and try to connect to my mongodb.
import express from "express";
import dbo from "./db/conn.js";
const app = express();
app.listen(PORT, async () => {
await dbo.connectToDatabase();
log.info(`Server is running on port: ${PORT}`);
});
In file conn.js
const client = new MongoClient(uri);
let db: Db;
export async function connectToDatabase() {
try {
await client.connect();
db = client.db(dbName);
await db.command({ ping: 1 });
log.info(`Successfully connected to database: ${db.databaseName}.`);
} finally {
await client.close();
}
}
export async function getDb() {
return db;
}
export default { connectToDatabase, getDb };
And now in a seperate file, I wantt to use the database connection I created in the app.js file.
like this:
const collection = getDb().collection(collectionName);
// insert documents in collection.
but when I try to run the app I get error TypeError: Cannot read properties of undefined.
So, is there a way to wait until the db connection is established?
There are some problems with your code.
First, the getDb function returns promise. So you can't use the collection method right away. Remove the async keyword because the current getDb function is not an asynchronous function.
export function getDb() {
return db;
}
Second, in the try & finally statement, the finally block is executed unconditionally regardless of whether an exception occurs in the code. Therefore, after displaying the connection success log in the connectToDatabase, the connection closes immediately. In order to insert the data, the connection must be connected, so please erase the await client.close(); code in finally block from connectToDatabase.
Then you can add a document to the collection without a problem.
One way to accomplish what you're trying to do is to pass that connection to a class or function you define elsewhere.
Say this is your collection code (or whatever thing even outside a collection that needs a raw db connection instance in order to use):
class Collection {
constructor(db) {
this.db = db
//do whatever else needs to be setup
}
use(name) {
//or use it here
this.db.use(name)
}
}
export default Collection
Then in your main file some code:
import Collection from './Collection.js'
const collection = new Collection(getDb)
collection.use('some arbitrary name')
This is just one way to do it but there are other ways as well. Also if this is the only thing using your getDb() method above, you can remove it from your main and put it in here as a manager.
static async getInitialProps({ query }) {
let content;
let alert;
try {
const first = query.first ? query.first : '';
content = await getContent(first);
} catch (error) {
alert = 'There was an error loading data, please try again.';
content = [];
}
return {
content,
alert,
};
}
I'm trying to write tests for this logic but because it is server side code I'm struggling to understand how I write a test for it as it isn't available to me in instance().
Google hasn't shown me the way on this one so I'm wondering how others have tackled writing tests for getInitial props.
First, take a look at what's a static method and what does the static keyword do.
Since getInitialProps is just a static function on a component, you can test it manually like any other function.
import MyComponent from "./path/to/MyComponent";
// Mock the getContent function which I don't know where it comes from.
jest.mock("../someModule.js", () => ({
getContent: () => Promise.reject()
}));
describe("MyComponent", () => {
it('populates the "alert" prop on getContent failure.', async () => {
// Inject anything you want to test
const props = await MyComponent.getInitialProps({
query: { first: "whatever" }
});
// And make sure it results in what you want.
expect(props).toEqual({
content: [],
alert: "There was an error loading data, please try again."
});
});
});
Most of the time, getInitialProps is called like that anyway.
export default class MyApp extends App {
static async getInitialProps ({ Component, router, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
The documentation describes getInitialProps goal and we can confirm that it's ok to call it directly and test its return value as an Object.
Notice that to load data when the page loads, we use getInitialProps
which is an async static method. It can asynchronously fetch
anything that resolves to a JavaScript plain Object, which populates
props.
Data returned from getInitialProps is serialized when server
rendering, similar to a JSON.stringify. Make sure the returned
object from getInitialProps is a plain Object and not using
Date, Map or Set.
For the initial page load, getInitialProps will execute on the
server only. getInitialProps will only be executed on the client
when navigating to a different route via the Link component or using
the routing APIs.
I am implementing Login Authentication in my reactjs aplication following the tutorial https://auth0.com/blog/adding-authentication-to-your-react-flux-app//
I have written a class call the AuthService and inside the AuthService I have a function call Login as shown below
import LoginActions from './LoginAction';
const URL_LOGIN = 'loginurl';
class AuthService {
login(username, password) {
// do something
}
}
Now, I am calling this Login method in my Login Component as shown below
//this function is to save data to the API
loginUser = (user) => {
// Here, we call an external AuthService.
Auth.login(user.username, user.password)
.catch(function(err) {
console.log("Error logging in", err);
console.log(err);
});
Everythin works well but when I submit data, I get the error
TypeError: WEBPACK_IMPORTED_MODULE_4__authentication_AuthService.a.login(...) is undefined
When I console log at the login method of the AuthService class, I see the returned data. I have looked around for a fast solution to this error but I have not gotten it. Any help on this will be appreciate.
I do not want to bring this action to the component as I am also going to use it in other areas of my application.
Also,I am a newbie to Reactjs as this is my first Authentication I am doing here.
Add the static keyword before the login method in the AuthService class.
I was waiting for djfdev to see an answer as in his comment:
You’ve defined login as an instance method, but are calling it like a static method. Either add the static keyword before the function name, or create an instance of Auth before calling login on it.
But it seems he's not providing an answer. What he meant that you can define a static method like:
static login(username, password) {
Or call login method in an instance like:
const auth = new Auth
auth.login(user.username, user.password)
Further, I hope you're exporting the class like:
export default AuthService
And importing it like:
import Auth from '...'
From my comment on the OP:
You’ve defined login as an instance method, but are calling it like a static method. Either add the static keyword before the function name, or create an instance of Auth before calling login on it.
So your two options are to, A) use the static keyword to define your method on the class itself:
import LoginActions from './LoginAction';
const URL_LOGIN = 'loginurl';
class AuthService {
static login(username, password) {
// do something
}
}
Or B) create an instance of Auth before calling the login method:
loginUser = (user) => {
// Here, we call an external AuthService.
new AuthService().login(user.username, user.password)
.catch(function(err) {
console.log("Error logging in", err);
console.log(err);
});
See the MDN docs for static
I solved this problem by adding static to the login method as advice above and removing the catch from it.
I have this aurelia component for displaying a feed to the user which depends on a custom API service class called Api for fetching the feed. The Api class has a get() function which in turn uses HttpClient to fetch the data.
Trying to test the component I want to mock the service class, specifically the get function, to return suitable test data and have this mock injected into the component via aurelia's DI container. The DI part I am having trouble with.
Here is the relevant part of component's js file
import {bindable, inject} from 'aurelia-framework';
import {Api} from 'services/api';
#inject(Api)
export class Feed {
events = null;
constructor(api) {
console.info('feed.js constructor, api:', api)
this.api = api;
}
And the relevant code from my test
beforeEach(done => {
...
let mockApi = new Api();
spyOn(mockApi, 'get').and.returnValue(mockGetResponse);
const customConfig = (aurelia) => {
let conf = aurelia.use.standardConfiguration().instance("Api", mockApi);
console.info('Registering Api:', conf.container.get("Api"));
return conf;
}
const ct = new ComponentTester();
ct.configure = customConfig;
sut = ct.withResources('activityfeed/feed');
sut.inView('<feed username.bind="username"></feed>')
.boundTo({username: TEST_USER});
sut.create(bootstrap).then(() => {
done();
});
});
This code is actually working the way I intended as far as I can tell. On creation of the component my customConfig function is called and the mockApi instance is logged to the console.
However later in the bootstrapping process the component constructor still receives an instance of the actual Api service class instead of my mock instance which was registered to the container.
Spent the last couple of hours trying to dig up any documentation or examples for doing things like this without success so if anyone can assist I would greatly appreciate it.
Or if there is / are alternative ways to accomplish this that would work just as well.
When testing a standard component that consists of both a view and a view model, using the aurelia-testing package, I find that a cleaner approach might be to let Aurelia create both the view and view-model, and use mocked classes for all view model dependencies.
export class MockApi {
response = undefined;
get() { return Promise.resolve(this.response) }
}
describe("the feed component", () => {
let component;
let api = new MockApi();
beforeEach(() => {
api.response = null;
component = StageComponent
.withResources("feed/feed")
.inView("<feed></feed>");
component.bootstrap(aurelia => {
aurelia.use
.standardConfiguration();
aurelia.container.registerInstance(Api, api);
});
});
it("should work", done => {
api.response = "My response";
component.create(bootstrap).then(() => {
const element = document.querySelector("#selector");
expect(element.innerHTML).toBe("My response, or something");
done();
});
});
});
This approach lets you verify the rendered HTML using the normal view model class, mocking the dependencies to control the test data.
Just to answer my own question, at least partially, if it can be useful to someone.
By using the actual Api class constructor as the key instead of the string "Api" the mock seems to be correctly injected.
I.e.
import {Api} from 'services/api';
...
let conf = aurelia.use.standardConfiguration().instance(Api, mockApi);