December 3, 2018











Hello! In this new series, we build a RESTful application with TypeScript Express. The course is going to cover the fundamental concepts of the express framework and tools like MongoDB. Some basic knowledge of TypeScript would be useful, as we focus on Express here.

I will update the repository with the current state of the application as the course progresses. Feel free to give it a star! It is available at this address: mwanago/express-typescript. You can use it as a boilerplate if you would find it useful.

TypeScript Express tutorial #1

Express is a framework for Node.js used to build the backend for web applications. It is unopinionated, meaning that you can use it in a manner in which you see fit. In this tutorial, I present a way that works for me while working with the TypeScript Express.

Starting up the project

To get started we need to install necessary packages first using NPM.

1 2 npm init npm install typescript express ts - node

If you would like to know more about NPM, check out Keeping your dependencies in order when using NPM

We start with an elementary tsconfig.json file:

tsconfig.json

1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "compilerOptions" : { "sourceMap" : true , "target" : "es2017" , "outDir" : "./dist" , "baseUrl" : "./src" } , "include" : [ "src/**/*.ts" ] , "exclude" : [ "node_modules" ] }

To run our project, we need to add a script in our package.json:

package.json

1 2 3 "scripts" : { "dev" : "ts-node ./src/server.ts" }

As you can see, our app starts at the server.ts file in the src directory. Let’s start with the basics:

src/server.ts

1 2 3 4 5 6 7 8 9 import * as express from 'express' ; const app = express ( ) ; app . get ( '/' , ( request , response ) = > { response . send ( 'Hello world!' ) ; } ) ; app . listen ( 5000 ) ;

The express() function creates the Express application that we are going to interact with.

function creates the application that we are going to interact with. The get function, putting it simply, attaches a callback function to the specified path for the HTTP GET requests. When someone makes a GET request for the specified path, the callback function runs. the request and response objects represent the HTTP request and response properties the response.send function causes the response to be sent to the client

function, putting it simply, attaches a callback function to the specified path for the HTTP GET requests. the listen function makes the app listen for a connection on the specified port. locally running app . listen ( 5000 ) causes our application to be accessible at http : //localhost:5000

function makes the app listen for a connection on the specified port.

To test our application we use Postman. To install it, visit getpostman.com.

Let’s give it a try!

1 npm run dev

Middleware

Middleware functions have access to the request and response objects. It can attach to any place in the request-response cycle. A third argument that middleware receives is the next function. When called, the next middleware in the chain is executed. An example of a middleware is the get callback that handles the HTTP GET request that we’ve written above. It is a very specific middleware that executes on a particular case. They can also perform more generic tasks. Let’s create a very simple logger middleware that will log to console what requests were made.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import * as express from 'express' ; function loggerMiddleware ( request : express . Request , response : express . Response , next ) { console . log ( ` $ { request . method } $ { request . path } ` ) ; next ( ) ; } const app = express ( ) ; app . use ( loggerMiddleware ) ; app . get ( '/hello' , ( request , response ) = > { response . send ( 'Hello world!' ) ; } ) ; app . listen ( 5000 ) ;

In this example, as soon as someone sends the GET request to the /hello path, “GET /hello” will be printed in the console in which the app runs. In fact, it runs even when someone requests a page that you don’t have a handler for.

Thanks to calling next(), the control of the request can be passed further. If you create a middleware that neither ends the request-response cycle (for example by sending a response) or calls the next function, the request will not finish with a valid response.

There are a lot of ready to use middlewares that you can attach to your application and you will have plenty of chances to see some of them in this course. A crucial one is the body-parser. It parses the body of the incoming request and makes it available under the request.body property. In this example, we use the bodyParser.json middleware that parses the json data.

1 npm install body - parser

1 2 3 4 5 6 7 8 9 10 11 12 import * as express from 'express' ; import * as bodyParser from 'body-parser' ; const app = express ( ) ; app . use ( bodyParser . json ( ) ) ; app . post ( '/' , ( request , response ) = > { response . send ( request . body ) ; } ) ; app . listen ( 5000 ) ;

The body was sent back to us thanks to running response.send(request.body). Without the body parser, the request.body property wouldn’t be accessible.

As we go further, we explain more advanced concepts connected to the middleware.

Routing

The app object has a set of functions that attach callbacks to HTTP requests performed to specified paths, just like the examples above with app.get and app.post. You can also attach callbacks to other HTTP methods like POST, PUT, PATCH and DELETE. You can look up a whole list of them in the documentation.

Another way to set up routing is to use the router object. Once you create a router object you can call the methods like get, put, patch and delete just like on the app object.

