Building scalable and secure APIs is an immense challenge in-and-of-itself, and adding real-time functionality to an existing API only increases the complexity, often necessitating significant refactoring. Synapse attempts to solve this problem by defining a high-level, protocol-agnostic abstraction of a REST API, allowing resources to be defined once and served over multiple protocols—including HTTP, WebSockets, and SSE—while managing input validation and normalization as well as response caching under-the-hood. The library integrates seamlessly with Express, and can be used as much or as little as needed to add real-time functionality to an existing application, or to build an entire API from scratch.

Synapse applications are composed of Resources—classes that model resources as defined by the RESTful architectural style. In this sense, resources represent collections of data that are exposed by the API (e.g. User, Message, Session). Each resource defines a Schema composed of Fields, where fields are data types with specific requirements that can be validated (e.g. EmailAddress, Password, Number) and schemas are collections of fields representing a set of parameters by name (e.g. { email: EmailAddress, password: Password, age: Number }). A resource’s schema, then, defines the data necessary to construct an instance of that type.

Classes that extend Resource can also define static methods and expose them to the API. In TypeScript, this is accomplished using various decorators to attach information to the method, like its HTTP endpoint. For example, a class User might expose an endpoint method called register to POST /user. Each endpoint method also defines a schema, which determines exactly what data will be passed from an HTTP request to the method, and also ensures that all arguments are validated before the method is invoked.

Under the hood, Synapse uses these resource definitions to serve data produced by endpoint methods to clients in a protocol-independent manner, simplifying the process of adding real-time support to an application. By maintaining an in-memory cache of all requested data, it can also manage subscriptions to any cacheable request and automatically push updates to clients whenever the cached state changes.