Supercharge v3 changed the way you’re sharing state across controller and middleware along the request lifecycle. The shared state in v2 could be anything, like an object, or a string, or an array.
This changed in v3 where the state is always an object. We also introduced a state bag to interact with the shared state. The state bag provides methods like get, set, has, remove, all, clear.
We extended the Request and Response classes within the @supercharge/http package.
Sharing State
Both classes now extend the InteractsWithState mixin. Interacting with the state from requests and responses allows you to share information to a central store from both instances:
import { HttpContext } from'@supercharge/contracts'classSignupController {/** * Handle the given request. */asynchandle({ request, response }:HttpContext):Promise<void> {// share information along the request lifecycle request.state().set('userId','user-identifier')// works as well and you‘re writing to the same session store as the request response.state().set('name','Supercharge') }}
Macroable Requests
We also made the HTTP Request class macroable. This let’s you extend a request instance with your custom method. Here’s an example how the @supercharge/session package decorates the request.session() method:
import { ServiceProvider } from'@supercharge/support'import { HttpRequest, Session } from'@supercharge/contracts'import { StartSessionMiddleware } from'./middleware/start-session'exportclassSessionServiceProviderextendsServiceProvider {/** * Register the `request.session()` macro function to the request constructor. */overrideasyncboot():Promise<void> {const Request =this.app().make<HttpRequest>('request')const session =this.app().make<SessionManager>('session') Request.macro('session',function(this:HttpRequest) {return session.createFrom(this.ctx()) }) }}
New Config Methods
We extended the @supercharge/config package and added the following methods:
isEmpty(key): determine whether the config store contains an item for the given key with is empty
isNotEmpty(key): determine whether the config store contains an item for the given key with is not empty
ensureNotEmpty(key): throws an error if the config store contains an item for the given key which has an empty value
The Manager class from the @supercharge/manager package now uses protected method visibilities to allow access from parent classes. Previously, we used the private visibility but that constraint isn’t flexible when extending the Manager base class.
Breaking Changes and Upgrade Guide v2 to v3
We needed to introduce some breaking changes for the v3 release. Here’s a comprehensive overview of breaking changes.
Node.js v16 Required
Supercharge v3 requires Node.js 16. You may check out the docs on system requirements to find more information on how to upgrade the installed Node.js version.
Shutdown Method in Service Providers
Starting with the v3 release you can implement an async shutdown() method in your ServiceProvider instances. The shutdown method will be called by the application instance when receiving a SIGINT or SIGTERM signal. Both signals are usually emitted when using CTRL+C in your terminal to stop a process.
Here’s a sample service provider using the shutdown method to stop a database connection:
The Supercharge service container supports binding aliases starting in v3. Binding aliases describe alternative keys pointing to a binding.
Here’s an example used in the framework. The Supercharge HTTP server uses the server binding. To be more specific what server instance will be resolved, we added the http.server alias for the server binding. Here’s the code used in the HTTP service provider:
this.app() .singleton('server',()=>newServer(this.app())) .alias('server','http.server')const server = app.make('http.server')// resolves the HTTP server instance
remove synchronous collection: everything is async and must be awaited
// nowconst { Collect } =require('@supercharge/collections')const numsGreater5 =awaitCollect([5,6,7]).filter(num => num >5)// beforeconst Collect =require('@supercharge/collections')const numsGreater5 =Collect([5,6,7]).filter(num => num >5).all()
removed iterator support: @supercharge/collections v4 is fully async and we’re going to add async iterators. In a later feature release. For now, we’re shipping v4 without async iterators. We appreciate a pull request if you want to add iterator support.
What is @supercharge/collections?
The @supercharge/collections package is an async array implementation. It provides a fluent interface to work with JavaScript arrays supporting async callbacks in methods like map, find, filter, reduce, and so on.
Here’s an example how to use @supercharge/collections:
The new @supercharge/sttp package has been published the in the NPM registry 🥳 Version 1.0 is now available to everyone.
What is Sttp?
Sttp is an HTTP client for a pleasant developer experience. It wraps the axios library providing a fluent interface to expressively compose your HTTP requests.
Sttp is an axios wrapper. So “why not use axios directly"?
Sttp (like axios) should make your life as a developer enjoyable. And I don’t enjoy working axios directly. Everything is configured using objects. But the editors and IDEs are not good at showing IntelliSense for objects. It’s always a guess on what to configure.
Do you find yourself looking at the axios docs on how to send query parameters? Or searching for the config key to send a request payload? Yeah, same here. I hate that.
Sttp solves that config-guessing by providing a fluent and expressive interface. Configure a request using Sttp’s chainable methods. You’ll also notice that Sttp’s requests are more readable.
Another thing is that axios throws an error when receiving a response with status code >= 400. Who thinks throwing an error for a valid response is the correct behavior? Sttp always returns a response instance. The Sttp response comes with handy methods to determine whether it’s a success, redirect, or error response.
Sttp Example
You may send a POST request with query parameters, request headers, and payload like this:
We published a new major version of the @supercharge/promise-pool package: version 2.0 is now available in the NPM registry!
Changes
There’s no new feature for the promise pool in version 2.0.
A change that may be helpful for you is the extended package exports. We’re now exporting internal classes and types besides the PromisePool class:
PromisePool
PromisePoolError
StopThePromisePoolError
Stoppable (interface)
ReturnValue (interface)
These classes and types may help you to type your code properly when passing promise pool values instance between methods.
Breaking Changes and Migration
Starting from @supercharge/promise-pool@2.x the package uses named exports. When migrating from 1.x to 2.x you must adjust your imports and destructure the PromisePool class out of the package:
// Now: 2.ximport { PromisePool } from'@supercharge/promise-pool'// orconst { PromisePool } =require('@supercharge/promise-pool')// Before: 1.ximport PromisePool from'@supercharge/promise-pool'// required the `esModuleInterop` flag in tsconfig.json// orconst PromisePool =require('@supercharge/promise-pool')
Previously, in @supercharge/promise-pool@1.x we used a default export matching the module.exports = XYZ syntax from CommonJS. This style of export required TypeScript users to enable the esModuleInterop flag in their tsconfig.json. To avoid this esModuleInterop flag requirement we changed the exports to use named exports.
Thank you Amit (amitlevy21) for sharing this issue and also providing a pull request to fix it ❤️
Promise Pool 1.9.0: Stoppable Pool
Nov 03, 2021
Starting from version 1.9.0 of @supercharge/promise-pool you can manually stop a running promise pool instance. You may stop a pool from the .process() or .handleError() methods. Both methods provide a stoppable pool instance as the third parameter.
Here’s an example how to stop a running promise pool:
import PromisePool from'@supercharge/promise-pool'await PromisePool .for(users) .process(async(user, index, pool)=> {if (condition) {return pool.stop() }// processes the `user` data })
You may also stop the pool from within the .handleError() method in case you need to:
import PromisePool from'@supercharge/promise-pool'await PromisePool .for(users) .handleError(async(error, user, pool)=> {if (error instanceofSomethingBadHappenedError) {return pool.stop() }// handle the given `error` }) .process(async(user, index, pool)=> {// processes the `user` data })