npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nodeboot/encore-server

v1.1.0

Published

Node-Boot Encore.ts server package. It lets you run a NodeBoot application as an Encore.ts service by exposing a raw endpoint handler, with support for routing, middleware, and request handling.

Downloads

82

Readme

🧭 @nodeboot/encore-server – Encore.ts Server for Node-Boot

Overview

@nodeboot/encore-server lets you run a full Node-Boot application (controllers, DI, middleware, authorization, validation, OpenAPI, actuator, ...) as an Encore.ts service.

Encore.ts owns the actual HTTP listening socket and its own request lifecycle - it doesn't let a third-party framework listen() on a port. Instead, it exposes "raw endpoints" (api.raw), which behave just like a plain node:http request handler: (req: IncomingMessage, resp: ServerResponse) => void.

EncoreServer takes advantage of this: it boots Node-Boot against an internal find-my-way router (the same approach used by @nodeboot/http-server) and exposes a single getHandler() method with exactly that signature. You register it once as a catch-all api.raw endpoint, and every request handled by Encore.ts is routed into Node-Boot's engine - controllers, middlewares, authorization checks and all.

✨ Features

  • Boot a Node-Boot app inside an Encore.ts service using NodeBoot.run(EncoreServer)
  • Implements the @nodeboot/engine driver contract via EncoreDriver
  • Routing powered by find-my-way - a fast, radix-tree based router
  • Single catch-all raw endpoint forwards every request into Node-Boot, so all of your @Controllers keep working unchanged
  • Built-in JSON body parsing for POST/PUT/PATCH requests
  • Optional CORS handling through server configuration, including preflight (OPTIONS) requests
  • Cookie parsing for @CookieParam()/@CookieParams() decorators
  • Works with Node-Boot authorization and middleware pipelines, typed against native IncomingMessage/ServerResponse (the types Encore.ts passes to raw endpoints)
  • Request/response logging with timing, out of the box

🚀 Installation

pnpm add @nodeboot/core @nodeboot/context @nodeboot/encore-server @nodeboot/di typedi reflect-metadata encore.dev

find-my-way and cookie are bundled as direct dependencies of @nodeboot/encore-server. encore.dev is a peer dependency - install it as part of setting up your Encore.ts app (encore app create / encore run).

🔥 Usage

1️⃣ Bootstrap a Node-Boot app with EncoreServer

// app.ts
import "reflect-metadata";
import {Container} from "typedi";
import {NodeBoot, NodeBootApp, NodeBootApplication, NodeBootAppView} from "@nodeboot/core";
import {EncoreServer} from "@nodeboot/encore-server";
import {EnableDI} from "@nodeboot/di";
import {EnableComponentScan} from "@nodeboot/aot";

@EnableDI(Container)
@EnableComponentScan()
@NodeBootApplication()
export class SampleApp implements NodeBootApp {
    start(): Promise<NodeBootAppView> {
        return NodeBoot.run(EncoreServer);
    }
}

Unlike the other adapters, EncoreServer.listen() doesn't open a socket - Encore.ts's runtime already does that. It simply finishes wiring up Node-Boot's engine (controllers, middlewares, DI, OpenAPI, actuator, ...) against the internal router and publishes the usual application.started lifecycle event.

2️⃣ Define an encore.service.ts and a catch-all raw endpoint

Encore.ts requires every service directory to declare itself via encore.service.ts:

// encore.service.ts
import {Service} from "encore.dev/service";

export default new Service("api");

Then expose Node-Boot through a single catch-all raw endpoint using the fallback route syntax (/!path), so every request - regardless of path or method - is routed into Node-Boot's own router:

// api.ts
import {api} from "encore.dev/api";
import {EncoreServer} from "@nodeboot/encore-server";
import {SampleApp} from "./app";

// Reused across invocations of the same process. Only re-initialized on cold start.
let handler: ReturnType<EncoreServer["getHandler"]> | null = null;

export const apiHandler = api.raw({expose: true, method: "*", path: "/!path"}, async (req, resp) => {
    if (!handler) {
        const app = await new SampleApp().start();
        handler = (app.server as EncoreServer).getHandler();
    }
    return handler(req, resp);
});

3️⃣ Add controllers normally

Controllers remain completely framework-agnostic - the same controller code runs unchanged whether you're using Express, Fastify, Koa, the native HTTP adapter, or Encore.ts:

