Providing routing support

Whenever a request comes into our web server, we have to determine what response we want to send back. What we are building is going to respond to post and receive requests, which is similar to the way we build REST APIs. The ability to route incoming requests to responses is known as routing. Our application is going to handle three types of request:

  • A POST request with add as the URL (in other words, when we see http://localhost:3000/add/). This will add an image and the associated details to the database.
  • A GET request with get in the URL (as in http://localhost:3000/get/). This gets the IDs of all the saved pictures and returns an array of these IDs back to the caller.
  • A GET request with /id/ in the URL. This uses an additional parameter in the URL to get the ID of the individual picture to send back to the client. 
The reason that we are returning an array of IDs is that an individual image can be large. If we were to attempt to return all the images in one go, we would slow down the displaying of the images at the client side as they can be displayed as each one is being loaded. We could also breach the limits of how big the response we are passing back can be. In the case of large chunks of data, it's always worth looking to see how we can minimize what we are transmitting with each request.

The destination of each request corresponds to a unique action that we want to take. This gives us a hint that we should be able to split each route into a single class that does nothing but service that action. To enforce the single action, we define the interface that we want our routing classes to use:

export interface IRouter {
AddRoute(route: any): void;
}

We are going to add a helper class that will be responsible for instantiating each router instance. The class starts off simply enough, creating an IRouter array that the route instances will be added into:

export class RoutingEngine {
constructor(private routing: IRouter[] = new Array<IRouter>()) {
}
}

Things get interesting with the method that we use to add the instances in. What we are going to do is accept a generic type as a parameter and instantiate the type. To do this, we must take advantage of a TypeScript feature that allows us to accept a generic type and specify that when new is called on it, it returns an instance of the type.

As we have specified a generic constraint on our type, we will only accept IRouter implementations:

public Add<T1 extends IRouter>(routing: (new () => T1), route: any) {
const routed = new routing();
routed.AddRoute(route);
this.routing.push(routed);
}
The route that is passed into the method comes from Express. It's the router instance that we tell our application to use.

Now that we have our routing support in place, we need to write the classes that correspond with the route requests we identified previously. The first one that we are going to look at is the class that accepts the add post:

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

}
}

This method works by stating that when we receive an /add/ post, we will take the request, process it, and send a response back. What we do with the request is up to us, but whenever the routing determines that we have a match here, we will execute this method. In this method, we are going to create a server-side representation of the picture and save it to the database.

For the purposes of this application, we have only introduced Express routing. Angular has its own routing engine, but for the purposes of what we wanted to put in place in our code, we have no need for it. In Chapter 5, Angular ToDo App with GraphQL and Apollo, we introduce Angular routing.

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

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