Skip to main content

Quick Start

How to install#

# via npm
npm install --save farrow farrow-pipeline farrow-schema farrow-http
# via yarn
yarn add farrow farrow-pipeline farrow-schema farrow-http

How to setup a development environment#

add scripts to your package.json

{
"scripts": {
"dev": "farrow dev",
"build": "farrow build",
"start": "farrow start"
}
}

and then:

  • npm run dev for developing
  • npm run build for bundling the source code
  • npm run start for runing the output code of bundler

farrow assumes that your source code is in src folder, and the output code is in dist folder.

You can use farrow.config.js to change the default configuration, see the documentation for more detail.

How to setup a server#

import { Http, Response } from 'farrow-http'
const http = Http()
// add http middleware
http.use(() => {
// returning response in middleware
return Response.text(`Hello Farrow`)
})
http.listen(3000)

How to setup a https server#

import { Https, Response } from 'farrow-http'
const CERT = fs.readFileSync(path.join(__dirname, './keys/https-cert.pem'))
const KEY = fs.readFileSync(path.join(__dirname, './keys/https-key.pem'))
const CA = fs.readFileSync(path.join(__dirname, 'keys/https-csr.pem'))
const https = Https({
tsl: {
cert: CERT,
ca: CA,
key: KEY,
},
})
// add http middleware
https.use(() => {
// returning response in middleware
return Response.text(`Hello Farrow`)
})
https.listen(3000)

How to serve static assets#

http.serve('/static', dirname)

How to respond text or json or html or file#

// respond text
http.use(() => {
return Response.text(`Farrow`)
})
// respond json
http.use(() => {
return Response.json({
farrow: true,
data: {},
})
})
// respond html
http.use(() => {
return Response.html(`<h1>Farrow</h1>`)
})
// respond file
http.use(() => {
return Response.file(filename)
})

How to access request info#

http.use((request) => {
// access request pathname
console.log('pathname', request.pathname)
// access request method
console.log('method', request.method)
// access request query
console.log('query', request.query)
// access request body
console.log('body', request.body)
// access request headers
console.log('headers', request.headers)
// access request cookies
console.log('cookies', request.cookies)
})

How to match specific request#

Click Router-Url-Schema to read more

// http.match(schema).use(...middlewares)
// farrow will validate request info and extract the data for middlewares
// schema has the similar shape like request info: { pathname, method, query, body, headers, cookies, params }
// the params is readed from path-to-regexp if you config schema.pathname to be /product/:id, and params is equal to { id }
// learn more about pathname: https://github.com/pillarjs/path-to-regexp#usage
http
.match({
pathname: '/product',
// if method was not given, the default value wounld be `GET`.
query: {
productId: Number,
},
})
.use((request) => {
// productId is a number
console.log('productId', request.query.productId)
})
// or using routing-methods
http.get('/get0/<arg0:int>?<arg1:int>').use((request) => {
return Response.json({
type: 'get',
request,
})
})

How to pass new request info for downstream middleware#

http.use((request, next) => {
// no need to modify the request, just calling next(new_request) with a new request info
return next({
...request,
pathname: '/fixed',
})
})
http.use((request) => {
// request pathname will be '/fixed'
console.log('pathname', request.pathname)
})

How to filter and manipulate response in upstream middleware#

http.use(async (request, next) => {
// next() returning response received from downstream
let response = await next()
let headers = {
'header-key': 'header-value',
}
// filter or merge response and return
return Response.headers(headers).merge(response)
})
http.use(async (request) => {
return Response.json(request)
})

How to set response headers#

http.use(() => {
return Response.header('a', '1').header('b', '2').text('ok')
})
// or
http.use(() => {
return Response.headers({
a: '1',
b: '2',
}).text('ok')
})

How to set response cookies#

http.use(() => {
return Response.cookie('a', '1').cookie('b', '2').text('ok')
})
// or
http.use(() => {
return Response.cookies({
a: '1',
b: '2',
}).text('ok')
})

How to set response status#

http.use(() => {
return Response.status(404, 'Not Found').html('some text')
})

How to redirect#

http.use(() => {
return Response.redirect(targetUrl)
})

How to merge responses#

let response0 = Response.status(200)
let response1 = Response.header('a', '1')
let response2 = Response.header('b', '2')
let response3 = Response.cookie('c', '3')
let response = Response.merge(response0, response1, response2, response3)
// or
let response = response0.merge(response1, response2, response3)

How to add router#

Router() has the same methods like Http() except http.listen(...) and http.server()

import { Http, Router, Response } from 'farrow-http'
// create http
const http = Http()
// create product router
const product = Router()
// create user router
const user = Router()
// add sub route for product
http.route('/product').use(product)
// add sub route for user
http.route('/user').use(user)
http.listen(3000)
// handle product router
// this will match /product/:id
product.get('/<id:int>').use(async (request) => {
return Response.json({
productId: request.params.id,
})
})
// this will match /product/info
product.get('/info').use(async (request) => {
return Response.json({
productInfo: {},
})
})
// handle user router
// this will match /user/:id
user.get('/<id:int>').use(async (request) => {
return Response.json({
userId: request.params.id,
})
})
// this will match /user/info
user.get('/info').use(async (request) => {
return Response.json({
userInfo: {},
})
})

How to add view-engine#

Farrow provide an official server-side rendering library based on React, but you can implement your own via Response.html(...) or Response.stream(...).

# via npm
npm install --save react react-dom farrow-react
# via yarn
yarn add react react-dom farrow-react
import React from 'react'
import { useReactView } from 'farrow-react'
// use Link to auto prefix basename came from http.route(name, ...) or router.route(name, ...)
import { Link } from 'farrow-react/Link'
http.use(() => {
let ReactView = useReactView({
docType: '<!doctype html>', // optional, specify the doctype in html response
useStream: true, // optional, if ture it will use ReactDOMServer.renderToNodeStream internally
})
return ReactView.render(
<>
<h1>Hello Farrow-React</h1>
<Link href="/">Home</Link>
</>,
)
})

How to write a farrow hooks#

Click here to learn more.

import { createContext } from 'farrow-pipeline'
import { Http, HttpMiddleware } from 'farrow-http'
import { useReactView } from 'farrow-react'
// define an interface
interface User {
id: string
name: string
email: string
}
// define a farrow context via interface
const UserContext = createContext<User | null>(null)
// define a provider
const UserProvider = (): HttpMiddleware => {
return async (request, next) => {
let session = SessionContext.get()
let db = DbContext.get()
if (!request?.cookies?.token) {
return next()
}
let userId = await session.read(request?.cookies?.token)
let user = await db.query({
User: {
token,
},
})
UserContext.set(user)
return next()
}
}
const http = Http()
http.use(UserProvider())
http
.match({
pathname: '/userinfo',
})
.use(async (request, next) => {
let ReactView = useReactView()
// assert context value is not null or undefined and return context value
let user = UserContext.assert()
return ReactView.render(<div>{JSON.stringify(user)}</div>)
})