https://github.com/andywer/srv
📡 Functional node server. Composable routing. Take a request, return a response.
https://github.com/andywer/srv
Last synced: 8 months ago
JSON representation
📡 Functional node server. Composable routing. Take a request, return a response.
- Host: GitHub
- URL: https://github.com/andywer/srv
- Owner: andywer
- License: mit
- Created: 2019-03-21T19:17:39.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2023-01-23T22:24:14.000Z (over 3 years ago)
- Last Synced: 2025-06-24T11:44:13.020Z (about 1 year ago)
- Language: TypeScript
- Homepage:
- Size: 318 KB
- Stars: 9
- Watchers: 3
- Forks: 1
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
SRV
Node.js servers rethought: Functional, lean, performant.
What if we were to write [express](https://github.com/expressjs/express) from scratch in 2019…?
Would we use async functions and promises? Would we make it more functional? With TypeScript in mind?
Sure we would! So here we go.
🚀 **Built for ES2017+ & TypeScript**
🔌 **Functional - Take a request, return a response**
☝️ **Explicit - Clear static types**
🗜 **Almost no dependencies & less than 1000 lines of code**
⚠️ Status: Experimental ⚠️
## At a glance
```ts
import {
Response,
Route,
Router,
Service
} from "@andywer/srv"
const greet = Route.GET("/welcome", async (request) => {
const name = request.query.name
return Response.JSON({
name: "Greeting service",
welcome: name ? `Hello, ${name}!` : `Hi there!`
})
})
const service = Service(Router([ greet ]))
service.listen(8080)
.catch(console.error)
```
## Documentation
Find some documentation and sample code here. Work in progress right now.
* [Routing](./docs/routing.md)
* [Middleware](./docs/middleware.md)
## Features
Async functions
No callbacks. Leverage modern day features instead for an optimal developer experience.
```ts
import { Response, Route } from "@andywer/srv"
const greet = Route.GET("/health", async () => {
try {
const stats = await fetchHealthMetrics()
return Response.JSON({
operational: true,
stats
})
} catch (error) {
return Response.JSON(500, {
operational: false
})
}
})
```
Functional route handlers
Take a request, return a response. Lean, clean, easy to test and debug.
```ts
import { Response, Route, Router } from "@andywer/srv"
import { queryUserByID } from "./database/users"
const getUser = Route.GET("/user/:id", async request => {
const userID = request.params.id
const user = await queryUserByID(userID)
if (!user) {
return Response.JSON(404, {
message: `User ${userID} not found`
})
}
const headers = {
"Last-Modified": user.updated_at || user.created_at
}
return Response.JSON(200, headers, user)
})
export const router = Router([
getUser
])
```
No mutations
Stop passing data from middlewares to route handlers by dumping it in an untypeable `context`. Take the request object, extend it, pass it down to the route handler.
By applying middlewares in a direct and explicit manner, the passed requests and responses are completely type-safe, even if customized by middlewares.
```ts
import { Middleware, Request, RequestHandler, Service } from "@andywer/srv"
import { Logger } from "./logger"
export default function LoggingMiddleware(logger: Logger): Middleware {
return async (request: Request, next: RequestHandler) => {
const requestWithLogger = request.derive({
log: logger
})
// typeof requestWithLogger.log === Logger
return next(requestWithLogger)
}
}
```
```ts
import { composeMiddlewares, Service } from "@andywer/srv"
import logger from "./logger"
import router from "./routes"
const applyMiddlewares = composeMiddlewares(
LoggingMiddleware(logger),
SomeOtherMiddleware()
)
const service = Service(applyMiddlewares(router))
```
Everything is a function
The code base is relatively simple. Middlewares, routes and routers, they are all just implementations of the following function type:
```ts
type RequestHandler = (request: Request, next?: NextCallback) => Response | Promise
```
```ts
type NextCallback = (req: Request) => Response | Promise
```
## Debugging
Set the `DEBUG` environment variable to `srv:*` to get some debug logging:
```
$ DEBUG=srv:* node ./dist/my-server
```
## License
MIT