Skip to content

Writing to a Stream (HTTP)

Dan Leech edited this page Dec 16, 2014 · 6 revisions

 

 

 

DOCS HAVE MOVED

This wiki is no longer maintained and should not be used. Read the Event Store docs at docs.geteventstore.com.

The latest version of the page you are currently viewing is available here.

 

 

 

Writing to a stream over http is simply a POST to the resource of the stream. If the stream does not exist then the stream will be implicitly created.

##Single Event

note that writing a single event has changed from version 2 to version 3. In version 2 all posts were assumed to be in the media type listed below. In version 3 a post of application/json assumes the post data to be the actual event to move from v2 you must update your code to post as application/vnd.eventstore.events+json as opposed to application/json

Writing an event to a stream is quite simple and can be done from any client that supports http. To write an event simply POST it to the head uri of the stream. This process can be seen:

myevent.txt

{
  "something" : "has data"
}
ouro@ouroboros:~$ curl -i -d@/home/greg/my:2113/streams/newstream -H "Content-Type:application/json" -H "ES-EventType: SomeEvent" -H "ES-EventId: C322E299-CB73-4B47-97C5-5054F920746E"
HTTP/1.1 201 Created
Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER, Authorization, ES-LongPoll
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
Location: http://127.0.0.1:2113/streams/newstream/1
Content-Type: text/plain; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Mon, 21 Apr 2014 20:59:22 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100

The event will be available in the stream. Some clients however may not be able to generate a guid (or may not want to generate a guid) for the id. This id is needed for idempotence purposes but can also be generated by the server. If we were to leave off the ES-EventId header a slightly different behaviour would be observed.

ouro@ouroboros:~$ curl -i -d@/home/ouro/myevent.json "http://127.0.0.1:2113/streams/newstream" -H "Content-Type:application/json" -H "ES-EventType: SomeEvent"
HTTP/1.1 301 FOUND
Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER, Authorization, ES-LongPoll
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
Location: http://127.0.0.1:2113/streams/newstream/incoming/ad1c1288-0d61-4995-88b2-06c57a42495b
Content-Type: ; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Mon, 21 Apr 2014 21:01:29 GMT
Content-Length: 28
Keep-Alive: timeout=15,max=100

In this case the Event Store has replied with a 301 redirect. The location points to another URI that the event can be posted to. This new uri that gets passed back will be idempotent for posting to even without an event id.

ouro@ouroboros:$ curl -i -d @/home/ouro/myevent.json "http://127.0.0.1:2113/streams/newstream/incoming/ad1c1288-0d61-4995-88b2-06c57a42495b" -H "Content-Type: application/json" -H "ES-EventType: SomeEvent" 
HTTP/1.1 201 Created
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER, Authorization, ES-LongPoll
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
Location: http://127.0.0.1:2113/streams/newstream/0
Content-Type: text/plain; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Mon, 21 Apr 2014 21:15:33 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100

It is generally recommended to include an event id if possible as it will result in fewer round trips between the client and the server.

When posting to either the stream or to the returned redirect it is required that clients include the "EventType" header. If you forget to include the header you will be given an error.

ouro@ouroboros:~$ curl -i -d @/home/greg/my:2113/streams/newstream" -H "Content-Type:application/json"
HTTP/1.1 400 Must include an event type with the request either in body or as ES-EventType header.
Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER, Authorization, ES-LongPoll
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
Content-Type: 
Server: Mono-HTTPAPI/1.0
Date: Mon, 21 Apr 2014 21:05:45 GMT
Content-Length: 0
Connection: close

##Event Store Events Media Type

The event store also supports a custom media type for posting events application/vnd.eventstore.events (+json/+xml). This format allows for a few things that posting events as above does not. As example it allows you to post multiple events in a single batch. If you want to post multiple events in a transactional batch you must use the custom media type.

simple-event.txt:

[
  {
    "eventId": "fbf4a1a1-b4a3-4dfe-a01f-ec52c34e16e4",
    "eventType": "event-type",
    "data": { "a": "1" }
  }
]

or for XML

<Events>
  <Event>
    <EventId>fbf4a1a1-b4a3-4dfe-a01f-ec52c34e16e4</EventId>
    <EventType>event-type</EventType>
    <Data>
      <MyEvent>
        <Something>1</Something>
      </MyEvent>
    </Data>
  </Event>
</Events>

The data can be represented by the following jschema (note that eventId must be a UUID)

[
    { 
	  "eventId"    : "string", 
      "eventType"  : "string",
      "data"       : "object",
      "metadata"   : "object"
    }
]

###Create

Then posting this data to the /streams/newstream resource will result in the stream being created.

ouro@es:~/src/ES.wiki$ curl -i -d @test.js "http://127.0.0.1:2113/streams/newstream" -H "Content-Type:application/vnd.eventstore.events+json"
HTTP/1.1 201 Created
Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER
Access-Control-Allow-Origin: *
Location: http://127.0.0.1:2113/streams/newstream/0
Content-Type: text/plain; charset: utf-8
Server: Mono-HTTPAPI/1.0
Date: Fri, 28 Jun 2013 12:17:59 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100

###Appending Events

If you edit the Message Id Guid and post it again it will append to the stream.

