npm package: @starscream/core
Description: Starscream is an small, opinionated framework ontop of Fastify, almost like a distribution. It includes a bunch of plugins, sets up sensible defaults, and wires things together so you can work in a convention over configuration style. It aims to be the mo
Latest version: 0.4.3

# Starscream - Batteries Included Fastify

Starscream is an small, opinionated framework ontop of Fastify, almost like a distribution. It includes a bunch of plugins, sets up sensible defaults, and wires things together so you can work in a convention over configuration style. It aims to be the most productive tool for building API driven web services, including all the other little bits that don't happen to exactly be APIs.

## Status

Still pretty Gadget specific. Mostly used as a place to pull out code that feels general into a shared spot. Not really a framework yet, but handy for demarcating between Gadget API specific stuff and Good Web Server specific stuff.

## Features

- TypeScript everywhere
- Centralized, multi-environment, user extensible configuration
- File based Fastify plugin registration with scoping contexts
- pnpm workspace based development workflow
- Good testing support via transactional fixtures and integration testing sessions (with `fastify.inject`)

## Included Plugins

- `@fastify/secure-sessions` for sessions
- `@fastify/view` for view rendering
- `@fastify/websocket` for websockets
- `typeorm` for database connectivity
- etc, see `boot.ts`

### File Based Routes with Scope

Starscream uses a filesystem based router similar to next.js where each HTTP route is a separate file on disk, and the file's path governs the HTTP path pattern that runs the code in the file. For example, you might have this folder structure:

```
src/
  routes/
    GET-home.ts
    GET-posts.ts
    POST-posts.ts
    posts/
      GET-[id].ts
```

This would register routes for `GET /home`, `GET /posts`, `POST /posts`, and `GET /posts/:id` with Fastify. Starscream uses this convention so that it's always easy to find the file that powers a given route in a project, and also so that routes can be lazily required, improving boot and reboot performance in development. A route file is a file which exports an async route handler function like this:

```typescript
import { StarscreamRoute } from "@starscream/core";

const route: StarscreamRoute = async (request, reply) => {
  await reply.send({ pong: request.ip });
};

export default route;
```

If you want to add route options to a route like route hooks, route schema, or route constraints, you can do that by setting the `options` property on the route object like so:

```typescript
import { StarscreamRoute } from "@starscream/core";

const route: StarscreamRoute = async (request, reply) => {
  // some useful stuff
};

route.options = {
  preValidation: someAuthHook,
  constraints: { host: "one-specific-domain.com" },
  // and all the other fastify route options here: https://www.fastify.dev/docs/latest/Reference/Routes/#routes-option
};

export default route;
```

### File Based Fastify Plugins

Fastify has a [plugin encapsulation](https://www.fastify.dev/docs/v3.5.x/Plugins/) feature where registered hooks, decorators, or other required plugins apply only to the other stuff registered within that plugin and not outside it. This is really useful for isolation and composition -- if you want to run a hook for user authentication for example, you can add it in an outer plugin, and all routes added to that plugin or inner plugins will run the hook.

To make encapsulation easy and predictable, Starscream maps each folder of routes on the filesystem to a new plugin encapsulation context. So, hooks added to one folder of routes apply to all the routes within that folder, but not to routes in other folders.

Plugin files must start with a `+` character. This shows Starscream you mean for it to be a plugin file and not a route. Files that aren't routes and aren't plugins aren't allowed in the `src/routes` directory -- they can go anywhere else in `src` and be imported by routes or plugins just fine.

For example, if you had this folder structure:

```
src/
  routes/
    GET-home.ts
    GET-posts.ts
    +scope.ts
    posts/
      GET-[id].ts
      +scope.ts
```

Hooks or decorators defined in `src/routes/scope.ts` will apply to all routes, and then hooks or decorators defined in `src/routes/posts/+scope.ts` will only apply to routes `src/routes/posts`.

Plugins are one source file that exports an async function which is passed the `server` instance the plugin should work with. They look like this:

```typescript
import { StarscreamPlugin } from "@starscream/core";

const scope: StarscreamPlugin = async (server) => {
  server.decorateRequest("myCoolRequest", true);
};

export default scope;
```

Plugin files can do anything normal Fastify plugins can, and are eager loaded during development to ensure any setup they do is applied before routes start getting run. Plugins are the right place to register other plugins.

## Config

Starscream exports a `Config` object which has both starscream's configuration and your app's configuration on it. Set config in `starscream.config.ts` which will be auto required from the root of the `api` project. If you have a `starscream.config.local.ts` it will also be auto required and merged into the final `Config` object.

Handy config doodads:

- `Config.env` has an `EnvironmentName` object that has a `name: string` as well as `testLike()` or `productionLike()` interrogators
- `Config.app` has your app's configuration

Starscream also replaces TypeORM's config management facilities with it's own, so configure TypeORM with `Config.database`. To use secrets in production for the config, reference them in the `starscream.config.ts` file like you might any other environment variable with `process.env['SOME_SECRET_VAR']`.

## Utilities

### `inlineOperation`

Useful for booting up the application but not actually starting the server in order to do useful stuff, like seed the database or run a migration.

## Releasing

We release Starscream from the Gadget monorepo to the npm registry directly. To release:

1. Optionally, release `@starscream/utils` if necessary. Do this by:
   1. Bump the version manually in `packages/@starscream/utils/package.json`
   2. Run `pnpm -F=@starscream/utils publish --access=public`
   3. Bump the version dependency in `packages/@starscream/core/package.json` under `dependencies` (leave `devDependencies` alone)
2. Bump the version manually in `packages/@starscream/core/package.json`
3. Manually remove any `workspace:*` `devDependencies` from the `packages/@starscream/core/package.json`. These dependencies are incompatible with `npm` and it pukes if you don't remove them. This will create a dirty git diff that you should NOT commit.
4. Run `pnpm -F=@starscream/core publish --access=public`