import {Controller, Get} from "@nodeboot/core";

@Controller("/hello", "v1")
export class HelloController {
    @Get("/")
    async hello(): Promise<string> {
        return "Hello, World!";
    }
}

Run it locally with encore run - requests to any path/method reach the apiHandler raw endpoint, which delegates to Node-Boot's router to find and execute the matching controller action.

⚙️ Server configuration

This package exports EncoreServerConfigs/EncoreServerConfigProperties. Currently, only cors is actively used by the driver (cookie, session, multipart, and template are part of the shared config shape but are not implemented by this adapter yet):

import {Bean, Configuration, SERVER_CONFIGURATIONS, SERVER_CONFIGURATIONS_PROPERTY_PATH} from "@nodeboot/core";
import {BeansContext} from "@nodeboot/context";
import {EncoreServerConfigProperties, EncoreServerConfigs} from "@nodeboot/encore-server";

@Configuration()
export class ServerConfiguration {
    @Bean(SERVER_CONFIGURATIONS)
    public serverConfig({config}: BeansContext): EncoreServerConfigs {
        const serverConfigs = config.getOptional<EncoreServerConfigProperties>(SERVER_CONFIGURATIONS_PROPERTY_PATH);

        return {
            cors: {options: serverConfigs?.cors},
        };
    }
}
server:
    cors:
        origin: "*"
        credentials: true

If no SERVER_CONFIGURATIONS bean is provided at all, the driver logs a warning and runs without CORS handling.

🔐 Authorization and current user integration

Because the raw endpoint handler works directly with Node's IncomingMessage/ServerResponse (the types Encore.ts passes to api.raw handlers), authorization checkers and current-user resolvers registered through @nodeboot/authorization must be typed against those native types:

import {Action, AuthorizationChecker} from "@nodeboot/context";
import {Component} from "@nodeboot/core";
import {Inject} from "@nodeboot/di";
import {Logger} from "winston";
import {IncomingMessage, ServerResponse} from "node:http";

@Component()
export class DefaultAuthorizationResolver implements AuthorizationChecker<IncomingMessage, ServerResponse> {
    @Inject()
    private logger: Logger;

    async check(_: Action<IncomingMessage, ServerResponse>, roles: string[]): Promise<boolean> {
        this.logger.info(`Checking authorization`);
        const user = {roles: ["USER", "ADMIN"]};
        if (user && !roles.length) return true;
        return user && roles.find(role => user.roles.indexOf(role) !== -1) !== undefined;
    }
}
import {Action, CurrentUserChecker} from "@nodeboot/context";
import {Component} from "@nodeboot/core";
import {Inject} from "@nodeboot/di";
import {Logger} from "winston";
import {IncomingMessage, ServerResponse} from "node:http";

@Component()
export class LoggedInUserResolver implements CurrentUserChecker<IncomingMessage, ServerResponse> {
    @Inject()
    private logger: Logger;

    async check(_action: Action<IncomingMessage, ServerResponse>): Promise<any> {
        this.logger.info(`Checking current logged in user`);
        return {id: 1, username: "exampleUser"};
    }
}

Wire both resolvers with @EnableAuthorization(LoggedInUserResolver, DefaultAuthorizationResolver) on your application class, exactly as with any other Node-Boot server adapter. The driver runs the authorization check for any action decorated with @Authorized(...), converting a failed check into an AuthorizationRequiredError (401) or AccessDeniedError (403).

🪝 Middleware behavior

Node-Boot middlewares (MiddlewareInterface) targeting this adapter should be typed against IncomingMessage/ServerResponse:

import {Middleware} from "@nodeboot/core";
import {MiddlewareInterface} from "@nodeboot/context";
import {IncomingMessage, ServerResponse} from "node:http";

@Middleware({type: "before"})
export class RequestLoggerMiddleware implements MiddlewareInterface<IncomingMessage, ServerResponse> {
    async use({request}: {request: IncomingMessage; response: ServerResponse}): Promise<void> {
        console.log(`${request.method} ${request.url}`);
    }
}
  • Global {type: "before"} middlewares run for every request whose URL starts with your configured route prefix, before route matching happens.
  • Global {type: "after"} middlewares run once the matched route handler has completed, unless the response was already ended (res.writableEnded/res.headersSent).
  • A custom ErrorHandlerInterface (an onError(error, action, actionMetadata) method) is detected automatically and used instead of the built-in global error handler for errors not already marked as handled.
  • Like the native HTTP adapter, this driver does not yet support controller/action-level @UseBefore/@UseAfter
    • global middlewares are the primary mechanism.

