A Node.js Web API Framework WITHOUT Express
Midori is an opinionated Web API Framework designed for Node.js, using Node.js's native HTTP module as basis, built with TypeScript and being inspired from PSR standards.
- Router
- Basic Middlewares
- Request and Response
- Error Handling
- Service Providers
- Config Providers
- Logger
- Console
- File
- Task Scheduler
- JWT
- JWS
- JWE
- JWK
- CORS
- Content Security Policy
- Static Files
- Response Compression using
node:zlib
module- Gzip
- Deflate
- Brotli
- Extensible Body Parser and Response Serializer
- JSON+BigInt
- CSV
- Form
- Multipart
- Streams
- Hashing using native Node.js
node:crypto
module- PBKDF2
- Scrypt
- SHA-256
- SHA-512
- Problem Details
- Request Validation
- Auth
- Basic
- Bearer
- Tests
- JWE
- JWS
- Documentation
- Rate Limiting
npm install modscleo4/midori
See HTTP Bin for an example project.
// Import the node:http wrapper
import { Server } from 'midori/app';
// Import the HTTP utilities
import { Request, Response } from 'midori/http';
// Create the Server
const server = new Server();
// Install a basic Middleware
server.pipe(async (req: Request, next: (req: Request) => Promise<Response>): Promise<Response> => {
// Return a response for all requests
return Response.json({
message: 'Hello World!'
});
});
// Start the server
server.listen(8080);
// Import the node:http wrapper
import { Server } from 'midori/app';
// Import the HTTP utilities
import { Handler, Request, Response } from 'midori/http';
// Import the Route creator utility
import { Router } from 'midori/router';
// Import the required middlewares to support Router in the pipeline
import { RouterMiddleware, DispatchMiddleware, NotFoundMiddleware } from 'midori/middlewares';
// Import the Router Service Provider factory
import { RouterServiceProviderFactory } from 'midori/providers';
// Create a Router instance
const router = new Router();
// Create a GET route
router.get('/', async (req: Request): Promise<Response> => {
// Return a response
return Response.json({
message: 'Hello World!'
});
});
// Create the Server
const server = new Server();
// Install the Router Service Provider with the Router instance
server.install(RouterServiceProviderFactory(router));
// Install the middlewares
server.pipe(RouterMiddleware);
server.pipe(DispatchMiddleware);
server.pipe(NotFoundMiddleware);
// Start the server
server.listen(8080);
The framework is designed to be as simple as possible, while still being powerful and flexible. As such, it is distributed in the following modules:
The App module is the main module of the framework. It is responsible for bootstrapping the application, loading service providers, config providers, middlewares, routes and starting the server.
ConfigProvider
- Abstract class designed for config providers. Install on the app usingapp.configure(DerivedConfigProvider)
and retrieve usingapp.config.get(DerivedConfigProvider)
.ServiceProvider
- Abstract class designed for service providers. Install on the app usingapp.install(DerivedServiceProvider)
and retrieve usingapp.service.get(DerivedServiceProvider)
.Server
- Midori's implementation of HTTP server using Node.js's native HTTP module. It is responsible for listening to requests and sending responses, as well as being the container for Configurations and Services installed on the app usingapp.configure
andapp.install
respectively.
The Auth module is responsible for handling authentication and authorization.
Auth
- Base Auth Service class using UserService to authenticate and authorize users, storing the user in the request Container.User
- Base User class used by Auth Service.UserService
- Abstract User Service class used by Auth Service responsible for fetching users from the source.
The Errors module is responsible for handling errors.
AuthError
- Base class for authentication/authorization errors.HTTPError
- Base class for HTTP errors (any 4xx or 5xx response). It is designed to be caught and treated byHTTPErrorMiddleware
.JWTError
- Base class for JWT errors, thrown byJWT
.UnknownServiceError
- Error thrown when a service is not found in the Service Container.
The Hash module is responsible for generating secure hashes and verifying them.
Hash
- Abstract Hash Service class responsible for generating and verifying hashes. UseHash.hash
to generate a hash andHash.verify
to verify a hash.
import { Scrypt } from 'midori/hash'; // Or any other Hash Service you want to use
const hash = await Scrypt.hash('password'); // Can also pass a Buffer
const isTheSame = await Scrypt.verify(hash, 'password');
PBKDF2
- Hash Service class using PBKDF2 algorithm.Scrypt
- Hash Service class using Scrypt algorithm.SHA256
- Hash Service class using SHA-256 algorithm.SHA512
- Hash Service class using SHA-512 algorithm.
The HTTP module is responsible for handling HTTP requests and responses.
EStatusCode
- Enum for all HTTP status codes.Handler
- Abstract class for all handlers. You can also implement a handler as a function.
import { Application } from 'midori/app';
import { Request, Response } from 'midori/http';
const handler = async (
req: Request,
app: Application
): Promise<Response> {
// ...
}
Middleware
- Abstract class for all middlewares. You can also implement a middleware as a function.
import { Application } from 'midori/app';
import { Request, Response } from 'midori/http';
const middleware = async (
req: Request,
next: (req: Request) => Promise<Response>,
app: Application
): Promise<Response> {
// ...
}
Request
- Class representing an HTTP request, extending Node.js'sIncomingMessage
.Response
- Class representing an HTTP response. It does NOT extend Node.js'sServerResponse
, as it is designed to be sent only at the end of the pipeline.
The JWT module is responsible for handling JSON Web Tokens.
JWT
- Class responsible for signing and verifying (JWS), encrypting and decrypting (JWE).
The Log module is responsible for logging.
Logger
- Abstract class for all loggers.ConsoleLogger
- Logger class usingconsole.log
andconsole.error
.FileLogger
- Logger class using Node.js's nativenode:fs/promises
module.
The Middlewares module is responsible for providing basic middlewares to be used by the application.
AuthMiddleware
- Middleware to check if the user is authenticated. It uses theAuth
service installed on the app.AuthBasicMiddleware
- Middleware for Basic Authentication.AuthBearerMiddleware
- Middleware for Bearer Authentication.ContentLengthMiddleware
- Middleware to send the Content-Length header to the client. It is automatically installed on the app as the first middleware.ContentSecurityPolicyMiddleware
- Middleware for Content Security Policy. Can be configured usingapp.configure(ContentSecurityPolicyConfigProviderFactory({ ... }))
.CORSMiddleware
- Middleware for Cross-Origin Resource Sharing. Can be configured usingapp.configure(CORSConfigProviderFactory({ ... }))
.DispatchMiddleware
- Middleware to dispatch the request to the handler. It uses theRouter
to find the handler for the request.ErrorLoggerMiddleware
- Middleware to log errors thrown anywhere in the pipeline using theLogger
installed on the app.ErrorMiddleware
- Middleware to handle any errors thrown anywhere in the pipeline. It should be one of the first middlewares installed on the app. Can be configured usingapp.configure(ErrorConfigProviderFactory({ ... }))
.HTTPErrorMiddleware
- Middleware to handleHTTPError
s thrown anywhere in the pipeline.ImplicitHeadMiddleware
- Middleware to handle implicit HEAD requests, returning the same response as the corresponding GET request but without the body.ImplicitOptionsMiddleware
- Middleware to handle implicit OPTIONS requests, returning the allowed methods for the requested path using theRouter
.MethodNotAllowedMiddleware
- Middleware to handle requests with matching path but not matching method, returning a405 Method Not Allowed
response.NotFoundMiddleware
- Middleware intended to be the last middleware in the pipeline, returning a404 Not Found
response.ParseBodyMiddleware
- Middleware to parse the request body based on theContent-Type
header.PublicPathMiddleware
- Middleware to serve static files from a public path. Can be configured usingapp.configure(PublicPathConfigProviderFactory({ ... }))
.RequestLoggerMiddleware
- Middleware to log requests using theLogger
installed on the app.ResponseCompressionMiddleware
- Middleware to compress responses usingnode:zlib
module. Can be configured usingapp.configure(ResponseConfigProviderFactory({ compression: { ... } }))
.RouterMiddleware
- Middleware to find the handler for the request using theRouter
.ValidationMiddleware
- Abstract class for validation middlewares. Thevalidate
method is internally used to validate an object against aValidationRules
object. Define yourValidationRules
object by extending theValidationMiddleware.rules
property.
import { ValidationMiddleware } from 'midori/middlewares';
import { ValidationRules } from 'midori/util/validation.js';
class MyValidationMiddleware extends ValidationMiddleware {
override get rules(): ValidationRules {
return {
fieldA: {
type: 'string',
required: true
},
fieldB: {
type: 'number',
required: false,
min: 15,
},
fieldC: {
type: 'boolean',
required: false,
},
fieldD: {
type: 'array',
required: false,
all: {
type: 'string',
}
},
fieldE: {
type: 'object',
required: false,
properties: {
fieldA: {
type: 'string',
required: true
},
}
}
};
}
}
The Providers module is responsible for providing basic providers to be used by the application.
AuthServiceProvider
- Service Provider forAuth
service.ContentSecurityPolicyConfigProvider
- Config Provider forContentSecurityPolicyMiddleware
.CORSConfigProvider
- Config Provider forCORSMiddleware
.ErrorConfigProvider
- Config Provider forErrorMiddleware
.HashServiceProvider
- Service Provider forHash
service.JWTConfigProvider
- Config Provider forJWT
.JWTServiceProvider
- Service Provider forJWT
.LoggerServiceProvider
- Service Provider forLogger
service.PublicPathConfigProvider
- Config Provider forPublicPathMiddleware
.RequestConfigProvider
- Config Provider forRequest
related services.ResponseConfigProvider
- Config Provider forResponse
related services.RouterServiceProvider
- Service Provider forRouter
service.UserServiceProvider
- Service Provider forUserService
service.
The Router module is responsible for routing requests to handlers.
Route
- Class representing a route, containing the path, method, handler and middlewares.Router
- Class responsible for storing routes and finding the correct handler for a request.
The Scheduler module is responsible for scheduling tasks.
Task
- Abstract class for all tasks. You can also implement a task as a function.
import { Application } from 'midori/app';
const task = async (app: Application): Promise<void> {
// ...
}
Install on the app using app.schedule(cronString, DerivedTask)
. cronString
is a string representing the schedule of the task using the cron format WITH seconds. For example, * * * * * *
will run the task every second.
The Util module is responsible for providing utility functions used by the framework.