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);
});
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) => {
});
}
}
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) => {
});
}
}
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();
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.