Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I use fx to build giki.app #470

Open
metrue opened this issue Mar 4, 2020 · 2 comments
Open

How do I use fx to build giki.app #470

metrue opened this issue Mar 4, 2020 · 2 comments
Assignees
Labels
RFC idea, design, and tasks.

Comments

@metrue
Copy link
Owner

metrue commented Mar 4, 2020

I found AWS lambda in July 2015 and became a big fan of Function as a Service (FaaS) since then, Three years ago I built my own FaaS framework fx at a Go Hackathon, then I published it on Hacker News, it quickly became one of Github trending repositories and pull in 700+ stars in one day.

While I was a little bloated, I struggled to find real-world scenario for it. This week I decided to become my own user of it, so I built an Web App with fx, and fx is amazingly handy in building APIs with FaaS way.

Hello World

fx is a simple tool I build to simplify the API development, let's take a look at how easy we build an API with fx.

You define an API in func.js , looks like this.

module.exports = (ctx) => {
  ctx.body = 'hello world'
}

Then you can deploy your function to be a service with fx with one command.

$ fx up --name helloworld --port 3000 func.js

Let's test it with curl.

$ curl -v 127.0.0.1:8080

HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 11
Content-Type: text/plain; charset=utf-8
Date: Tue, 06 Aug 2019 15:58:41 GMT

hello world

fx in fleself.com

fleself.com is a website (UI and APIs) are totally written with JavaScript/Node, all the APIs are built with fx.

Architecture

This's overall APIs code structure of fleself.com,

services
├── Makefile
├── db-migrations
├── tweets
│   ├── create
│   │   └── fx.js
│   ├── delete
│   │   └── fx.js
│   ├── query
│      └── fx.js
│   
└── users
    ├── oauth
    │   └── fx.js
    ├── sign
    │   └── fx.js
    └── update
        └── fx.js

And each source code of an API is just a function, take /api/tweets/delete as example,

const { Client } = require('pg')
const jwt = require('jsonwebtoken')

const k = 'key_xxxxxxxx'
const create = async (ctx) => {
  const { id } = ctx.request.body

  if (!ctx.headers.authorization) {
    ctx.throw(403, 'token required')
    return
  }

  let user = null
  try {
    const token = ctx.headers.authorization.split(' ')[1]
    user = jwt.verify(token, Buffer.from(k, 'base64'))
  } catch (e) {
    ctx.throw(e.status || 403, e.text)
    return
  }
  try {
    const client = new Client()
    await client.connect()
    const res = await client.query({
      text: `DELETE  FROM tweets WHERE id=$1 AND user_id=$2`,
      values: [id, user.id],
    })
    await client.end()
    ctx.body = 'deleted'
  } catch (e) {
    console.warn(e)
    await client.end()
    ctx.status = 500
  }
}
module.exports = create

GitHub Actions Workflows

The whole development process is managed on GitHub, and I use GitHub Actions to do CI/CD, its GitHub Actions workflow looks like this.

name: api
on:
  push:
    paths:
      - '.github/**'
      - 'services/**'
    branches:
      - master
jobs:
  api:
    runs-on: ubuntu-latest
    steps:
      - name: check out
        uses: actions/checkout@master
      - name: use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - name: install SSH key
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.SSH_KEY }}
          name: id_rsa
          known_hosts: ${{ secrets.KNOWN_HOSTS }}
      - name: install fx
        run: |
          curl -o- https://raw.githubusercontent.com/metrue/fx/master/scripts/install.sh | sudo bash
          fx -v
      - name: add fx host
        run: |
          fx infra create --name <node_name> --type docker --host root@<xxx.xxx.xxx.xxx>
          fx use <node_name>
      - name: deploy tweets_create
        run: |
          fx up -n tweetscreate -p 6000 services/tweets/create/fx.js --force

Caddy as Services Proxy

And I use Caddy as the frontend of the service, the config looks like this,

fleself.com {
  tls [email protected]
  gzip
  log ./fleself.com.log

  proxy /api/users/update 127.0.0.1:5000
  proxy /api/users/login  127.0.0.1:5010
  proxy /api/users/oauth 127.0.0.1:5020
  proxy /api/tweets/create 127.0.0.1:6000
  proxy /api/tweets/query 127.0.0.1:6010
  proxy /api/tweets/update 127.0.0.1:6020
  proxy /api/tweets/delete 127.0.0.1:6030
  proxy /api/tweets/sync 127.0.0.1:7000

  root ./fleself.com
  rewrite / {
    if {path} not_match ^/api
    to {path} /
  }
}
@metrue metrue added the RFC idea, design, and tasks. label Mar 4, 2020
@metrue metrue self-assigned this Mar 5, 2020
@lu-zen
Copy link

lu-zen commented Aug 28, 2020

Just found this project. Every lambda fx runs is a docker container?

@metrue
Copy link
Owner Author

metrue commented Aug 29, 2020

@lu-zen Yes, it's.

@metrue metrue changed the title How do I use fx to build fleself.com How do I use fx to build giki.app Aug 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
RFC idea, design, and tasks.
Projects
None yet
Development

No branches or pull requests

2 participants