URI Types
Paradox has first-class URI types with literal syntax. URIs are not strings — they are structured, typed values that decompose into scheme, authority, path, query, fragment, HTTP methods, MIME types, and request/response type pairs.
The URI Type
Defined in the standard library (import std):
type URI
scheme: Text
authority: URIAuth?
path: Text?
query: Text?
fragment: Text?
io: (Type. Type)?
meta: {Text}?
accept: [Text]?
The URIAuth Type
type URIAuth
user: Text?
name: Text
port: Port?
The authority component of a URI. Port is a validated wrap type (0-65535) defined in std.
URI Literal Syntax
URIs can be written directly as expressions. The parser recognizes any scheme:// prefix and parses the full structure:
simple: URI
http://example.com
secure: URI
https://api.example.com/v1/users
withPort: URI
http://localhost:8080
withUser: URI
ssh://deploy@prod.server.com:22
Full Grammar
scheme://[user@]host[:port][/path][?query][#fragment] [methods...] [mime/types...] [(Input. Output)]
Every component after scheme and host is optional. Combine them freely:
| Scheme and host only
http://example.com
| With port
http://localhost:8080
| With user and port
ssh://admin@server.com:22
| With path
https://api.example.com/v1/users
| With query
https://search.com/find?q=paradox
| With fragment
https://docs.com/manual#installation
| Everything
https://admin@api.example.com:443/v2/users?format=json#response
HTTP Methods (meta)
Append HTTP method names after the URI. They are stored in the meta field as a {Text} set:
getUsers: URI
http://api.example.com/users GET
createUser: URI
http://api.example.com/users POST
updateUser: URI
http://api.example.com/users/123 PUT
deleteUser: URI
http://api.example.com/users/123 DELETE
Multiple methods are space-separated:
usersEndpoint: URI
http://api.example.com/users GET POST
MIME Types (accept)
Tokens containing / are classified as MIME types and stored in the accept field as a [Text] array:
jsonApi: URI
http://api.example.com/data GET application/json
multiFormat: URI
http://api.example.com/export GET application/json text/csv
Methods and MIME types can be mixed freely — the parser distinguishes them by the presence of /.
Request/Response Types (io)
The io field defines a typed request/response pair using (Input. Output) syntax. This is the critical semantic field for API specification — it bridges domain types to generated code:
createUser: URI
http://localhost/users POST (CreateUserRequest. CreateUserResponse)
getUser: URI
http://localhost/users GET (Unit. User)
healthCheck: URI
http://api.example.com/health GET ((). StatusResponse)
logEvent: URI
https://api.com/log POST (Request. ())
Use () or Unit for endpoints that take no input or produce no output:
| No input, returns data
https://api.com/fetch ((). Response)
| Accepts input, returns nothing
https://api.com/log (Request. ())
| Ping -- no input, no output
https://api.com/ping ((). ())
Interpolation
Every text component of a URI supports ${} interpolation, just like string interpolation:
protocol: Text
"ftp"
host: Text
"ftp.example.com"
port: Integer
21
path: Text
"files/document.pdf"
action: Text
"READ"
api: URI
${protocol}://${host}:${port}/${path} ${action}
Interpolated expressions are cast to Text. When an interpolated expression starts with an uppercase letter, it is parsed as a type expression (useful for parameterized types like ${Key t}).
URIs in Type Definitions
URI fields work naturally in product types. The literal syntax is used directly in field values:
type ApiConfig
baseUrl: URI
healthCheck: URI
fallback: URI?
config: ApiConfig
ApiConfig:
baseUrl: http://api.example.com/v1
healthCheck: http://api.example.com/health GET
fallback: ()
Service Registries
A common pattern is defining typed service registries where each endpoint carries its full contract:
type ServiceRegistry
auth: URI
users: URI
posts: URI
registry: ServiceRegistry
ServiceRegistry:
auth: https://auth.service.internal:8443/oauth POST (Request. Response)
users: https://users.service.internal:8080/api
posts: https://posts.service.internal:8080/api
Webhook Configs
type WebhookConfig
onSuccess: URI
onFailure: URI
onTimeout: URI?
retryEndpoint: URI?
webhooks: WebhookConfig
WebhookConfig:
onSuccess: https://hooks.example.com/success POST (Response. ())
onFailure: https://hooks.example.com/failure POST (Error. ())
onTimeout: https://hooks.example.com/timeout POST (Request. ())
retryEndpoint: ()
How URIs Desugar
URI literals are syntactic sugar for URI product literals. The parser converts:
https://admin@api.com:443/v1/users?active=true POST (User. Response)
into the equivalent of:
URI:
scheme: "https"
authority: URIAuth:
user: "admin"
name: "api.com"
port: 443
path: "v1/users"
query: "active=true"
fragment: ()
meta: {"POST"}
accept: ()
io: (User. Response)
Port values are automatically wrapped through the Port(Natural(Integer)) chain, enabling validation.
See Also
-
Std —
URIandURIAuthtype definitions -
Numeric —
PortandNaturalwrap types -
Expressions — Expression syntax and string interpolation
-
Types — Product types and wrap types