WS + API: A NodeJS Server
Designing a Node Server with Web Sockets and RESTful API
- Part 1: One Server To Rule Th…Most of Them
- Part 2: WS + API: Getting Started
- Part 3: WS + API: Enter the Index.ts
- Part 4: Interlude: Protocols
- Part 5: WS + API: Web Socket Handler
- Part 6: WS + API: Getting the Message
- Part 7: Interlude: Redis & Redis-OM
- Part 8: WS + API: Actions Speak Louder Than Code
- Part 9: From Node to Deno
I suppose the best way to get started when talking about a development project is to discuss the requirements, setup, and goals. I briefly covered the goals in the first post so I won’t go in-depth here but I will talk about targets as the code describes them throughout.
Requirements
There’s nothing platform-specific about the project; I’m developing on Windows 10 using Visual Studio Code but that’s no so much a requirement as it is a bit of trivia.
Eventually, though, the project will be using databases. One goal (see?) is to allow for this app to leverage several popular relational databases for “hard” storage, but to favor none of them by allowing the user/developer to change config values to connect to the database of their choice. Since I come from a Microsoft-centric background, though, my working set is using SQL Server. I have folders set up in the project for Postgres and MySQL currently, but they have not been populated. For “soft” (cache) storage I’m building against Redis. As of this exact moment I don’t plan on adding in support for other NoSQL platforms, but if things go well I might spend some time adding them in.
Typescript
I am using Typescript for this project which, depending on your opinion on TS, is either the right way to do things or nothing you’re concerned about. I want to use TS because, again, I am used to the type-safe world of C#, but after having worked without a net using plain ‘ol Javascript for so long, I’m finding the act of hunting for custom types in the libraries I’m using to be a massive pain in the ass. If anything, searching for some third-party’s TS support and type structure is slowing me down by measurable amounts and I don’t like it, but I value such constructs as interfaces and types, so I shall persist.
Packages
Here’s a list of packages currently proscribed by the package.json
file. Not all of them are in use quite yet and I expect more to be added over time.
Dependencies
better-config
Better Config allows for the creation of a JSON config file that can be loaded early on in the app’s lifecycle, which can then be easily referenced throughout the app. The goal is that, after the app has been compiled for deployment, there will be a config file that a user can modify without having to recompile the app.
cors
Man, I hate CORS. I understand why it exists but I don’t think any web tech has caused as many headaches as having to work with CORS.
dotenv
This might not, really shouldn’t be needed, but I started using it a long time ago and never stopped. It allows for the use of .env
files for configuration options. These values are compiled into the app and aren’t available for editing, which is why I’m also using better-config.
express
It wouldn’t be a web-enabled server without something abstracting the roles of a server. Express allows for web sockets, API endpoints, and static file serving, so it can handle everything I want to do with this project.
lodash
I think lodash is one of those “love/hate” packages. Some people love the convenience it affords when working with collections and objects, but many people hate how bloated it can be. I only started using it in the past year, and it’s helped a lot, so I’m in the “love” camp.
mssql
This is the library which allows for interfacing with SQL Server. It includes a few other, smaller, possibly well-known packages and makes them easier to use.
redis
Redis is a NoSQL database which uses RAM to store data, and is often used to cache that data for a limited time. I have plans for Redis, but have never gotten to the actual implementation of those plans so we’ll have to see how they suss out when I get there.
redis-om
Resid-OM is a “relational object mapper” library which helps to take objects based on TS interfaces and converts them to a JSON format that can stored in Redis, and vice versa.
uuidv4
This library generates unique GUID/UUID which is a 128-bit value that I’m using as identifiers in several places.
In looking up the links for these packages I noticed that UUIDv4 is really old and has been superseded by uuid. I’ll need to upgrade my project to use this new package.
ws
WS stands for web sockets and is the library that uses a server (Express in this case) to listen for connections, messages, and disconnects while keeping open the bi-directional connection between client and server.
Type Libraries
I also have several type libraries for Express, lodash, MSSQL, node, and ws. All of these are named for their parent library, and are in the form of @types/[LIBRARY_NAME]
.
DevDependencies
These libraries are only used when developing, and are not included in the final build package.
nodemon
NodeMon — Node Monitor — allows for the development environment to be started, and re-started whenever a file in the project is modified. It saves a lot of time between edits.
typescript
Based on the values in the tsconfig.json
file, when a TS app is compiled for distribution, the result is a form of pure Javascript. Typescript is not distributed with the final app.
Also, nodemon has an accompanying type library.
Package.json
Here’s the package.json
file in it’s current glory.
{
"name": "web-socket-server-template",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"scripts": {
"start": "SET NODE_ENV=development&& nodemon ./src/index.ts"
},
"devDependencies": {
"@types/nodemon": "^1.19.6",
"nodemon": "^3.1.0",
"typescript": "^5.4.2"
},
"dependencies": {
"@types/express": "^4.17.21",
"@types/lodash": "^4.17.0",
"@types/mssql": "^9.1.5",
"@types/node": "^20.11.30",
"@types/ws": "^8.5.10",
"better-config": "^1.2.3",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"lodash": "^4.17.21",
"mssql": "^10.0.2",
"redis": "^4.6.13",
"redis-om": "^0.4.3",
"uuidv4": "^6.2.13",
"ws": "^8.16.0"
}
}
Config
.json
The config.json
file is currently small, and may be expanded in the future.
{
"application": {
"port" : 5335,
"api-version": "1.0.0",
"data-store": "MSSQL"
}
}
The most immediate setting is port
which dictates the port that the server will listen on for both API and web socket connections. The other uses for api-version
and data-store
will be discussed later.
.env.development
The standard for environment files is to name the active file .env
, but Node allows us to add a suffix which will be compiled in based on the value of NODE_ENV
. Since I’m still developing, the only .env
file has the .development
suffix.
MSSQL_DB_USER = 'USER_NAME'
MSSQL_DB_PASS = 'PASSWORD'
MSSQL_DB_STORE = 'DATABASE_NAME'
MSSQL_DB_SERVER = 'SERVER_NAME'
I currently only need SQL Server information so that’s all that’s present. I’ll fill in the values of the variables to work with my development SQL Server.
tsconfig.json
The boilerplate tsconfig.json
file is pretty long, and I haven’t yet modified any of the values so I won’t include the output here. The tsconfig.json
file can be generated automatically when the project is bootstrapped for a Typescript project.