Latest Supercharge Updates

News and updates from Supercharge and the ecosystem.

Supercharge 3.0 Released

Jun 21, 2022

This week we shipped Supercharge 3.0 🥳 it’s an exciting chapter bringing you the newest framework additions and a completely new session package.

Supercharge 3 new ships a new session package. HTTP sessions allow you to keep a state across requests and remember information for users.

Sessions

The Supercharge 3.x release line ships with a new @supercharge/session package. You may check out the session docs for more details.

All New Supercharge Projects Ship With Session Support

We added sessions to the application boilerplate. You’ll have sessions configured when creating a new Supercharge application.

Adding Sessions to your Existing Supercharge Project

You can add HTTP sessions to your existing application. First, install the package as a project dependency:

npm i @supercharge/session

Then change the following files and add the given content:

app/http/kernel.ts

import { StartSessionMiddleware as StartSession } from '@supercharge/session'

export class HttpKernel extends Kernel {
  override middleware (): Array<MiddlewareCtor | Class> {
    return [
      StartSession,
    ]
  }
}

bootstrap/providers.ts

import { SessionServiceProvider } from '@supercharge/session'

export const providers: ServiceProviderCtor[] = [

  SessionServiceProvider,
]

The last step is to add the config/session.ts file. Please create that file in your project and copy and paste the content from the config file on GitHub.

HTTP State Bag

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.

Supercharge v2

// setting values
request.state().userId = 'user-identifier'

// getting values
request.state().userId
// 'user-identifier'

Supercharge v3

// setting values
request.state().set('userId', 'user-identifier')

// getting values
request.state().get('userId')
// 'user-identifier'

Please find more details in the HTTP docs on sharing state.

Extended HTTP Request and Response

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'

class SignupController {
  /**
   * Handle the given request.
   */
  async handle({ 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'

export class SessionServiceProvider extends ServiceProvider {
  /**
   * Register the `request.session()` macro function to the request constructor.
   */
  override async boot (): 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

Find more details in the config docs.

Visibility Changes in the Manager

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:

import { ServiceProvider } from '@supercharge/support'

export class MongodbServiceProvider extends ServiceProvider implements ServiceProviderContract {
  /**
   * Register MongoDB services into the container.
   */
  override register (): void {
    this.app().singleton('mongodb', () => {
      // …
    })
  }

  /**
   * Stop MongoDB connections.
   */
  override async shutdown (): Promise<void> {
    await this.app().make('mongodb').disconnect()
  }
}

Find more details in the service provider docs.

Container Aliases

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', () => new Server(this.app()))
  .alias('server', 'http.server')

const server = app.make('http.server')
// resolves the HTTP server instance

Find more details in the service container docs.


Collections 4.0

Jan 08, 2022

A new major release of our @supercharge/collections package has been published the in the NPM registry 🥳 Version 4.0 is now available to everyone.

Breaking Changes (compared to version 3.x)

  • use named exports

    // now
    const { Collect } = require('@supercharge/collections')
    
    // before
    const Collect = require('@supercharge/collections')
  • remove synchronous collection: everything is async and must be awaited

    // now
    const { Collect } = require('@supercharge/collections')
    const numsGreater5 = await Collect([5, 6, 7]).filter(num => num > 5)
    
    // before
    const 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:

import { Collect } from '@supercharge/collections'

await Collect([ 1, 2, 3, 4, 5 ])
  .map(async id => {
    return await User.findById(id)
  })
  .filter(async user => {
    return await user.notSubscribedToNewsletter()
  })
  .forEach(user => {
    await user.subscribe()
  })

Documentation

We added a dedicated and extensive docs section for collections. Sweet!

Enjoy working with collections!


Arrays 1.0

Dec 31, 2021

The new @supercharge/arrays package has been published the in the NPM registry 🥳 Version 1.0 is now available to everyone.

What is Arrays?

The @supercharge/arrays package is an extended array class with helpful, additional methods compared to native JavaScript arrays. For example, there’s

  • at()
  • min()
  • max()
  • last()
  • findLast()
  • collapse()
  • isEmpty()
  • isNotEmpty()
  • diff(...candidates)
  • intersect(...candidates)
  • … and many more

Documentation

Check out the extensive documentation for Arrays is available in the docs. Sweet!

Examples

Import the @supercharge/arrays package and use it the same way you would use JavaScript’s Array class:

import { Arr } = from '@supercharge/arrays'

const users = Arr.from([])

users.isEmpty()
// true

users
  .push({ id: 1, name: 'Marcus' })
  .push({ id: 2, name: 'Norman' })
  .push({ id: 3, name: 'Christian' })

users.isNotEmpty()
// true

users.length()
// 3

const christian = users.last(user => {
  return user.id > 1
})
// { id: 3, name: 'Christian' }

Enjoy Arrays!


Sttp 1.0

Nov 12, 2021

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.

Documentation

An extensive documentation for Sttp is available in the docs. Sweet!

Why Not Use Axios Directly?

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:

import { Sttp } from '@supercharge/sttp'

const response = await Sttp
  .withQueryParams({ page: 3 })
  .withHeaders({ 'X-API-Token': 123 })
  .withPayload({ name: 'Supercharge' })
  .post('https://your-api.com/v2/users')

Then use the response instance to process the returned data. For example, you can call:

if (response.isSuccess()) {
  return response.payload()
  // { id: 1, name: 'Supercharge' }
}

if (response.isError()) {
  return response.payload()
  // { statusCode: 401, message: 'Invalid X-API-Token' }
}

Enjoy Sttp!


Promise Pool 2.0

Nov 10, 2021

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.x
import { PromisePool } from '@supercharge/promise-pool'
// or
const { PromisePool } = require('@supercharge/promise-pool')

// Before: 1.x
import PromisePool from '@supercharge/promise-pool' // required the `esModuleInterop` flag in tsconfig.json
// or
const 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 instanceof SomethingBadHappenedError) {
      return pool.stop()
    }

    // handle the given `error`
  })
  .process(async (user, index, pool) => {
    // processes the `user` data
  })

Enjoy!