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 — URI and URIAuth type definitions

  • Numeric — Port and Natural wrap types

  • Expressions — Expression syntax and string interpolation

  • Types — Product types and wrap types