1 2 3 4 5 const router = express . Router ( ) ; router . get ( '/' , ( request , response ) = > { response . send ( 'Hello world!' ) ; } ) ;

The only thing left that is required is to use the router.

1 app . use ( '/' , router ) ;

As you can see by the usage of app.use, the router instance is just a middleware that you can attach to your application.

The addresses of the routes are a combination of paths provided for the app.use and the router.METHOD.

1 2 3 4 5 router . get ( '/hello' , ( request , response ) = > { response . send ( 'Hello world!' ) ; } ) ; app . use ( '/api' , router ) ;

The code above results in creating a route /api/hello that responds with a text “Hello world!”.

Request

The request object contains information about the HTTP request, such as headers, the request query string, and parameters.

It inherits from http.IncomingMessage.prototype and therefore contains its fields and methods, aside from adding new ones.

1 http . IncomingMessage . prototype . isPrototypeOf ( request ) ; // true

If you would like to know more about prototypes, check out Prototype. The big bro behind ES6 class

1 2 3 4 5 6 7 app . get ( '/' , ( request , response ) = > { response . send ( { hostname : request . hostname , path : request . path , method : request . method , } ) ; } ) ;

We continue to go deeper into the request object as the course progresses.

Response

The response object represents the HTTP response that the application sends when receiving an HTTP request.

It inherits from http.ServerResponse.prototype: http.ServerResponse.prototype.isPrototypeOf(response); // true

Its the most important method is called send. It sends the HTTP response so that the client can receive it. The function accepts different types of data: strings, objects (Array included), or Buffers. Send ends the response process with data, but you can also end it without any data using the end function.

The same as with the request, we dive more into the response object as we go.

Controllers

A common way of structuring an Express application is called Model-View-Controller. Some of the key components of MVC are controllers. They contain the logic of the application and deal with handling client requests. Since this course covers TypeScript Express, we use classes. For the sake of readable code, I also create a class for the app instance itself.

src/server.ts

1 2 3 4 5 6 7 8 9 10 11 import App from './app' ; import PostsController from './posts/posts.controller' ; const app = new App ( [ new PostsController ( ) , ] , 5000 , ) ; app . listen ( ) ;

src/app.ts

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import * as express from 'express' ; import * as bodyParser from 'body-parser' ; class App { public app: express . Application ; public port: number ; constructor ( controllers , port ) { this . app = express ( ) ; this . port = port ; this . initializeMiddlewares ( ) ; this . initializeControllers ( controllers ) ; } private initializeMiddlewares ( ) { this . app . use ( bodyParser . json ( ) ) ; } private initializeControllers ( controllers ) { controllers . forEach ( ( controller ) = > { this . app . use ( '/' , controller . router ) ; } ) ; } public listen ( ) { this . app . listen ( this . port , ( ) = > { console . log ( ` App listening on the port $ { this . port } ` ) ; } ) ; } } export default App ;

src/posts/post.interface.ts

1 2 3 4 5 6 7 interface Post { author : string ; content : string ; title : string ; } export default Post ;

src/posts/posts.controller.ts

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import * as express from 'express' ; import Post from './post.interface' ; class PostsController { public path = '/posts' ; public router = express . Router ( ) ; private posts: Post [ ] = [ { author : 'Marcin' , content : 'Dolor sit amet' , title : 'Lorem Ipsum' , } ] ; constructor ( ) { this . intializeRoutes ( ) ; } public intializeRoutes ( ) { this . router . get ( this . path , this . getAllPosts ) ; this . router . post ( this . path , this . createAPost ) ; } getAllPosts = ( request : express . Request , response : express . Response ) = > { response . send ( this . posts ) ; } createAPost = ( request : express . Request , response : express . Response ) = > { const post: Post = request . body ; this . posts . push ( post ) ; response . send ( post ) ; } } export default PostsController ;

The code above results in creating the route named /posts that responds on the GET and POST request, storing and displaying the list of posts. In the upcoming parts of the Typescript Express course, we continue to use controllers to structure our code.

The getAllPosts and createAPost are arrow functions because they access properties of an instance of the class. Since they are passed to the router and not called directly, the context changes.

You can achieve the same result by calling this.router.get(this.path, this.getAllPosts.bind(this))

If you would like to know more about the this keyword, check out What is “this”? Arrow functions

Summary

In this article, we’ve grasped the very basics of building RESTful applications with TypeScript Express. It covered starting up a project, the use of middleware, routing and what are the request and response objects. The code that we write is structured in controllers and available in the repository. I hope that you will find it helpful. The repository will grow a lot in the upcoming future because there are more parts of the tutorial coming.