o@es:~/src/ES.wiki$ curl -i -d @test.js "http://127.0.0.1:2113/streams/newstream" -H "Content-Type:application/vnd.eventstore.events+json"
HTTP/1.1 201 Created
Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER
Access-Control-Allow-Origin: *
Location: http://127.0.0.1:2113/streams/newstream/1
Content-Type: text/plain; charset: utf-8
Server: Mono-HTTPAPI/1.0
Date: Fri, 28 Jun 2013 12:32:18 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100

##Expected Version

The expected version header is normally a number representing the version of the stream you read from. As an example if you read from the stream and it was at version 5 then you expect it to be at version 5. This can allow for optimistic locking when having multiple things reading/writing to streams. If your expected version is not the current version you will receive a HTTP status code of 400. See Idempotency section below, if you post the same event twice it will be idempotent and will not give a version error.

o@es:~/src/ES.wiki$ curl -i -d @test.js "http://127.0.0.1:2113/streams/newstream" -H "Content-Type:application/json" -H "ES-ExpectedVersion: 3"
HTTP/1.1 400 Wrong expected EventNumber
Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER
Access-Control-Allow-Origin: *
Content-Type: text/plain; charset: utf-8
Server: Mono-HTTPAPI/1.0
Date: Fri, 28 Jun 2013 12:33:30 GMT
Content-Length: 0
Connection: close

There are also some special values you can put into the expected version header.

-2 states that this write should never conflict with anything and should always succeed. -1 states that the stream should not exist at the time of the writing (this write will create it) 0 states that the stream should exist but should be empty

##Batch Writes

You can include more than one write in a single post. To do this you place multiple events inside of the array representing the events (and metadata if you wish to place metadata on your events).

This can be seen in the following post body that would insert two events.

[
  {
    "eventId": "fbf4b1a1-b4a3-4dfe-a01f-ec52c34e16e4",
    "eventType": "event-type",
    "data": {

      "a": "1"
    }
  },
  {
    "eventId": "0f9fad5b-d9cb-469f-a165-70867728951e",
    "eventType": "event-type",
    "data": {

      "a": "1"
    }
  }
]

When you write multiple events in a single post, they are treated transactionally. All events will be written together or all will fail.

###Idempotency

Appends to streams are idempotent based upon the EventId assigned in your post. If I were to re-run the last curl command it will return the same value again.

o@es:~$ curl -i -d @simple-event.txt "http://127.0.0.1:2113/streams/newstream" -H "Content-Type:application/json"
HTTP/1.1 201 Created
Access-Control-Allow-Methods: DELETE, GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER
Access-Control-Allow-Origin: *
Location: http://127.0.0.1:2113/streams/newstream/2
Content-Type: ; charset: utf-8
Server: Mono-HTTPAPI/1.0
Date: Wed, 03 Apr 2013 15:21:53 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100

This is a very important behaviour as this is how error handling works. If you get a timeout, broken connection, no answer, etc from your HTTP POST then your job is to retry the post. You must also keep the same uuid that you assigned to the event in the first post.

If you are using expected version with your message the event store is 100% idempotent. If you are using Any as your expected version the Event Store will do its best to keep things idempotent but cannot always assure that everything is fully idempotent and you will end up in atleast-once messaging. This is discussedc further in the idempotency document.

This idempotency also applies to the URIs generated by the server if you post a body as an event without the ES-EventId header associated with the request.

 ouro@ouroboros:$ curl -i -d @/home/ouro/myevent.json "http://127.0.0.1:2113/streams/newstream" -H "Content-Type:application/json" -H "ES-EventType: SomeEvent" 
HTTP/1.1 301 FOUND
Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER, Authorization, ES-LongPoll
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
Location: http://127.0.0.1:2113/streams/newstream/incoming/c7248fc1-3db4-42c1-96aa-a071c92649d1
Content-Type: ; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Mon, 21 Apr 2014 21:11:59 GMT
Content-Length: 28
Keep-Alive: timeout=15,max=100

We can then post multiple times to the generated redirect uri and the requests will be made idempotent for us.

ouro@ourobors:$ curl -i -d @/home/ouro/myevent.json "http://127.0.0.1:2113/streams/newstream/incoming/c7248fc1-3db4-42c1-96aa-a071c92649d1" -H "Content-Type: application/json" -H "ES-EventType: SomeEvent" 
HTTP/1.1 201 Created
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER, Authorization, ES-LongPoll
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
Location: http://127.0.0.1:2113/streams/newstream/0
Content-Type: text/plain; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Mon, 21 Apr 2014 21:14:28 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100

If you retry the post you will receive

ouro@ouroboros:$ curl -i -d @/home/ouro/myevent.json "http://127.0.0.1:2113/streams/newstream/incoming/c7248fc1-3db4-42c1-96aa-a071c92649d1" -H "Content-Type: application/json" -H "ES-EventType: SomeEvent" 
HTTP/1.1 201 Created
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER, Authorization, ES-LongPoll
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
Location: http://127.0.0.1:2113/streams/newstream/0
Content-Type: text/plain; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Mon, 21 Apr 2014 21:15:33 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100

Google analytics pixel

Clone this wiki locally