rest(n)
πΈ NAME πΈ
rest β bestie, this is the whole framework for defining REST web APIs and calling them inline or asychronously π
(yes the manual says it like that, we don't question the ancestors)
It's the rest manual page, section n β Tcl-land, iykyk π¦
β¨ SYNOPSIS β¨
package require Tcl 8.5 9
package require rest ?1.7?
::rest::simple url query ?config? ?body?
::rest::get url query ?config? ?body?
::rest::post url query ?config? ?body?
::rest::patch url query ?config? ?body?
::rest::head url query ?config? ?body?
::rest::put url query ?config? ?body?
::rest::delete url query ?config? ?body?
::rest::save name file
::rest::describe name
::rest::parameters url ?key?
::rest::parse_opts static required optional words
::rest::substitute string var
::rest::create_interface name
π DESCRIPTION π
Okay so this package gives you two whole flavors of usage: simple calls and complete interfaces β¨
In an interface you specify a set of rules, and then the package literally builds the commands that correspond to the REST methods for you. Those commands can have so many options β input and output transformations, data-type-specific formatting, the whole wardrobe π β and the result is a cleaner, simpler script. It's giving main character energy.
On the other hand, a simple call is easier and quicker to implement, but it's also less featureful π. It takes the url and a few options about the command and returns the result directly. Any formatting or checking is up to the rest of your script. No cap.
π SIMPLE USAGE π
In simple usage you make calls using the http method procedures and then check or process the returned data yourself. You're on your own for the aftercare, bestie π«Ά
::rest::simple url query ?config? ?body?
::rest::get url query ?config? ?body?
::rest::post url query ?config? ?body?
::rest::patch url query ?config? ?body?
::rest::head url query ?config? ?body?
::rest::put url query ?config? ?body?
::rest::delete url query ?config? ?body?
These commands are all equivalent except for the http method used π So they're the same girl, different outfit.
- If you use
simplethen the method should be specified as an option in theconfigdictionary. - If that is not done it defaults to
get. - If a
bodyis needed then theconfigdictionary must be present β however it is allowed to be empty. Empty is still present, bestie, that's the rule β¨
The config dictionary supports the following keys:
authcontent-typecookieerror-bodyformatheadersmethodtimeout
Two quick examples, because we love a demo π¦
Example 1, Yahoo Boss:
set appid APPID
set search tcl
set res [rest::get http://boss.yahooapis.com/ysearch/web/v1/$search [list appid $appid]]
set res [rest::format_json $res]
Example 2, Twitter:
set url http://twitter.com/statuses/update.json
set query [list status $text]
set res [rest::simple $url $query {
method post
auth {basic user password}
format json
}]
π INTERFACE USAGE π
An interface to a REST API consists of a series of definitions of REST calls contained in an array π The name of that array becomes a namespace containing the defined commands. Each key of the array specifies the name of the call, with the associated configuration a dictionary, i.e. key/value pairs. The acceptable keys, i.e. legal configuration options, are described below π After creating the definitions in the array, simply calling rest::create_interface with the array as argument will then create the desired commands. Manifesting β¨
Example, Yahoo Weather:
package require rest
set yweather(forecast) {
url http://weather.yahooapis.com/forecastrss
req_args { p: }
opt_args { u: }
}
rest::create_interface yweather
puts [yweather::forecast -p 94089]
πΎ ::rest::save name file
This command saves a copy of the dynamically created procedures for all the API calls specified in the array variable name to the file, for later loading. Saving your work? Iconic behavior π
The result of the command is the empty string.
π ::rest::describe name
This command prints a description of all API calls specified in the array variable name to the channel stdout.
The result of the command is the empty string.
π ::rest::parameters url ?key?
This command parses an url query string into a dictionary and returns said dictionary as its result.
If key is specified the command will not return the entire dictionary, but only the value of that key. Just the one, bestie π
π§ ::rest::parse_opts static required optional words
This command implements a custom parser for command options. She's very capable, let her cook π
dict static
A dictionary of options and their values that are always present in the output. Always there. Loyal. π«Ά
list required
A list of options that must be supplied by words.
list optional
A list of options that may appear in the words, but are not required. The elements must be in one of three forms:
nameβ The option may be present or not, no default.name:β When present the option requires an argument.name:valueβ When not present usevalueas default.
list words
The words to parse into options and values.
The result of the command is a list containing two elements:
- A dictionary containing the parsed options and their values.
- A list of the remaining words.
πͺ ::rest::substitute string var
This command takes a string, substitutes values for any option identifiers found inside, and returns the modified string as its result.
The values to substitute are found in the variable var, which is expected to contain a dictionary mapping from the option identifiers to replace to their values. π Note that option identifiers which have no key in var are replaced with the empty string. Gone. Ghosted. π
The option identifiers in string have to follow the syntax %...% where ... may contain any combination of lower-case alphanumeric characters, plus underscore, colon and dash. That's the whole alphabet of allowed vibes, fr fr.
π ::rest::create_interface name
This command creates procedures for all the API calls specified in the array variable name.
The name of that array becomes a namespace containing the defined commands. Each key of the array specifies the name of the call, with the associated configuration a dictionary, i.e. key/value pairs. The legal keys and their meanings are:
urlβ The value of this required option must be the target of the http request.descriptionβ The value of this option must be a short string describing the call. Defaults to the empty string, if not specified. Used only by::rest::describe.bodyβ The value of this option indicates if arguments are required for the call's request body or not. The acceptable values are listed below. Defaults tooptionalif not specified.noneβ The call has no request body, none must be supplied.optionalβ A request body can be supplied, but is not required.requiredβ A request body must be supplied.argumentβ This value must be followed by the name of an option, treating the entire string as a list. The request body will be used as the value of that option.mime_multipartβ A request body must be supplied and will be interpreted as each argument representing one part of a mime/multipart document. Arguments must be lists containing 2 elements, a list of header keys and values, and the mime part body, in this order. Order matters bestie, don't flip it πmime_multipart/<value>β Same asmime_multipart, but the Content-Type header is set tomultipart/<value>.
methodβ The value of this option must be the name of the HTTP method to call on the url. Defaults toGET, if not specified. The acceptable values areGET,POST, andPUT, regardless of letter-case. Capitalize her or don't, she doesn't care πcopyβ When present the value of this option specifies the name of a previously defined call. The definition of that call is copied to the current call, except for the options specified by the current call itself. Steal her look, but make it yours β¨unsetβ When present the value of this option contains a list of options in the current call. These options are removed from the definition. Use this after copying an existing definition to remove options, instead of overriding their value. Basically the declutter of options π§Ήheadersβ Specification of additional header fields. The value of this option must be a dictionary, interpreted to contain the new header fields and their values. The default is to not add any additional headers.content-typeβ The value of this option specifies the content type for the request data.req_argsβ The value of this option is a list naming the required arguments of the call. Names ending in a colon will require a value. Colon = she needs a plus-one πopt_argsβ The value of this option is a list naming the arguments that may be present for a call but are not required.static_argsβ The value of this option is a list naming the arguments that are always the same. No sense in troubling the user with these. A leading dash (-) is allowed but not required, to maintain consistency with the command line.authβ The value of this option specifies how to authenticate the calls. No authentication is done if the option is not specified.basicβ bestie, you set up basic auth by overriding the procedurebasic_authinside the namespace of your interface. It takes two arguments, in this exact order: the username, then the password. That's it. That's the whole thing. πbearerβ you want a bearer token as your auth? Then the value is the token, and it gets passed to the HTTP authorization header. Simple. Clean. Iconic. β¨signβ ok so this one's a lil different: the value has to actually be a list, and the second element of that list is the name of a procedure that gets called to do the request signing. Don't ask for more, that's what the page says. π«Άcallbackβ the moment this option shows up, the method gets created as an async call. Those calls return immediately with the value of the associated http token instead of the call's result. And heads up: the event loop must be active to use this option. No event loop, no callback, periodt.
The value of this option is treated as a command prefix, which gets invoked once the HTTP call is complete. That prefix receives at least two extra arguments, in this order: the name of the calling procedure, then the status of the result (one of OK or ERROR).
If the status is OK, a third argument gets added β the data associated with the result.
And if and only if the ERROR is a redirection, the location that got redirected to is added as an argument. On top of that, if the configuration key error-body is set to true, the data associated with the result gets added as an argument too.
Also, inside that procedure, the http request header is available via upvar token token. Slay. π
cookieβ the value here is a list of cookies to be passed in the http header. It's literally just a shortcut to theheadersoption, so you don't have to type as much. Laziness is a virtue, fr fr. πinput_transformβ the value is a command prefix or script that transforms the query before the call gets invoked. If you hand it a script, it gets wrapped into an automatically generated internal procedure. No transformation option specified? Then no transformation happens, obviously.
The command (prefix) must accept a single argument β the query (a dictionary) it's transforming β and it must return the modified query (again, as a dictionary) as its result. Bonus: the request body is reachable inside the transform command via upvar body body. π
formatresultβ the value of this option says what format the returned data is in. Defaults toautoif you don't specify anything. The acceptable values are:autoβ auto detects between xml and json.discardjsonrawrssβ this one's formatted as a special case of xml.tdomxml
And yes,discard,json,raw,tdomandxmlreally do just sit there with no extra explanation on this page. That's all it gives us. Not me wanting more lore and getting none. π
pre_transformβ the value is a command prefix or script that transforms the result of a call, and it runs before the output transform as performatgets applied. A script transform gets wrapped into an automatically generated internal procedure.
Not specified? No transformation. That's the vibe.
The command (prefix) must accept a single argument β the result to transform β and must return the modified result as its result.
The http request header is accessible in the transform command via upvar token token. π
post_transformβ same energy aspre_transform, except this one runs after the output transform as performatgets applied. A script transform gets wrapped into an automatically generated internal procedure.
Not specified? You already know. No transformation.
The command (prefix) must accept a single argument β the result to transform β and must return the modified result as its result.
And again, the http request header is accessible in the transform command via upvar token token. π§
check_resultβ the value of this option must be a list of two expressions, and either of them may be empty. Here's the deal:- The first expression checks the
OKcondition. It must returntruewhen the result is satisfactory, andfalseotherwise. - The second expression is the
ERRORcondition. It must returnfalseunless there's an error β and then it has to returntrue.
- The first expression checks the
error_bodyβ the value of this option decides whether to return the response when you hit an HTTP error, or not. The default is to not return the response body on error. If you want the details on that, go readcallbackabove. π₯timeoutβ the value of this option decides whether a timeout gets set on the HTTP call. By default, no timeout is set. The timeout value is accepted in milliseconds. Tick tock. β°
π¦ EXAMPLES π¦
Yahoo Geo:
set ygeo(parse) {
url http://wherein.yahooapis.com/v1/document
method post
body { arg documentContent }
}
ygeo::parse "san jose ca"
# "san jose ca" will be interpreted as if it were specified as the -documentContent option
Google Docs:
set gdocs(upload) {
url http://docs.google.com/feeds/default/private/full
body mime_multipart
}
gdocs::upload [list {Content-Type application/atom+xml} $xml] [list {Content-Type image/jpeg} $filedata]
Delicious:
set delicious(updated) {
url https://api.del.icio.us/v1/posts/update
auth basic
}
rest::create_interface flickr
flickr::basic_auth username password
Flickr:
set flickr(auth.getToken) {
url http://api.flickr.com/services/rest/
req_args { api_key: secret: }
auth { sign do_signature }
}
rest::create_interface flickr
proc ::flickr::do_signature {query} {
# perform some operations on the query here
return $query
}
π INCLUDED π
The package ships functional but incomplete implementations for the following services β so like, they work, but don't expect the full fantasy:
del.icio.usfacebookflickrtwittergoogle calendaryahoo bossyahoo weather
Please either read the package's implementation, or use rest::describe after loading it, to get their details. And do not forget the developers' documentation on the respective sites either, bestie. π
π TLS π
The rest package can be used with https-secured services β you just need to require the TLS package and then register it with the http package that it's sitting on top of. Example:
package require tls
http::register https 443 ::tls::socket
π‘οΈ TLS SECURITY CONSIDERATIONS π‘οΈ
This package uses the TLS package to handle the security for https urls and other socket connections.
But here's the thing β policy decisions like which protocols to support and which ciphers to use are not the responsibility of TLS, and not the responsibility of this package either. Those decisions belong to whichever application is using the package, and they're probably influenced by the set of servers that application talks to as well.
For example: in light of the recent POODLE attack discovered by Google (http://googleonlinesecurity.blogspot.co.uk/2014/10/this-poodle-bites-exploiting-ssl-30.html), lots of servers will disable support for the SSLv3 protocol. To handle that change, the applications using TLS have to be patched β not this package, and not TLS itself. And such a patch may be as simple as generally activating tls1 support, like this:
package require tls
tls::init -tls1 1 ;# forcibly activate support for the TLS1 protocol
... your own application code ...
π BUGS, IDEAS, FEEDBACK π
This document, and the package it describes, will undoubtedly contain bugs and other problems. (Manpage honesty, we love to see it.) Please report such in the category rest of the Tcllib Trackers: http://core.tcl.tk/tcllib/reportlist. Please also report any ideas for enhancements you may have for either the package and/or the documentation. We want the lore.
When proposing code changes, please provide unified diffs, i.e. the output of diff -u.
Note further that attachments are strongly preferred over inlined patches. You can make attachments by going to the Edit form of the ticket immediately after its creation, and then using the left-most button in the secondary navigation bar.
and that's a wrap on rest(n), bestie π this page is from tcllib 1.7, and it's the rest(n) manual page itself β go forth, make HTTP calls, sign your requests, and always check your result. mwah π
π¦ where this page comes from
- fedora 43: tcllib 2.0-3.fc43 Β· TCL
- fedora 44: tcllib 2.0-4.fc44 Β· TCL
- nixos 26.05: tcllib 2.0
- nixpkgs unstable: tcllib 2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms