# Http/server How to get the request body?

**URL:** https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985
**Category:** Help & Support
**Created:** [April 22, 2025, 4:21pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985 "2025-04-22T16:21:50Z")
**Posts on this page:** 16
**Page:** 1

<div class="post-metadata">

### Author: ![Steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@Steve](https://forum.crystal-lang.org/u/Steve)
#### Post date: [April 22, 2025, 4:21pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/1 "2025-04-22T16:21:50Z")

</div>

http/server… How to get the request body?  
The POST body from the client is JSON in the format below:

```auto
{
"service": "serviceName",
"method": "methodName",
"params": {}
}

```

Server side I tried to retrieve that like this:

```auto
postbody = context.request.body.to_s

```

but that gives me something like: `#<HTTP::FixedLengthContent:0x7f30dcf19850>`  
How do I get that request body as a string?

---

<div class="post-metadata">

### Author: ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)
#### Post date: [April 22, 2025, 4:42pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/2 "2025-04-22T16:42:12Z")

</div>

`HTTP::FixedLengthContent` is an [`IO`](https://crystal-lang.org/api/1.16.1/IO.html) stream. You can consume it partially (bytewise), or entirely with [`#gets_to_end`](https://crystal-lang.org/api/1.16.1/IO.html#gets_to_end%3AString-instance-method):

```cr
postbody = context.request.body.gets_to_end

```

---

<div class="post-metadata">

### Author: ![Steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@Steve](https://forum.crystal-lang.org/u/Steve)
#### Post date: [April 22, 2025, 5:09pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/3 "2025-04-22T17:09:33Z")

</div>

> [@straight-shoota](#):
>
> `context.request.body.gets_to_end`

I can’t even run with that, I get the following error:  
`Error: undefined method 'gets_to_end' for Nil (compile-time type is (IO | Nil))`

---

<div class="post-metadata">

### Author: ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)
#### Post date: [April 22, 2025, 5:30pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/4 "2025-04-22T17:30:13Z")

</div>

It’s possible a request does not have a body, so it could be `nil`. If you are 100% sure there will be one, could do something like `context.request.body.not_nil!.gets_to_end`. Otherwise might be better to check for one and return a proper HTTP error back.

---

<div class="post-metadata">

### Author: ![Steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@Steve](https://forum.crystal-lang.org/u/Steve)
#### Post date: [April 22, 2025, 5:51pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/5 "2025-04-22T17:51:12Z")

</div>

> [@Blacksmoke16](#):
>
> context.request.body.not\_nil!.gets\_to\_end

Thank you.  
If the body is missing can I be sure this will just resolve to an empty string?

---

<div class="post-metadata">

### Author: ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)
#### Post date: [April 22, 2025, 5:59pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/6 "2025-04-22T17:59:11Z")

</div>

No, if the body is missing and you’re using `.not_nil!` it would likely result in an exception which would likely return a `500` error to the user. If this code is going to be used for real, I’d consider explicitly handling it by returning a `400` or something if there is no body and you’re expecting one.

---

<div class="post-metadata">

### Author: ![Steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@Steve](https://forum.crystal-lang.org/u/Steve)
#### Post date: [April 22, 2025, 6:30pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/7 "2025-04-22T18:30:02Z")

</div>

Sorry, I’m new to Crystal. I love the syntax and I seemed to be doing OK until I hit this bump in the road 😕

How can I test for nil in my code when the compiler won’t even let me run when that possibility exists?  
`Error: undefined method 'gets_to_end' for Nil (compile-time type is (IO | Nil))`

---

<div class="post-metadata">

### Author: ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)
#### Post date: [April 22, 2025, 6:42pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/8 "2025-04-22T18:42:16Z")

</div>

Something like this should do it, assuming you’re using the default HTTP server and not a framework:

```crystal
unless body = context.request.body
  break context.response.respond_with_status(400, "Request is missing a body")
end

puts body.gets_to_end
```

---

<div class="post-metadata">

### Author: ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)
#### Post date: [April 22, 2025, 8:52pm UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/9 "2025-04-22T20:52:35Z")

</div>

Alternative:

```cr
context.request.body.try(&.gets_to_end)

```

This evaluates to either a string containing the body content, or `nil` if there’s no body.

---

<div class="post-metadata">

### Author: ![Steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@Steve](https://forum.crystal-lang.org/u/Steve)
#### Post date: [April 23, 2025, 8:10am UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/10 "2025-04-23T08:10:36Z")

</div>

Thank you both for your help.

This had me going for a while but I’ve found even if there is no body in the POST request `context.request.body` is still not nil…

```crystal
    unless body = context.request.body
        puts "It's nil"
        return
    end
    
    puts typeof(body) # IO
    postbody = body.gets_to_end
    puts typeof(postbody) # String
    if postbody == ""
        puts "It's an empty string!" # is displayed
    end

```

---

<div class="post-metadata">

### Author: ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)
#### Post date: [April 23, 2025, 8:44am UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/11 "2025-04-23T08:44:50Z")

</div>

What does the `POST` request look like?

---

<div class="post-metadata">

### Author: ![Steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@Steve](https://forum.crystal-lang.org/u/Steve)
#### Post date: [April 23, 2025, 9:10am UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/12 "2025-04-23T09:10:39Z")

</div>

I just commented out the body:

```js
const response = await fetch("/", {
    method: "POST",
    //body: JSON.stringify({"service": "foo", "method": "bar", "params": ["baz"]})
});
console.log(response);

```

And now I currently have this in the server’s handler:

```crystal
begin
    js = JSON.parse(context.request.body.not_nil!.gets_to_end)
rescue
    do_error(context, HTTP::Status::BAD_REQUEST, "Malformed request", "text/plain")
    return    
end

```

---

<div class="post-metadata">

### Author: ![ysbaddaden](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/ysbaddaden/32/2252_2.png) [@ysbaddaden](https://forum.crystal-lang.org/u/ysbaddaden)
#### Post date: [April 23, 2025, 9:59am UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/13 "2025-04-23T09:59:56Z")

</div>

It’s possible the body ain’t nil but empty? Like the server is returning content-length zero or a chunked response with an empty body?

BTW: the nilable body sounds a bit harsh. Couldn’t it always return an empty IO object?

---

<div class="post-metadata">

### Author: ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)
#### Post date: [April 23, 2025, 10:34am UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/14 "2025-04-23T10:34:42Z")

</div>

That JS request sends the header `Content-Length: 0`. That implies an empty body (not a nil body), just as you observed.

> [@ysbaddaden](#):
>
> BTW: the nilable body sounds a bit harsh. Couldn’t it always return an empty IO object?

I was thinking the same.  
It’s quite rare that the body is actually nil.

---

<div class="post-metadata">

### Author: ![luislavena](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/luislavena/32/572_2.png) [@luislavena](https://forum.crystal-lang.org/u/luislavena)
#### Post date: [April 23, 2025, 10:36am UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/15 "2025-04-23T10:36:43Z")

</div>

> [@ysbaddaden](#):
>
> BTW: the nilable body sounds a bit harsh. Couldn’t it always return an empty IO object?

Oh, yes, please!

I had so many fights with code readability due that little detail.

Ended always with this noop pattern:

> <https://github.com/luislavena/fruit-jokes/blob/main/src/main.cr#L65-L71>

> <https://github.com/luislavena/fruit-jokes/blob/main/src/main.cr#L95-L118>

😿

---

<div class="post-metadata">

### Author: ![Steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@Steve](https://forum.crystal-lang.org/u/Steve)
#### Post date: [April 23, 2025, 11:25am UTC](https://forum.crystal-lang.org/t/http-server-how-to-get-the-request-body/7985/16 "2025-04-23T11:25:19Z")

</div>

> [@luislavena](#):
>
> ```crystal
> def parse_json(io : IO, type)
> type.from_json(io)
> end
>           
> # no-op
> def parse_json(io : Nil, type)
> end
> 
> ```

Quite a neat bit of lateral thinking there imo 😁
