Creating our application

By now, creating a node application should be second nature, so we aren't going to cover how to do that anymore. The tsconfig file that we are going to use is as follows:

{
"compileOnSave": true,
"compilerOptions": {
"incremental": true,
"target": "es5",
"module": "commonjs",
"outDir": "./dist",
"removeComments": true,
"strict": true,
"esModuleInterop": true,
"inlineSourceMap": true,
"experimentalDecorators": true,
}
}
The incremental flag in the settings is a new feature introduced in TypeScript 3.4 that allows us to perform incremental builds. What this feature does is build something called a project graph when the code is compiled. The next time the code is built, the project graph is used to identify code that hasn't changed, meaning that it doesn't need to be rebuilt. In bigger applications, this can save a lot of time in terms of compiling.

We are going to save messages to a database, so it's going to come as no surprise that we are going to start off with the database connection code. What we are going to do on this occasion is move our database connection to a class decorator that accepts the name of the database as the parameter to the decorator factory:

export function Mongo(connection: string) {
return function (constructor: Function) {
mongoose.connect(connection, { useNewUrlParser: true}, (e:unknown) => {
if (e) {
console.log(`Unable to connect ${e}`);
} else {
console.log(`Connected to the database`);
}
});
}
}
Don't forget to install mongoose and @types/mongoose before creating this.

With this in place, when we create our server class, we simply need to decorate it, like this:

@Mongo('mongodb://localhost:27017/packt_atp_chapter_06')
export class SocketServer {
}

That's it. When SocketServer is instantiated, the database will be connected automatically. I have to admit that I really like the simplicity of this approach. It's an elegant technique that can be carried over into other applications.

In the previous chapter, we built a DataAccessBase class to simplify the way we worked with data. We are going to take that class and remove some of the methods we aren't going to use in this application. At the same time, we are going to see how we can remove the hard model constraints. Let's start with the class definition:

export abstract class DataAccessBase<T extends mongoose.Document>{
private model: Model;
protected constructor(model: Model) {
this.model = model;
}
}

The Add method should also look familiar from the previous chapter:

Add(item: T): Promise<boolean> {
return new Promise<boolean>((callback, error) => {
this.model.create(item, (err: unknown, result: T) => {
if (err) {
error(err);
}
callback(!result);
});
});
}

In the previous chapter, we had a constraint that finding a record needed to have a field called Id on it. While that was an acceptable limitation there, we really don't want to force applications to have Id as a field. We are going to provide a more open implementation that will allow us to specify any criteria we need for retrieving records and the ability to select what fields to return:

GetAll(conditions: unknown, fields: unknown): Promise<unknown[]> {
return new Promise<T[]>((callback, error) => {
this.model.find(conditions, fields, (err: unknown, result: T[]) => {
if (err) {
error(err);
}
if (result) {
callback(result);
}
});
});
}

Just like in the previous chapter, we are going to create a mongoose.Document-based interface and a Schema type. This will form the message contract and will store details about the room, the message text, and the date when we received the message. These will then be combined to create the physical model that we need to use as our database. Let's see how:

  1. First, we define the mongoose.Document implementation:
export interface IMessageSchema extends mongoose.Document{
room: string;
messageText: string;
received: Date;
}
  1. The Schema type that corresponds to this looks like this:
export const MessageSchema = new Schema({
room: String,
messageText: String,
received: Date
});
  1. Finally, we create a MessageModel instance, which we will use to create the data access class that we will use to save and retrieve data:
export const MessageModel = mongoose.model<IMessageSchema>('message', MessageSchema, 'messages', false);
export class MessageDataAccess extends DataAccessBase<IMessageSchema> {
constructor() {
super(MessageModel);
}
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.160.61