api



Call an API or run a REST server

Schema

Schema

package api

Method: *"GET" | "POST" | "PUT" | "DELETE" | "OPTIONS" | "HEAD" | "CONNECT" | "TRACE" | "PATCH"

Call: {
	@task(api.Call)
	$task: "api.Call"

	req: {
		method: Method
		host:   string
		path:   string | *""
		auth?:  string
		headers?: [string]: string
		query?: [string]:   string
		form?: [string]:    string
		data?:    string | {...}
		timeout?: string
		// curl?: string
		retry?: {
			count: int | *3
			timer: string | *"6s"
			codes: [...int]
		}
	}

	// filled by task
	resp: {
		status:     string
		statusCode: int

		body: *{} | bytes | string
		header: [string]:  string | [...string]
		trailer: [string]: string | [...string]
	}

}

Serve: {
	@task(api.Serve)
	$task: "api.Serve"

	port:        string
	quitMailbox: string

	routes: [...{
		// @flow() is needed to run sub-tasks per request, which is more typical
		// you can omit if you only need to reshape the data with CUE code

		// filled by hof/flow on each request
		req: {
			method: Method
			url:    string

			headers: [string]: string
			query: [string]:   string

			body: bytes | string | *{} // assumed json body if object
		}

		// any tasks you may need to convert the req -> resp
		// these will be run after the `req` fields has been filled

		// you construct the resp value which is sent back to the client
		// (todo, make this include headers, code, etc
		// for now, this is a value which will be turned into a JSON body for the response
		resp: {
			status: int

			// one of, if none -> NoContent
			json?: {}
			html?: string
			body?: string
		}

	}]
}

Example: api.Call

api.Call example

exec hof flow flow.cue
cmp stdout golden.stdout

-- flow.cue --
package hof

import "encoding/json"

pick: {
	args: cow: string
}

tasks: [string]: {
	Out: _
	...
}

tasks: {
  @flow()
	call: { 
    @task(api.Call)
    req: {
      host: "https://postman-echo.com"
      // method: "GET"
      path: "/get"
      query: {
        cow: "moo"
      }
    }
    resp: {
      body: _
      statusCode: 200
    }
  }
  mask: {
    @task(st.Mask)
    val: call.resp.body
    mask: { headers: "x-amzn-trace-id": string }
  }
	out: { text: json.Indent(json.Marshal(mask.out), "", "  ") +"\n" } @task(os.Stdout)
}

-- golden.stdout --
{
  "args": {
    "cow": "moo"
  },
  "headers": {
    "x-forwarded-proto": "https",
    "x-forwarded-port": "443",
    "host": "postman-echo.com",
    "accept-encoding": "gzip",
    "user-agent": "Go-http-client/2.0"
  },
  "url": "https://postman-echo.com/get?cow=moo"
}

Example: api.Serve

api.Call example

exec hof flow ./in.cue
-- in.cue --
import "encoding/json"

@flow()

config: {
  port: "2323"
}

init: {
  m: { "mailbox": "quit", buf: 2, done: _ } @task(csp.Chan)
}

run: {
  @task(api.Serve)
  dep: init.m.done

  quitMailbox: "quit"
  port: config.port
  routes: {
    "/hello": {
      method: "GET"
      resp: {
        status: 200
        body: "hallo chat!"
      }
    }
    "/echo": {
      method: ["get", "post"]
      req: _
      // resp: req.query
      resp: json: req.query.cow
    }
    "/pipe": {
      @flow()
      req: _
      r: { filename: req.query.filename[0], contents: string } @task(os.ReadFile)
      j: json.Unmarshal(r.contents)
      resp: {
        status: 200
        json: j
      }
    }
  }
}

msg: {
  @task(os.Stdout)
  text: "server has shutdown\n"
  dep: run.done
}

call: {
  wait: {
    @task(os.Sleep)
    duration: "1s"
    done: _
  }
  do: {
    @dummy()
    @task(api.Call)
    dep: wait.done
    resp: body: string
    req: {
      host: "http://localhost:\(config.port)"
      method: "GET"
      path: "/hello"
      query: {
        cow: "moo"
      }
    }
  }
  say: {
    @task(os.Stdout)
    text: do.resp.body
  }
}

stop: {
  wait: {
    @task(os.Sleep)
    duration: "2s"
    done: _
  }

  send: {
    @task(csp.Send)
    dep: wait.done
    "mailbox": "quit" 
    val: "quit"
  }

}
2022 Hofstadter, Inc