egg Documentation

Born to build better enterprise frameworks and apps with Node.js & Koa.

18,990 stars TypeScript TypeScript 1 file ยท ~11,059 tokens #egg#eggjs#enterprise#framework#koa#koa-middleware#koa2#node-framework
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy egg on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name egg -p 6379:6379 --restart always redis:alpine
Configured Services & Ports
โš™๏ธ redis
image: redis:alpine
๐Ÿ”Œ Ports: 6379:6379
docker-compose.yml
yaml (docker-compose.yml)
name: eggjs_redis_dev

services:
  redis:
    image: redis:alpine
    ports:
      - 6379:6379

File: README.md

English | ็ฎ€ไฝ“ไธญๆ–‡








Features

  • Built-in Process Management
  • Plugin System
  • Framework Customization
  • Lots of plugins

Quickstart

Follow the commands listed below.

bash
$ corepack enable utoo
$ mkdir showcase && cd showcase
$ ut create egg@beta
$ ut install
$ ut run dev
$ open http://localhost:7001

Node.js >= 22.18.0 required.

Monorepo Structure

This project is structured as a utoo monorepo with the following packages:

  • packages/egg - Main Eggjs framework
  • examples/helloworld-commonjs - CommonJS example application
  • examples/helloworld-typescript - TypeScript example application
  • site - Documentation website

The monorepo uses utoo catalog mode for centralized dependency management, ensuring consistent versions across all packages.

Development Commands

bash
# Install dependencies for all packages
ut install --from pnpm

# Build all packages
ut run build

# Test all packages
ut run test

# Run specific package commands
ut --filter=egg run test
ut --filter=@examples/helloworld-typescript run dev
ut --filter=site run dev

Local External Services

Some DAL, ORM, Redis, and ecosystem benchmark paths need local MySQL and Redis services. Start the repository-aligned Docker services before running those tests on a clean machine:

bash
ut run dev:services:start

This starts MySQL 8 and Redis 7, matching the main CI service versions, and creates the databases used by local DAL/ORM/e2e fixtures: test, apple, banana, test_runtime_datasource, test_runtime_dao, test_dal_plugin, test_dal_standalone, cnpmcore, and cnpmcore_unittest.

Useful commands:

bash
ut run dev:services:status
ut run dev:services:stop
ut run dev:services:reset

The default host ports are 127.0.0.1:3306 for MySQL and 127.0.0.1:6379 for Redis. If either port is already used, the start command stops before changing containers. Keep using the existing service if it is compatible with CI, or stop it and run the command again. You can change Docker host ports with EGG_DEV_SERVICES_MYSQL_PORT and EGG_DEV_SERVICES_REDIS_PORT; however, the full DAL/ORM/Redis local test path still expects the default host ports.

Image overrides are available for compatibility checks:

bash
EGG_DEV_SERVICES_MYSQL_IMAGE=mysql:5.7 ut run dev:services:start
EGG_DEV_SERVICES_REDIS_IMAGE=redis:7 ut run dev:services:start

Run ut run dev:services:reset before switching MySQL image families, for example between MySQL 8 and MySQL 5.7, because MySQL data directories are not downgrade-compatible across major versions.

Current hard-coded service assumptions:

  • Redis plugin fixtures under plugins/redis/test/fixtures/apps/**/config.* use 127.0.0.1:6379; skipped Redis plugin tests become runnable when that port is available.
  • Session Redis fixtures under plugins/session/test/fixtures/redis-session/config/config.default.js use 127.0.0.1:6379.
  • DAL runtime tests in tegg/core/dal-runtime/test/DataSource.test.ts and tegg/core/dal-runtime/test/DAO.test.ts use local MySQL on port 3306.
  • DAL module fixtures in tegg/plugin/dal/test/fixtures/apps/dal-app/modules/dal/module.yml and tegg/standalone/standalone/test/fixtures/dal-*/module.yml use local MySQL on port 3306.
  • ORM fixtures in tegg/plugin/orm/test/fixtures/prepare.js and tegg/plugin/orm/test/fixtures/apps/orm-app/config/config.default.ts use local MySQL on port 3306.

Documentations

