Server-Side TypeScript with NodeHow to build APIs and more with TypeScript and NodeKenneth ReillyBlockedUnblockFollowFollowingMay 28IntroductionIn this article we’ll examine the process of creating APIs and other tools with Node and TypeScript.
The powerful and expressive features in TypeScript, combined with the flexibility and scalability of Node, make this a great combo for building high-performance services in a relatively short period of time.
As of 2019, Node has been around for about a decade and TypeScript has been on the scene for about six years, give or take.
Both of these technologies showed great potential even back in early alpha stages, and after many years of production use and maintenance, they have become solid and mature tools that are steadily gaining popularity and widespread adoption, especially in the age of rapid development in which software is expected to just work.
The example project will demonstrate a simple combination HTTP server and command-line tool, to create and save new instances of vehicles with types and unique non-serial ID’s, and to retrieve the resulting list of vehicles.
Getting StartedThe complete source code for the project used in this article is available here.
If you don’t already have these tools, grab a copy of Node and TypeScript.
Likewise if you don’t already have an editor that supports this environment, the popular Visual Studio Code supports TypeScript out of the box and will provide an option to download a Node extension when the project is opened.
Once you have a working installation of Node and a TypeScript compiler, you can compile the project from within the source directory with tsc, and then start the project with the .
Project ConfigurationThis project was created with the standard npm init command.
Next, the popular Express server was installed with npm install –save express.
The TypeScript compiler itself is configured with the .
/build folder as the output directory along with esnext as the target version of ECMAScript and commonjs as the JS output module type.
Additionally, TypeScript types are installed for Node with the command install –save @types/node.
Let’s take a look at the project’s package.
json and tsconfig,json files:Contents of package.
json and tsconfig.
json, the dependencies for this project consist of types for node and express along with the express package itself.
json, the compiler is instructed to produce CommonJS modules written in esnext syntax (the latest current development version of ECMAScript), and place them under the .
/build folder along with source maps for debugging in TypeScript.
That covers the basic project setup with a few requirements for types and Node/Express.
The rest we’ll implement from the ground up to get a feel for the environment and what can be done using these development tools.
Application Entry PointThe main application file defined in the package config file is build/index.
js so we’ll take a look at its corresponding source file, src/index.
ts:Contents of the main application file src/index.
tsWithin this file we have the App class with a few properties and methods for handling initialization via command line arguments along with a get accessor for port, which is the port number that our express server will listen on.
The static init() method takes a single command-line argument which can be either start-server or list-vehicles depending on what the operator wants to do at the time.
If one of these options is not provided, a default help message is displayed listing out the available arguments for this program.
The start_server() method does just that, by creating an instance of express and setting up two routes, the / route which displays a help message, and /make-vehicle which takes a query string parameter of class that should correspond to one of the available vehicle types which we will examine in further detail momentarily.
If the value of class is not one of the expected values, a help message is sent to indicate the available vehicle classes, which we will take a look at in the next section.
Vehicle Class DefinitionsThe next file we’ll check out is src/vehicles.
ts with our vehicle definitions:The file src/vehicles.
ts with vehicle type definitionsIn this file, we have a few different things helpful in the creation of new vehicle instances.
First of all, the crypto library is imported to generate new random ID’s rather than using serial mechanisms which are prone to sync issues along with the classic predictable serial number attack.
Of course that isn’t a huge deal with our simple API, however it becomes good practice to build things that are difficult to circumvent the design of by default rather than trying to bolt-on security at a later point in time, which rarely works.
Next up are a couple of enum definitions for VehicleColor and VehicleType giving us some constraint over these vehicle properties rather than letting them be set to something nonsensical in our app, like ‘transparent’ or ‘UFO’.
The abstract Vehicle class defines a base set of properties and methods expected of any vehicle in our system, such as vehicle_id, color, type, and a few others.
In addition, this class defines a static method generate_id() for generating the vehicle_id itself, which is called by the constructor when any vehicle is created.
This means that any class extending the Vehicle class will have a properly formatted ID, without requiring any further implementation.
Also defined are the individual classes GroceryGetter, CopAttractor, and JunkHauler, each with property values that reflect the type of vehicle it is.
For example, the sports car has double the speed and half the efficiency of the family car, with the truck coming in last on both of these.
Each vehicle has an implementation of drive() which returns a description of how it moves along.
API Logic DefinitionNext up is the API controller file itself, src/simple-api.
ts:The file src/simple-api.
ts with the SimpleAPI classThe SimpleAPI class implements some basic API logic for creating and saving new vehicle instances, in addition to listing the vehicles currently in the data store.
The make_vehicle(vehicle_class) method takes a class name for the requested vehicle, calls get_new_vehicle(vehicle_class) in turn to create an instance of whichever vehicle is required, and then saves the vehicle to the data store by calling save_vehicle(vehicle) which builds a file path and requests a write operation on the DataStore.
The resulting vehicle is finally returned to the caller as a Promise<Vehicle> object.
For more information about asynchronous programming, see this this guide on ES6 Promises.
The list_vehicles() method lists all of the vehicles stored in the data store, by retrieving a list from the DataStore and then calling console.
log() on each.
Simple File-Based Data StoreThe last file in our example project is src/datastore.
ts:The simple DataStore class in src/datastore.
tsThe DataStore class implements a very simple file-based datastore for this project, with a write(path, data) method for storing the vehicle JSON data, and a list(path) method for retrieving an array of vehicles from the previously stored data files.
The typical file IO operations with all of the callbacks have been wrapped in Promise objects to keep the implementation details hidden from the rest of the program, which keeps these components from becoming tightly coupled (a situation that can get out of hand very fast).
Test DriveWith the file definitions out of the way, the next thing to do is try out the API and the command-line features of our ultra-simple demo service:Running the simple API server from the command lineRunning the project with no arguments using node build/index.
js produces a help message, which is useful for someone who doesn’t work with it every day (like a project maintainer months or years down the road, who will either love or hate you depending on how easy or difficult you make their job).
When we run it again with node build/index.
js start-server we get the message Listening on port 3000 letting us know that everything is working as expected.
Now, let’s try running some curl requests against the API:Making some requests against the simple API serverHere we can see how the help messages provide clues about how to use the API.
Someone can go from making blind requests against it, to understanding exactly how to use it properly, in just a few simple commands that follow the help prompts.
Of course, in some cases you may not want to provide help info, but for simple public APIs this can be useful for the developers who consume your application web services.
Last but not least, let’s check out the feature for listing all the vehicles that were created by calling the API:Output of the list-vehicles command line argumentThe list-vehicles argument retrieves a list of .
json files from the folder data/vehicles and then prints this list in a nice format that shows all the various properties and types of the shiny new vehicles made by the API.
ConclusionThis example demonstrates the kind of power and flexibility available with modern server-side technologies such as Node with TypeScript.
With a few simple files, we have defined an API to create instances of an object with a unique ID for each, in addition to a custom file-based JSON data store and a utility for listing all the vehicles that have been created.
This project could be extended to create cool things like a simple general-purpose database.
For source code to this and other example projects utilizing state-of-the-art modern technologies, check out my GitHub profile.
Thanks for reading!.