# Request

> Read path parameters, the query string, headers, the client's IP behind proxies, and the content type the client prefers.

This guide covers reading the request in Tinka. You will learn how to:

- Read path parameters and query string values
- Read request headers and cookies
- Find the client's IP address, also behind a proxy
- Answer with the format the client prefers

## Overview

The [HTTP context](http-context.md) has methods for the parts of a request handlers read most. Each returns a plain Go value, and anything they don't cover is on the `*http.Request` that `c.Request()` returns. The body has its own guide, [Body parser](body-parser.md), because it needs decoding and validation.

## Path parameters

`c.Param` returns a value the route pattern matched, and `c.ParamInt` returns it as a whole number, answering `422` when it isn't one:

```go title="comments.go"
package main

import (
	"github.com/mucolabs/tinka"
)

type Comment struct {
	PostID int64  `json:"post_id"`
	Slug   string `json:"slug"`
}

// Registered as app.Get("/posts/{id}/comments/{slug}", showComment).
func showComment(c *tinka.Ctx) (Comment, error) {
	postID, err := c.ParamInt("id")
	if err != nil {
		return Comment{}, err
	}
	return Comment{PostID: postID, Slug: c.Param("slug")}, nil
}
```

## Query string

`c.Query` returns the first value of a query string parameter, or `""` when the request has none. `c.QueryValues` returns them all as `url.Values`, for parameters that repeat. The query string is parsed once per request, however often you read it.

```go title="posts.go"
// GET /posts?page=2&tag=go&tag=web
func listPosts(c *tinka.Ctx) ([]Post, error) {
	page := c.Query("page")            // "2"
	tags := c.QueryValues()["tag"]     // ["go", "web"]
	return posts.List(c, page, tags)
}
```

Query values are text the client chose. Parse and check them before use, as you would a form field: `strconv.Atoi(c.Query("page"))` returns an error for `?page=two`.

## Headers

Read request headers from the request. `Header.Get` is case-insensitive and returns `""` for a missing header:

```go
	agent := c.Request().Header.Get("User-Agent")
	languages := c.Request().Header.Values("Accept-Language")
```

`c.Header()` returns the response's headers instead, which the [Response](response.md) guide covers.

## Cookies

`c.Cookie` reads a cookie set with [`c.SetCookie`](response.md#set-cookies) as the type you name, and reports whether the request has it with a valid signature. A cookie the client changed, or a value copied from another cookie, is reported missing, so a handler can trust what it gets:

```go title="cart.go"
func showCart(c *tinka.Ctx) (Cart, error) {
	cart, ok := c.Cookie[Cart]("cart")
	if !ok {
		return Cart{}, nil // no cart yet, or one that was tampered with
	}
	return cart, nil
}
```

`c.EncryptedCookie` reads a cookie set with `c.SetEncryptedCookie`, the same way. `c.PlainCookie` reads a plain cookie, or any cookie the browser sent, as a string: treat it as user input, like the query string.

## Client IP address

`c.IP` returns the client's IP address. Without a proxy, it's the address of the connection, and a client can't change it with a header.

Behind a load balancer or reverse proxy, every connection comes from the proxy, which adds the client's address to the `X-Forwarded-For` header. List your proxies in `TrustedProxies`, and `c.IP` reads the header, from the nearest hop back, until it reaches an address no trusted proxy added:

```go title="main.go"
	app := tinka.New(tinka.HTTPConfig{
		TrustedProxies: []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")},
	})
```

Without `TrustedProxies`, only a proxy on the same machine (a loopback address) is trusted. An empty list trusts none.

:::warning
Trust only the proxies in front of your App. Trusting a range clients can reach directly, such as every address with `0.0.0.0/0`, lets any client pick the IP your App sees by sending its own `X-Forwarded-For`, which defeats rate limits and audit logs keyed by IP.

```go title="main.go" del={2} ins={3}
	app := tinka.New(tinka.HTTPConfig{
		TrustedProxies: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")},
		TrustedProxies: []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")},
	})
```
:::

## Content negotiation

`c.Accepts` returns the format the client prefers among the ones you offer, by the request's `Accept` header and its quality values. Offer media types, such as `application/json`, or file extensions, such as `json` and `html`. With no `Accept` header the first offer wins, and so does the earlier offer when the client likes two equally. It returns `""` when the client accepts none of them.

```go title="posts.go"
func showPost(c *tinka.Ctx) (tinka.Response, error) {
	post, err := posts.Find(c, c.Param("id"))
	if err != nil {
		return nil, err
	}
	if c.Accepts("json", "html") == "html" {
		return tinka.HTML(renderPost(post)), nil
	}
	return tinka.JSON(post), nil
}
```

The App negotiates the same way for errors: a browser that asks for HTML gets an error page, and every other client gets problem details.

See also: [HTTP context](http-context.md), [Body parser](body-parser.md), [Response](response.md)