Contributors

How to Contribute

Please let us know how can we help. Do check out issues for bug reports or suggestions first.

To become a contributor, please follow our contributing guide, and review the repository guidelines for day-to-day development tips.

Sponsors and Backers


License

MIT


File: benchmark/ci-test/README.md

CI Test Benchmark

ut run benchmark:ci-test runs the reusable CI test benchmark harness and writes a Markdown report, a JSON summary, and the raw Vitest JSON reporter output.

Environment Setup

  • Use Node.js >=22.18.0.
  • Make sure the Utoo CLI is available as ut. The CI workflow uses utooland/setup-utoo before dependency installation.
  • Install workspace dependencies from the repository root with ut install --from pnpm, matching the CI workflow.
  • Run commands from the repository root so workspace paths and vitest.config.ts defaults can be detected.
  • Keep Redis and MySQL available when benchmarking suites that require them. The CI test job uses Redis 7 on the default Redis port and MySQL 8 with a test database; the benchmark harness mirrors the CI Vitest flags but does not start external services.
  • For CI-like metadata, set the relevant environment variables before running the command, for example CI=1, GITHUB_SHA, RUNNER_OS, or worker-related VITEST_* variables.

Usage

sh
# Run the suite and benchmark it
ut run benchmark:ci-test
ut run benchmark:ci-test -- --coverage
ut run benchmark:ci-test -- --output-dir .tmp/ci-benchmark -- ut execute vitest run packages/extend2/test/index.test.ts

# Summarize an existing Vitest JSON without running tests (the CI path)
ut run benchmark:ci-test -- --report-only --vitest-json benchmark/ci-test/ci-run/vitest-results.json

The default output directory is benchmark/ci-test/<timestamp>. Use --output-dir for a deterministic path when collecting artifacts.

Parallelism metrics

