Lab - Using httpYac

I am looking for some open source API clients for regular use. httpYac allows more flexible scripting and can also run headless as a headless service. It also comes with support for OAuth 2.0 and OIDC.

Preparation

  • Node.js, optionally Yarn

Installation

From https://httpyac.github.io/ then https://httpyac.github.io/guide/, install CLI:

yarn global add httpyac

We'll also try the VS Code extension. Install it by searching for httpyac within VS Code, or using the command below if you can run code in command line:

code --install-extension anweber.vscode-httpyac

Create a project folder and setup a dummy todo-list Express app:

mkdir yac
cd yac
yarn init -y
yarn add express

with the file app.js

const express = require('express');

const PORT = 3000;

let nextId = 4;
let todos = [
    { id: 1, title: 'Exercise', done: true, },
    { id: 2, title: 'Learn Python', done: false, },
    { id: 3, title: 'Lunch', done: false, },
];

function findTodo(id) {
    return todos.find(todo => todo.id === id);
}
function addTodo({ title, done }) {
    const todo = { id: nextId++, title, done };
    todos.push(todo);
    return todo;
}
function deleteTodo(id) {
    todos = todos.filter(t => t.id !== id);
}

const app = express();
app.use(express.json());
app.set('json spaces', 2);

app.get('/', (req, res) => {
    res.json({ message: 'ok' });
});

app.get('/todo', (req, res) => {
    res.json(todos);
});

app.post('/todo', (req, res) => {
    const { title = '', done = false } = req.body;
    const todo = addTodo({ title, done });
    res.json(todo);
});

app.get('/todo/:id(\\d+)', (req, res) => {
    const id = parseInt(req.params.id);
    const todo = findTodo(id);
    if (!todo) return res.status(404).send('Not Found');

    res.json(todo);
});
app.put('/todo/:id(\\d+)', (req, res) => {
    const id = parseInt(req.params.id);
    const { title, done } = req.body;

    const todo = findTodo(id);
    if (!todo) return res.status(404).send('Not Found');

    if (title !== undefined) todo.title = title;
    if (done !== undefined) todo.done = done;

    res.json(todo);
});

app.delete('/todo/:id(\\d+)', (req, res) => {
    const id = parseInt(req.params.id);
    deleteTodo(id);
    res.end();
});

app.get('/_bad', (req, res) => {
    throw Error('This is bad');
});

app.all('*', (req, res, next) => {
    res.status(400).send('Bad Request');
});

app.use((err, req, res, next) => {
    res.status(500).send('Something wrong!');
});

app.listen(PORT, () => {
   console.log(`Service at http://localhost:${PORT}`);
});

Then run it

$ node app.js
Service at http://localhost:3000

If we just use curl:

$ curl http://localhost:3000
{"message":"ok"}

$ curl -i http://localhost:3000
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 21
ETag: W/"15-FCv+/NlfTgD7mX/4JeAWgb2wmko"
Date: Tue, 08 Oct 2024 03:37:43 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{
  "message": "ok"
}

Preparing the http file home.http:

GET http://localhost:3000/
$ httpyac --all ./home.http

---------------------

