Back to our routing

With the Picture model available to us, we can populate it directly from inside our add route. The request body contains the same parameters as our schema, so the mapping is invisible to us. When it has been populated, we call the save method. If there's an error, we will send this back to the client; otherwise, we are going to send the picture back to the client:

const picture = new Picture(request.body);
picture.save((err, picture) => {
if (err) {
response.send(err);
}
response.json(picture);
});
In production applications, we wouldn't really want to send the error back to the client as that exposes the inner workings of our application. With a small application, intended for our own use only, it is less of an issue and it is a useful way to determine what has gone wrong with our application because we can simply view the error in the browser console window. Professionally, I would recommend sanitizing the error and sending one of the standard HTTP responses back instead.

The handler for get requests is no more complicated. It starts off in a similar fashion to the add router:

export class GetPicturesRouter implements IRouter {
public AddRoute(route: any): void {
route.get('/get/', (request: Request, response: Response) => {

});
}
}
The Request and Response types in our routes come from Express, so they should be added as imports in the class.

What we are trying to do with this call is get the unique list of pictures the user has uploaded. Internally, each schema adds an _id field, so we are going to use the Picture.distinct method to get the full list of these IDs, which we are then going to send back to the client code:

Picture.distinct("_id", (err, picture) => {
if (err) {
response.send(err);
}
response.send(pic);
});

The last router that we need to put in place takes an individual ID request and retrieves the related item from the database. What makes this class slightly more complicated than the preceding ones is that we need to manipulate the schema slightly to exclude the _id field before we transmit the data back to the client.

If we didn't remove this field, the data our client would receive wouldn't match the type that it was expecting, so it wouldn't be able to automatically populate an instance. This would result in our client not displaying this data even though it received it back unless we manually populated it at the client side:

export class FindByIdRouter implements IRouter {
public AddRoute(route: any): void {
route.get('/id/:id', (request: Request, response: Response) => {
});
}
}
The syntax with :id tells us that we are going to receive a parameter called id here. The request exposes a params object, which will expose this parameter as id.

We know that the id parameter we have received is unique so we can use the Picture.findOne method to retrieve the matching entry from the database. In order to exclude the _id field from the result we are going to send back to the client, we must use -_id in the parameters to remove this:

Picture.findOne({ _id: request.params.id }, '-_id', (err, picture) => {
if (err) {
response.send(err);
}
response.json(picture);
});

The Server class needs a little bit of extra attention at this point. We have created the RoutingEngine and Mongo classes, but there is nothing in the Server class to hook them up. This is easily sorted by extending the constructor to add instances of them. We also need to add a call to Start to connect to the database. If we changed our Server class to an abstract class and added an AddRouting method, we would stop anyone from directly instantiating the server.

Our applications will need to derive from this class and add their own routing implementations using the RoutingEngine class. This is the first step to breaking the server into smaller discrete units that separate out the responsibilities. One of the big changes in the Start method is that, once we have added our routing, we tell the application to use the same express.Router() that our routing engine is using, so any requests are then automatically hooked up:

constructor(private port: number = 3000, private app: any = express(), private mongo: Mongo = new Mongo(), private routingEngine: RoutingEngine = new RoutingEngine()) {}

protected abstract AddRouting(routingEngine: RoutingEngine, router: any): void;

public Start() : void {
...
this.mongo.connect();
this.router = express.Router();
this.AddRouting(this.routingEngine, this.router);
this.app.use(this.router);
this.OnStart();
this.app.listen(this.port, () => console.log(`Express server running on port ${this.port}`));
}

With this in place, we can now create a concrete class that extends our Server class and adds the routers that we have created. This is the class that we will start when we run our application:

export class AdvancedTypeScriptProjectsChapter4 extends Server {
protected AddRouting(routingEngine: RoutingEngine, router: any): void {
routingEngine.Add(AddPictureRouter, router);
routingEngine.Add(GetPicturesRouter, router);
routingEngine.Add(FindByIdRouter, router);
}
}

new AdvancedTypeScriptProjectsChapter4(3000).WithCorsSupport().Start();
Don't forget to remove the original call to start the new Server(3000).Start(); server.

Our server-side code is finished. We aren't going to add any more features to it, so we can get back to the client-side code.

..................Content has been hidden....................

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