Tests run with isolate: false (full parallelism; the tegg TeggScope per-app isolation keeps concurrent multi-app boots safe). The report reconstructs the concurrency timeline from each file's Vitest interval (min test start .. max test end) and reports:

  • Execution window โ€” max(end) - min(start), the wall-clock span of test execution.
  • Busy time โ€” sum of per-file spans (the area under the concurrency curve).
  • Avg concurrency โ€” busy time รท execution window, the time-weighted mean number of files running at once.
  • Peak concurrency โ€” the maximum number of files whose intervals overlap (sweep line). The robust headline signal.
  • Parallel efficiency โ€” avg concurrency รท worker ceiling (the ceiling mirrors vitest.config.ts: Windows CI caps workers, otherwise the machine's available parallelism).
  • Critical path โ€” the longest single-file span (the wall-clock floor with unlimited workers).

Interval caveat: the Vitest 4 JSON reporter derives a file's startTime/endTime from test-level timings only, so the span covers test bodies and per-test beforeEach/afterEach but excludes suite-level beforeAll/afterAll (where egg boots its apps โ€” often the dominant per-file cost) and module transform/import. That excluded time still occupies the worker threads, so avg concurrency and parallel efficiency are lower bounds on real worker utilization; for beforeAll-heavy suites avg can read below 1 while peak is high. Use peak concurrency as the primary signal. Fully-skipped files (no test timings) are dropped from the calculation.

CI integration

The test gating job in .github/workflows/ci.yml is instrumented without changing gate semantics:

  1. vitest.config.ts adds a json reporter when CI is set, writing benchmark/ci-test/ci-run/vitest-results.json during the gating ut run ci run.
  2. A Report parallelism metrics step (if: always()) runs the harness in --report-only mode against that JSON.
  3. When GITHUB_STEP_SUMMARY is set, the Markdown report (including the parallelism table) is appended to the GitHub Actions job summary, so the metrics are visible on the run page per OS/Node matrix entry.

Gating still comes entirely from ut run ci; the metrics step is informational and exits 0 even when the JSON is missing.

Outputs

  • report.md: human-readable benchmark report (run, environment, parameters, parallelism, long-tail tables).
  • report.json: structured report containing environment, command, wall time, Vitest summary, parallelism metrics, long-tail file/project durations, and coverage/worker/isolate parameters.
  • vitest-results.json: raw Vitest JSON reporter output.

File: packages/utils/README.md

@eggjs/utils

NPM version
npm download

Utils for all egg projects.

Installation

bash
npm i @eggjs/utils

API

`getPlugins(options)`

  • {String} baseDir - the current directory of application
  • {String} framework - the directory of framework
  • {String} env - egg environment

`getLoadUnits(options)`

  • {String} baseDir - the current directory of application
  • {String} framework - the directory of framework
  • {String} env - egg environment

`getConfig(options)`

  • {String} baseDir - the current directory of application
  • {String} framework - the directory of framework
  • {String} env - egg environment

`getFrameworkPath(options)`

  • {String} baseDir - the current directory of application
  • {String} framework - the directory of framework

`setBundleModuleLoader(loader)`

Register a module loader hook for bundled Egg apps. The hook runs before the
normal importModule() resolution path.

  • {Function | undefined} loader - a synchronous function that receives the
    original filepath argument passed to importModule() after POSIX separator
    normalization, or a virtual specifier. It does not receive the resolved
    absolute file path from importResolve(). Return undefined to fall back to
    the normal import path.

The bundle loader is stored on globalThis, so bundled and external copies of
@eggjs/utils share the same loader. Non-undefined results follow the same
default export unwrapping rules as importModule(), including
importDefaultOnly.

Bundle / snapshot module-loading hooks

importModule() resolves a module through the following hooks, in order. The
first one that produces a value wins; otherwise it falls back to the native
import() / require() path:

  1. globalThis.__EGG_BUNDLE_MODULE_LOADER__ โ€” set via
    setBundleModuleLoader(). Looks up a module already inlined into the bundle
    (typically a static bundle map emitted by egg-bundler). Runs before on-disk
    resolution and receives the POSIX-normalized importModule() filepath or a
    virtual specifier. Return undefined to fall through.
  2. Snapshot module loader โ€” set via setSnapshotModuleLoader(). This is a
    module-local hook (not a globalThis global) used by the V8 snapshot entry
    generator to serve pre-bundled modules synchronously, keyed by the resolved
    path. Once registered it handles every load that reaches it, so the importer
    below is not consulted while it is active.
  3. globalThis.__EGG_MODULE_IMPORTER__ โ€” an async (or sync, since the value
    is awaited) importer that receives the resolved file path (the
    importResolve() result, with OS-native separators โ€” not normalized). When
    set, and the two hooks above did not resolve the module, it replaces the
    native await import(filePath).

The bundle loader and importer globals are typed in @eggjs/typings
(BundleModuleLoader / ModuleImporter); import @eggjs/typings/global to pick
up the declare global augmentation. These hooks are the contract that
egg-bundler's generated entry relies on. @eggjs/core's ManifestLoaderFS
consults __EGG_BUNDLE_MODULE_LOADER__ directly; its importer/native fallback is
reached through @eggjs/loader-fs, which calls back into importModule(). The
tegg loader (LoaderUtil.loadFile) consults both globals directly, passing the
loader filepath with separators normalized to POSIX.

__EGG_MODULE_IMPORTER__ has two main uses:

  • Bundler-based test runners (e.g. Vitest): route module loading through the
    runner's own module graph so the loader and the test file share a single
    module instance (otherwise ctx.getEggObject(ClassRef) fails with
    "can not get proto").
  • V8 startup-snapshot restore: the deserialized main function runs without a
    host dynamic-import callback, so native import() throws. The snapshot entry
    installs a synchronous require()-based importer (createRequire() over the
    bundle output dir); require() can load ESM on Node >= 22, so modules resolve
    without dynamic import.

License

MIT

Contributors

Made with contributors-img.


File: packages/tsconfig/README.md

@eggjs/tsconfig

NPM version
npm download

Base tsconfig file for egg project

Install

shell
npm i --save-dev @eggjs/tsconfig

Usage

json
// tsconfig.json
{
  "extends": "@eggjs/tsconfig",
  // custom config
  "compilerOptions": {
    // override @eggjs/tsconfig options here
  }
}

License

MIT

Contributors

Made with contributors-img.


File: packages/supertest/README.md

@eggjs/supertest

NPM version

MIT License
npm download

HTTP assertions made easy via superagent. Maintained for Forward Email and Lad.
Forked for TypeScript friendly

Document see SuperTest

About

The motivation with this module is to provide a high-level abstraction for testing
HTTP, while still allowing you to drop down to the lower-level API provided by superagent.

Getting Started

Install SuperTest as an npm module and save it to your package.json file as a development dependency:

bash
npm install @eggjs/supertest --save-dev

Once installed it can now be referenced by simply calling require('supertest');

Example

You may pass an http.Server, or a Function to request() - if the server is not
already listening for connections then it is bound to an ephemeral port for you so
there is no need to keep track of ports.

SuperTest works with any test framework, here is an example without using any
test framework at all:

js
const { request } = require('@eggjs/supertest');
const express = require('express');

const app = express();

app.get('/user', function (req, res) {
  res.status(200).json({ name: 'john' });
});

request(app)
  .get('/user')
  .expect('Content-Type', /json/)
  .expect('Content-Length', '15')
  .expect(200)
  .end(function (err, res) {
    if (err) throw err;
  });

To enable http2 protocol, simply append an options to request or request.agent:

js
const { request } = require('@eggjs/supertest');
const express = require('express');

const app = express();

app.get('/user', function (req, res) {
  res.status(200).json({ name: 'john' });
});

request(app, { http2: true })
  .get('/user')
  .expect('Content-Type', /json/)
  .expect('Content-Length', '15')
  .expect(200)
  .end(function (err, res) {
    if (err) throw err;
  });

request
  .agent(app, { http2: true })
  .get('/user')
  .expect('Content-Type', /json/)
  .expect('Content-Length', '15')
  .expect(200)
  .end(function (err, res) {
    if (err) throw err;
  });

Here's an example with mocha, note how you can pass done straight to any of the .expect() calls:

js
describe('GET /user', function () {
  it('responds with json', function (done) {
    request(app).get('/user').set('Accept', 'application/json').expect('Content-Type', /json/).expect(200, done);
  });
});

You can use auth method to pass HTTP username and password in the same way as in the superagent:

js
describe('GET /user', function () {
  it('responds with json', function (done) {
    request(app)
      .get('/user')
      .auth('username', 'password')
      .set('Accept', 'application/json')
      .expect('Content-Type', /json/)
      .expect(200, done);
  });
});

One thing to note with the above statement is that superagent now sends any HTTP
error (anything other than a 2XX response code) to the callback as the first argument if
you do not add a status code expect (i.e. .expect(302)).

If you are using the .end() method .expect() assertions that fail will
not throw - they will return the assertion as an error to the .end() callback. In
order to fail the test case, you will need to rethrow or pass err to done(), as follows:

js
describe('POST /users', function () {
  it('responds with json', function (done) {
    request(app)
      .post('/users')
      .send({ name: 'john' })
      .set('Accept', 'application/json')
      .expect('Content-Type', /json/)
      .expect(200)
      .end(function (err, res) {
        if (err) return done(err);
        return done();
      });
  });
});

You can also use promises:

js
describe('GET /users', function () {
  it('responds with json', function () {
    return request(app)
      .get('/users')
      .set('Accept', 'application/json')
      .expect('Content-Type', /json/)
      .expect(200)
      .then((response) => {
        expect(response.body.email).toEqual('[email protected]');
      });
  });
});

Or async/await syntax:

js
describe('GET /users', function () {
  it('responds with json', async function () {
    const response = await request(app).get('/users').set('Accept', 'application/json');
    expect(response.headers['Content-Type']).toMatch(/json/);
    expect(response.status).toEqual(200);
    expect(response.body.email).toEqual('[email protected]');
  });
});

Expectations are run in the order of definition. This characteristic can be used
to modify the response body or headers before executing an assertion.

js
describe('POST /user', function () {
  it('user.name should be an case-insensitive match for "john"', function (done) {
    request(app)
      .post('/user')
      .send('name=john') // x-www-form-urlencoded upload
      .set('Accept', 'application/json')
      .expect(function (res) {
        res.body.id = 'some fixed id';
        res.body.name = res.body.name.toLowerCase();
      })
      .expect(
        200,
        {
          id: 'some fixed id',
          name: 'john',
        },
        done,
      );
  });
});

Anything you can do with superagent, you can do with supertest - for example multipart file uploads!

js
request(app)
  .post('/')
  .field('name', 'my awesome avatar')
  .field('complex_object', '{"attribute": "value"}', {contentType: 'application/json'})
  .attach('avatar', 'test/fixtures/avatar.jpg')
  ...

Passing the app or url each time is not necessary, if you're testing
the same host you may simply re-assign the request variable with the
initialization app or url, a new Test is created per request.VERB() call.

js
t = request('http://localhost:5555');

t.get('/').expect(200, function (err) {
  console.log(err);
});

t.get('/').expect('heya', function (err) {
  console.log(err);
});

Here's an example with mocha that shows how to persist a request and its cookies:

js
const { agent } = require('@eggjs/supertest');
const should = require('should');
const express = require('express');
const cookieParser = require('cookie-parser');

describe('request.agent(app)', function () {
  const app = express();
  app.use(cookieParser());

  app.get('/', function (req, res) {
    res.cookie('cookie', 'hey');
    res.send();
  });

  app.get('/return', function (req, res) {
    if (req.cookies.cookie) res.send(req.cookies.cookie);
    else res.send(':(');
  });

  const testAgent = agent(app);

  it('should save cookies', function (done) {
    testAgent.get('/').expect('set-cookie', 'cookie=hey; Path=/', done);
  });

  it('should send cookies', function (done) {
    testAgent.get('/return').expect('hey', done);
  });
});

There is another example that is introduced by the file agency.js

Here is an example where 2 cookies are set on the request.

js
agent(app)
  .get('/api/content')
  .set('Cookie', ['nameOne=valueOne;nameTwo=valueTwo'])
  .send()
  .expect(200)
  .end((err, res) => {
    if (err) {
      return done(err);
    }
    expect(res.text).to.be.equal('hey');
    return done();
  });

API

You may use any superagent methods,
including .write(), .pipe() etc and perform assertions in the .end() callback
for lower-level needs.

.expect(status[, fn])

Assert response status code.

.expect(status, body[, fn])

Assert response status code and body.

.expect(body[, fn])

Assert response body text with a string, regular expression, or
parsed body object.

.expect(field, value[, fn])

Assert header field value with a string or regular expression.

.expect(function(res) {})

Pass a custom assertion function. It'll be given the response object to check. If the check fails, throw an error.

js
request(app).get('/').expect(hasPreviousAndNextKeys).end(done);

function hasPreviousAndNextKeys(res) {
  if (!('next' in res.body)) throw new Error('missing next key');
  if (!('prev' in res.body)) throw new Error('missing prev key');
}

.end(fn)

Perform the request and invoke fn(err, res).

Notes

Inspired by api-easy minus vows coupling.

License

MIT

Contributors

Made with contributors-img.


File: packages/router/README.md

@eggjs/router





Router core component for Egg.js.

This repository is a fork of koa-router. with some additional features.
And thanks for the great work of @alexmingoia and the original team.

API Reference

Router โ

Kind: Exported class

new Router([opts])

Create a new router.

Param Type Description
[opts] Object
[opts.prefix] String prefix router paths

Example
Basic usage:

ts
import Koa from '@eggjs/koa';
import Router from '@eggjs/router';

const app = new Koa();
const router = new Router();

router.get('/', async (ctx, next) => {
  // ctx.router available
});

app.use(router.routes()).use(router.allowedMethods());

router.get|put|post|patch|delete|del โ‡’ `Router`

Create router.verb() methods, where verb is one of the HTTP verbs such
as router.get() or router.post().

Match URL patterns to callback functions or controller actions using router.verb(),
where verb is one of the HTTP verbs such as router.get() or router.post().

Additionaly, router.all() can be used to match against all methods.

ts
router
  .get('/', (ctx, next) => {
    ctx.body = 'Hello World!';
  })
  .post('/users', (ctx, next) => {
    // ...
  })
  .put('/users/:id', (ctx, next) => {
    // ...
  })
  .del('/users/:id', (ctx, next) => {
    // ...
  })
  .all('/users/:id', (ctx, next) => {
    // ...
  });

When a route is matched, its path is available at ctx.routePath and if named,
the name is available at ctx.routeName

Route paths will be translated to regular expressions using
path-to-regexp.

Query strings will not be considered when matching requests.

Named routes

Routes can optionally have names. This allows generation of URLs and easy
renaming of URLs during development.

ts
router.get('user', '/users/:id', (ctx, next) => {
  // ...
});

router.url('user', 3);
// => "/users/3"

Multiple middleware

Multiple middleware may be given:

ts
router.get(
  '/users/:id',
  (ctx, next) => {
    return User.findOne(ctx.params.id).then(function (user) {
      ctx.user = user;
      next();
    });
  },
  (ctx) => {
    console.log(ctx.user);
    // => { id: 17, name: "Alex" }
  },
);

Nested routers

Nesting routers is supported:

ts
const forums = new Router();
const posts = new Router();

posts.get('/', (ctx, next) => {...});
posts.get('/:pid', (ctx, next) => {...});
forums.use('/forums/:fid/posts', posts.routes(), posts.allowedMethods());

// responds to "/forums/123/posts" and "/forums/123/posts/123"
app.use(forums.routes());

Router prefixes

Route paths can be prefixed at the router level:

ts
const router = new Router({
  prefix: '/users'
});

router.get('/', ...); // responds to "/users"
router.get('/:id', ...); // responds to "/users/:id"

URL parameters

Named route parameters are captured and added to ctx.params.

ts
router.get('/:category/:title', (ctx, next) => {
  console.log(ctx.params);
  // => { category: 'programming', title: 'how-to-node' }
});

The path-to-regexp module is
used to convert paths to regular expressions.

Kind: instance property of [Router](#exp_module_egg-router--Router)

Param Type Description
path String
[middleware] function route middleware(s)
callback function route callback

router.routes โ‡’ `function`

Returns router middleware which dispatches a route matching the request.

Kind: instance property of [Router](#exp_module_egg-router--Router)

router.use([path], middleware) โ‡’ `Router`

Use given middleware.

Middleware run in the order they are defined by .use(). They are invoked
sequentially, requests start at the first middleware and work their way
"down" the middleware stack.

Kind: instance method of [Router](#exp_module_egg-router--Router)

Param Type
[path] String
middleware function
[...] function

Example

ts
// session middleware will run before authorize
router.use(session()).use(authorize());

// use middleware only with given path
router.use('/users', userAuth());

// or with an array of paths
router.use(['/users', '/admin'], userAuth());

app.use(router.routes());

router.prefix(prefix) โ‡’ `Router`

Set the path prefix for a Router instance that was already initialized.

Kind: instance method of [Router](#exp_module_egg-router--Router)

Param Type
prefix String

Example

ts
router.prefix('/things/:thing_id');

router.allowedMethods([options]) โ‡’ `function`

Returns separate middleware for responding to OPTIONS requests with
an Allow header containing the allowed methods, as well as responding
with 405 Method Not Allowed and 501 Not Implemented as appropriate.

Kind: instance method of [Router](#exp_module_egg-router--Router)

Param Type Description
[options] Object
[options.throw] Boolean throw error instead of setting status and header
[options.notImplemented] function throw the returned value in place of the default NotImplemented error
[options.methodNotAllowed] function throw the returned value in place of the default MethodNotAllowed error

Example

ts
import Koa from '@eggjs/koa';
import Router from '@eggjs/router';

const app = new Koa();
const router = new Router();

app.use(router.routes());
app.use(router.allowedMethods());

Example with Boom

ts
import Koa from '@eggjs/koa';
import Router from '@eggjs/router';
import Boom from 'boom';

const app = new Koa();
const router = new Router();

app.use(router.routes());
app.use(
  router.allowedMethods({
    throw: true,
    notImplemented: () => new Boom.notImplemented(),
    methodNotAllowed: () => new Boom.methodNotAllowed(),
  }),
);

router.redirect(source, destination, [code]) โ‡’ `Router`

Redirect source to destination URL with optional 30x status code.

Both source and destination can be route names.

javascript
router.redirect('/login', 'sign-in');

This is equivalent to:

ts
router.all('/login', (ctx) => {
  ctx.redirect('/sign-in');
  ctx.status = 301;
});

Kind: instance method of [Router](#exp_module_egg-router--Router)

Param Type Description
source String URL or route name.
destination String URL or route name.
[code] Number HTTP status code (default: 301).

router.route(name) โ‡’ `Layer` | `false`

Lookup route with given name.

Kind: instance method of [Router](#exp_module_egg-router--Router)

Param Type
name String

router.url(name, params, [options]) โ‡’ `String` | `Error`

Generate URL for route. Takes a route name and map of named params.

Kind: instance method of [Router](#exp_module_egg-router--Router)

Param Type Description
name String route name
params Object url parameters
[options] Object options parameter
[options.query] Object | String query options

Example

ts
router.get('user', '/users/:id', (ctx, next) => {
  // ...
});

router.url('user', 3);
// => "/users/3"

router.url('user', { id: 3 });
// => "/users/3"

router.use((ctx, next) => {
  // redirect to named route
  ctx.redirect(ctx.router.url('sign-in'));
});

router.url('user', { id: 3 }, { query: { limit: 1 } });
// => "/users/3?limit=1"

router.url('user', { id: 3 }, { query: 'limit=1' });
// => "/users/3?limit=1"

router.param(param, middleware) โ‡’ `Router`

Run middleware for named route parameters. Useful for auto-loading or
validation.

Kind: instance method of [Router](#exp_module_egg-router--Router)

Param Type
param String
middleware function

Example

ts
router
  .param('user', (id, ctx, next) => {
    ctx.user = users[id];
    if (!ctx.user) return (ctx.status = 404);
    return next();
  })
  .get('/users/:user', (ctx) => {
    ctx.body = ctx.user;
  })
  .get('/users/:user/friends', (ctx) => {
    return ctx.user.getFriends().then(function (friends) {
      ctx.body = friends;
    });
  });
// /users/3 => {"id": 3, "name": "Alex"}
// /users/3/friends => [{"id": 4, "name": "TJ"}]

Router.url(path, params [, options]) โ‡’ `String`

Generate URL from url pattern and given params.

Kind: static method of [Router](#exp_module_egg-router--Router)

Param Type Description
path String url pattern
params Object url parameters
[options] Object options parameter
[options.query] Object | String query options

Example

ts
const url = Router.url('/users/:id', { id: 1 });
// => "/users/1"

const url = Router.url('/users/:id', { id: 1 }, { query: { active: true } });
// => "/users/1?active=true"

Tests

Run tests using npm test.

Breaking changes on v3

  • Drop generator function support
  • Drop Node.js < 18.19.0 support

License

MIT

Contributors

Made with contributors-img.


File: packages/path-matching/README.md

@eggjs/path-matching

NPM version
Known Vulnerabilities
npm download

Installation

bash
npm install @eggjs/path-matching

Usage

ts
import { pathMatching } from '@eggjs/path-matching';

const options = {
  ignore: '/api', // string will use parsed by path-to-regexp
  // support regexp
  ignore: /^\/api/,
  // support function
  ignore: (ctx) => ctx.path.startsWith('/api'),
  // support Array
  ignore: [(ctx) => ctx.path.startsWith('/api'), /^\/foo$/, '/bar'],
  // support match or ignore
  match: '/api',
  // custom path-to-regexp module, default is `path-to-regexp@6`
  // pathToRegexpModule: customPathToRegexp,
};

const match = pathMatching(options);
assert.equal(match({ path: '/api' }), true);
assert.equal(match({ path: '/api/hello' }), true);
assert.equal(match({ path: '/api' }), true);

options

  • match {String | RegExp | Function | Array} - if request path hit options.match, will return true, otherwise will return false.
  • ignore {String | RegExp | Function | Array} - if request path hit options.ignore, will return false, otherwise will return true.
  • pathToRegexpModule {Object | Function} - custom path-to-regexp module. Default is path-to-regexp@6. Supports both path-to-regexp@6 and path-to-regexp@8+ formats.

ignore and match can not both be presented.
and if neither ignore nor match presented, the new function will always return true.

License

MIT

Contributors

Made with contributors-img.


File: packages/loader-fs/README.md

@eggjs/loader-fs

Loader-facing filesystem abstraction for Egg loaders and bundled runtimes.

Usage

ts
import { RealLoaderFS, type LoaderFS } from '@eggjs/loader-fs';

const loaderFS: LoaderFS = new RealLoaderFS();

LoaderFS intentionally covers only the operations required by loader code:
exists, stat, realpath, readJSON, glob, and loadFile. A source with
an authoritative, precomputed directory view may also implement
getKnownFiles; undefined means discovery should fall back to glob, while
an empty array means the directory is authoritatively empty.


File: packages/koa-static-cache/README.md

eggjs/koa-static-cache

NPM version
Known Vulnerabilities
npm download

Static cache middleware for koa.

Differences between this library and other libraries such as static:

  • There is no directory or index.html support.
  • You may optionally store the data in memory - it streams by default.
  • Caches the assets on initialization - you need to restart the process to update the assets.(can turn off with options.preload = false)
  • Uses MD5 hash sum as an ETag.
  • Uses .gz files if present on disk, like nginx gzip_static module

Forked from https://github.com/koajs/static-cache, refactor with TypeScript to support CommonJS and ESM both.

Installation

bash
npm install @eggjs/koa-static-cache

API

staticCache([options])

js
const path = require('path');
const { staticCache } = require('@eggjs/koa-static-cache');

app.use(
  staticCache(path.join(__dirname, 'public'), {
    maxAge: 365 * 24 * 60 * 60,
  }),
);
  • options.dir (str) - the directory you wish to serve, default to process.cwd.
  • options.maxAge (int) - cache control max age for the files, 0 by default.
  • options.cacheControl (str) - optional cache control header. Overrides options.maxAge.
  • options.buffer (bool) - store the files in memory instead of streaming from the filesystem on each request.
  • options.gzip (bool) - when request's accept-encoding include gzip, files will compressed by gzip.
  • options.usePrecompiledGzip (bool) - try use gzip files, loaded from disk, like nginx gzip_static
  • options.alias (obj) - object map of aliases. See below.
  • options.prefix (str) - the url prefix you wish to add, default to ''.
  • options.dynamic (bool) - dynamic load file which not cached on initialization.
  • options.filter (function | array) - filter files at init dir, for example - skip non build (source) files. If array set - allow only listed files
  • options.preload (bool) - caches the assets on initialization or not, default to true. always work together with options.dynamic.
  • options.files (obj) - optional files object. See below.

Aliases

For example, if you have this alias object:

js
const options = {
  alias: {
    '/favicon.png': '/favicon-32.png',
  },
};

Then requests to /favicon.png will actually return /favicon-32.png without redirects or anything.
This is particularly important when serving favicons as you don't want to store duplicate images.

Files

You can pass in an optional files object.
This allows you to do two things:

Combining directories into a single middleware

Instead of doing:

js
app.use(staticCache('/public/js'));
app.use(staticCache('/public/css'));

You can do this:

js
const files = {};

// Mount the middleware
app.use(staticCache('/public/js', {}, files));

// Add additional files
staticCache('/public/css', {}, files);

The benefit is that you'll have one less function added to the stack as well as doing one hash lookup instead of two.

Editing the files object

For example, if you want to change the max age of /package.json, you can do the following:

js
const files = {};

app.use(
  staticCache(
    '/public',
    {
      maxAge: 60 * 60 * 24 * 365,
    },
    files,
  ),
);

files['/package.json'].maxAge = 60 * 60 * 24 * 30;

Using a LRU cache to avoid OOM when dynamic mode enabled

You can pass in a lru cache instance which has tow methods: get(key) and set(key, value).

js
const LRU = require('lru-cache');
const files = new LRU({ max: 1000 });

app.use(
  staticCache({
    dir: '/public',
    dynamic: true,
    files,
  }),
);

License

MIT

Contributors

Made with contributors-img.