GET http://localhost:3000/
accept: */*
accept-encoding: gzip, deflate, br
user-agent: httpyac
HTTP/1.1 200  - OK
connection: keep-alive
content-length: 21
content-type: application/json; charset=utf-8
date: Tue, 08 Oct 2024 03:39:09 GMT
etag: W/"15-FCv+/NlfTgD7mX/4JeAWgb2wmko"
keep-alive: timeout=5
x-powered-by: Express

{
  "message": "ok"
}

1 requests processed (1 succeeded)

Showing available options:

$ httpyac --help
Usage: httpyac send <fileName...> [options]

send/ execute http files

Arguments:
  fileName                  path to file or glob pattern

Options:
  -a, --all                 execute all http requests in a http file
  --bail                    stops when a test case fails
  -e, --env  <env...>       list of environments
  --filter <filter>          filter requests output (only-failed)
  --insecure                allow insecure server connections when using ssl
  -i --interactive          do not exit the program after request, go back to selection
  --json                    use json output
  --junit                   use junit xml output
  -l, --line <line>         line of the http requests
  -n, --name <name>         name of the http requests
  --no-color                disable color support
  -o, --output <output>     output format of response (short, body, headers, response,
                            exchange, none)
  --output-failed <output>  output format of failed response (short, body, headers,
                            response, exchange, none)
  --raw                     prevent formatting of response body
  --quiet
  --repeat <count>          repeat count for requests
  --repeat-mode <mode>      repeat mode: sequential, parallel (default)
  --parallel <count>        send parallel requests
  -s, --silent              log only request
  -t, --tag  <tag...>       list of tags to execute
  --timeout <timeout>       maximum time allowed for connections
  --var  <variables...>     list of variables (e.g foo="bar")
  -v, --verbose             make the operation more talkative
  -h, --help                display help for command

Try some variations below:

$ httpyac --all ./home.http -o body

---------------------

{
  "message": "ok"
}

1 requests processed (1 succeeded)

$ httpyac --all ./home.http -o headers

---------------------

GET http://localhost:3000/
accept: */*
accept-encoding: gzip, deflate, br
user-agent: httpyac
HTTP/1.1 200  - OK
connection: keep-alive
content-length: 21
content-type: application/json; charset=utf-8
date: Tue, 08 Oct 2024 03:41:11 GMT
etag: W/"15-FCv+/NlfTgD7mX/4JeAWgb2wmko"
keep-alive: timeout=5
x-powered-by: Express

1 requests processed (1 succeeded)

$ httpyac --all ./home.http -o response

---------------------

HTTP/1.1 200  - OK
connection: keep-alive
content-length: 21
content-type: application/json; charset=utf-8
date: Tue, 08 Oct 2024 03:47:07 GMT
etag: W/"15-FCv+/NlfTgD7mX/4JeAWgb2wmko"
keep-alive: timeout=5
x-powered-by: Express

{
  "message": "ok"
}

1 requests processed (1 succeeded)

POST - Create New Item

To add a new todo item, create a file todo-new-item.http:

POST http://localhost:3000/todo
Content-Type: application/json

{
  "title": "Dinner",
  "done": false
}

GET http://localhost:3000/todo

And run it:

$ httpyac --all todo-new-item.http

---------------------

POST http://localhost:3000/todo
accept: */*
accept-encoding: gzip, deflate, br
content-length: 40
content-type: application/json
user-agent: httpyac

{
  "title": "Dinner",
  "done": false
}
HTTP/1.1 200  - OK
connection: keep-alive
content-length: 51
content-type: application/json; charset=utf-8
date: Tue, 08 Oct 2024 03:54:53 GMT
etag: W/"33-B64ZQVMIvzd8UgOtCCzSBxGpaws"
keep-alive: timeout=5
x-powered-by: Express

{
  "id": 4,
  "title": "Dinner",
  "done": false
}

---------------------

GET http://localhost:3000/todo
accept: */*
accept-encoding: gzip, deflate, br
user-agent: httpyac
HTTP/1.1 200  - OK
connection: keep-alive
content-length: 260
content-type: application/json; charset=utf-8
date: Tue, 08 Oct 2024 03:54:53 GMT
etag: W/"104-g18obrYyxa/iaCigiU6DM/maeaU"
keep-alive: timeout=5
x-powered-by: Express

[
  {
    "id": 1,
    "title": "Exercise",
    "done": true
  },
  {
    "id": 2,
    "title": "Learn Python",
    "done": false
  },
  {
    "id": 3,
    "title": "Lunch",
    "done": false
  },
  {
    "id": 4,
    "title": "Dinner",
    "done": false
  }
]

2 requests processed (2 succeeded)