📨 Supported request parameter sources

The Encore.ts driver resolves Node-Boot action parameters, including:

  • @Body() / @BodyParam() - parsed from the raw request stream as JSON for POST/PUT/PATCH
  • @Param() / @Params() - from find-my-way route params
  • @QueryParam() / @QueryParams() - from parsed search params
  • @HeaderParam() / @HeaderParams()
  • @CookieParam() / @CookieParams() - parsed from the Cookie header via the cookie package

Session (@Session()/@SessionParam()) and file upload (@UploadedFile()/@UploadedFiles()) decorators are not yet implemented by this driver - use Express, Fastify, or Koa if your application needs those features.

⚠️ Important: Dependency Injection under Encore.ts's bundler

Encore.ts bundles your application with esbuild, which does not emit TypeScript's emitDecoratorMetadata reflection data (design:type, design:paramtypes, design:returntype) even when emitDecoratorMetadata: true is set in tsconfig.json. Since typedi (Node-Boot's default DI container) and Node-Boot's own parameter metadata rely on this reflection data to auto-infer types, a few patterns are required when running under Encore.ts:

  1. Always pass an explicit token/type to @Inject() for property injection.

    // ❌ Breaks under esbuild - throws CannotInjectValueError
    @Inject() private logger: Logger;
    
    // ✅ Works - explicit token bypasses type reflection
    @Inject("logger") private logger: Logger;
    @Inject(() => UserService) private userService: UserService;
  2. Avoid constructor injection - use property injection instead. Without design:paramtypes, typedi invokes constructors with no real arguments (regardless of per-parameter @Inject() decorators) and silently appends its internal container instance as an extra argument, producing confusing runtime errors such as this.userService.findAll is not a function.

    // ❌ Breaks under esbuild
    constructor(@Inject("logger") private logger: Logger) {}
    
    // ✅ Works
    @Inject("logger") private logger: Logger;
  3. Pass an explicit type to @Body() when you need validation/transformation. Node-Boot infers a body parameter's target type via the same reflection metadata to decide whether to run class-transformer/ class-validator; without it, validation is silently skipped.

    // ❌ Validation silently skipped under esbuild
    createUser(@Body() userData: CreateUserDto) {}
    
    // ✅ Works - explicit type restores validation
    createUser(@Body({type: CreateUserDto}) userData: CreateUserDto) {}

These caveats are specific to Encore.ts's esbuild-based bundler and do not apply to other Node-Boot server adapters (Express, Fastify, Koa, http) which run under ts-node/tsc and preserve full reflection metadata. See samples/sample-encore for a complete working example that follows these patterns.

🛠️ Runtime behavior

  • Requests are logged on entry and exit, including method, URL, remote address, user agent, and duration in milliseconds.
  • Responses support plain strings, JSON-serializable objects, Buffer, redirects, and custom status codes.
    • @Render(...) (template rendering) is accepted by the metadata but not implemented.
  • EncoreServer exposes getHttpServer() and getFramework() returning undefined (Encore.ts owns the HTTP server, not Node-Boot), and getRouter() returning the find-my-way router instance for lower-level access.
  • getHandler() returns the (req, resp) => Promise<void> function to register as your api.raw endpoint.

🔌 How this package relates to @nodeboot/core and @nodeboot/engine

EncoreServer extends Node-Boot's base server abstraction and is the class you pass to NodeBoot.run(EncoreServer). Under the hood, it provides EncoreDriver, which implements the NodeBootDriver contract from @nodeboot/engine - handling route registration, parameter resolution, authorization, middleware execution, and error handling - the same responsibilities every other Node-Boot server driver fulfills, adapted to Encore.ts's raw endpoint model instead of an owned HTTP listening socket.

✅ Summary

Use @nodeboot/encore-server when you want to build your service's business logic with the full Node-Boot programming model (decorators, DI, authorization, middleware, validation, OpenAPI) while deploying it on Encore.ts's infrastructure-as-code platform - local dev environment, tracing, and cloud provisioning included.

📄 License

MIT