## Script types
Posting supports three types of scripts, which run at different points in the request/response lifecycle:
1. **Setup Scripts**: Runs before the request is constructed. This is useful for setting initial variables which may be substituted into the request.
2. **Pre-request Scripts**: Runs after the request has been constructed and variables have been substituted, but before the request is sent. You can directly modify the request object here.
3. **Post-response Scripts**: Runs after the response is received. This is useful for extracting data from the response, or for performing cleanup.
## Writing scripts
In the context of Posting, a "script" is a regular Python function.
By default, if you specify a path to a Python file, Posting will look for and execute the following functions at the appropriate times:
- `setup(posting: Posting) -> None`
- `on_request(request: RequestModel, posting: Posting) -> None`
- `on_response(response: httpx.Response, posting: Posting) -> None`
However, you can have Posting call any function you wish using the syntax `path/to/script.py:function_to_run`.
Note that relative paths are relative to the collection directory.
This ensures that if you place scripts inside your collection directory,
they're included when you share a collection with others.
Note that you do not need to specify all of the arguments when writing these functions. Posting will only pass the number of arguments that you've specified when it calls your function. For example, you could define a your `on_request` function as `def on_request(request: RequestModel) -> None` and Posting would call it with `on_request(request: RequestModel)` without passing the `posting` argument.
## Editing scripts
When you edit a script, it'll automatically be reloaded.
This means you can keep Posting open while editing it.
Posting also allows you to quickly jump to your editor (assuming you've set the `$EDITOR` or `$POSTING_EDITOR` environment variables) to edit a script.
Press ++ctrl+e++ while a script input field inside the `Scripts` tab is focused to open the path in your editor.
!!! warning
As of version 2.0.0, the script file must exist *before* pressing ++ctrl+e++. Posting will not create the file for you.
## Script logs
If your script writes to `stdout` or `stderr`, you'll see the output in the `Scripts` tab in the Response section.
This output is not persisted on disk.
### Example: Setup script
The **setup script** is run before the request is built.
You can set variables in the setup script that can be used in the request.
For example, you could use `httpx` to fetch an access token, then set the token as a variable so that it may be substituted into the request.
```python
def setup(posting: Posting) -> None:
# Set a variable which may be used in this request
# (or other requests to follow)
posting.set_variable("auth_token", "1234567890")
```
With this setup script attached to a request, you can then reference the `auth_token` variable in the request UI by typing `$auth_token`.
The `$auth_token` variable will remain for the duration of the session,
so you may wish to add a check to see if it has already been set in this session:
```python
def setup(posting: Posting) -> None:
if not posting.get_variable("auth_token"):
posting.set_variable("auth_token", "1234567890")
```
### Example: Pre-request script
The **pre-request script** is run after the request has been constructed and variables have been substituted, right before the request is sent.
You can directly modify the `RequestModel` object in this function, for example to set headers, query parameters, etc.
The code snippet below shows some of the API.
```python
from posting import Auth, Header, RequestModel, Posting
def on_request(request: RequestModel, posting: Posting) -> None:
# Add a custom header to the request.
request.headers.append(Header(name="X-Custom-Header", value="foo"))
# Set auth on the request.
request.auth = Auth.basic_auth("username", "password")
# request.auth = Auth.digest_auth("username", "password")
# request.auth = Auth.bearer_token_auth("token")
# This will be captured and written to the log.
print("Request is being sent!")
# Make a notification pop-up in the UI.
posting.notify("Request is being sent!")
```
### Example: Post-response script
The **post-response script** is run after the response is received.
You can use this to extract data from the response, for example a JWT token,
and set it as a variable to be used in later requests.
```python
from posting import Posting
def on_response(response: httpx.Response, posting: Posting) -> None:
# Print the status code of the response to the log.
print(response.status_code)
# Set a variable to be used in later requests.
# You can write '$auth_token' in the UI and it will be substituted with
# the value of the $auth_token variable.
posting.set_variable("auth_token", response.headers["Authorization"])
```
### The `Posting` object
The `Posting` object provides access to the application context and useful methods:
- `set_variable(name: str, value: object) -> None`: Set a session variable
- `get_variable(name: str, default: object | None = None) -> object | None`: Get a session variable
- `clear_variable(name: str) -> None`: Clear a specific session variable
- `clear_all_variables() -> None`: Clear all session variables
- `notify(message: str, title: str = "", severity: str = "information", timeout: float | None = None)`: Send a notification to the user
Note that variables are described as "session variables" because they persist for the duration of the session (until you close Posting).
### Execution environment
Scripts run in the same process and environment as Posting, so you should take care to avoid performing damaging global operations such as monkey-patching standard library modules.
#### Libraries
You can make use of any library that is available in the Python environment that Posting is running in. This means you can use all of the Python standard library as well as any of Posting's dependencies (such as `httpx`, `pyyaml`, `pydantic`, etc).
If you install Posting with `uv`, you can easily add extra dependencies which you can then use in your scripts:
```bash
uv tool install posting --with Posting is a beautiful open-source terminal app for developing and testing APIs.
Fly through your API workflow with an approachable yet powerful keyboard-centric interface. Run it locally or over SSH on remote machines and containers. Save your requests in a readable and version-control friendly format.
Navigate intuitively and efficiently with the keyboard using jump mode.
Access commands from anywhere using the built-in command palette.
Build requests quickly with powerful autocompletion.
Edit a request body in nvim or browse a JSON response in fx? No problem!
Import curl commands into Posting by simply pasting into the URL bar. Export requests to curl in seconds.
Use compact mode to fit more on screen.
Create your own themes, or choose from a selection of built-in options.
Gorgeous syntax highlighting powered by the popular tree-sitter library.
Adjust the user interface to your liking through the configuration system or at runtime.
Customize keybindings to your liking using the keymap system.
Share common data across requests and with others using environments.
Load variables from one or more dotenv environment files, or allow access to environment variables.
Edit variables in your favorite editor, and Posting will hot reload them.
Feeling lost? Press f1 to learn keybindings and other useful information for the currently focused widget.
Run Python code before and after requests to prepare headers, set variables, and more.
Run it on macOS, Windows, and Linux. Install it locally, on a remote server, in a Docker container, or even on a Raspberry Pi.
Posting is a community-driven project with an open roadmap.
The roadmap is highly influenced by user feedback.
Get involved on GitHub by reporting bugs, suggesting features, sponsoring development, or contributing code.
If you're happy with the content on the left,
save it to disk by running pytest with the --snapshot-update flag.
Snapshots are named after the name of the test you call snap_compare in by default.
If you've renamed a test, the association between the snapshot and the test is lost,
and you'll need to run with --snapshot-update to associate the snapshot
with the new test name.
If you're happy with the test output, run pytest with the --snapshot-update flag to update the snapshot.
Report generated at UTC {{ now }}.
backend default {\\n .host = "127.0.0.1";\\n .port = "80";\\n}\\nsub vcl_recv {\\n set req.backend = default;\\n}\\nsub vcl_hash {\\n set req.hash += req.url;\\n set req.hash += req.http.host;\\n set req.hash += "0";\\n}\\n \\n backend default {\\n .host = "127.0.0.1";\\n .port = "80";\\n}\\nsub vcl_recv {\\n set req.backend = default;\\n}\\nsub vcl_hash {\\n set req.hash += req.url;\\n set req.hash += req.http.host;\\n set req.hash += "0";\\n}\\n \\n